DBA Data[Home] [Help]

PACKAGE BODY: APPS.BSC_AW_LOAD_KPI

Source


1 package body BSC_AW_LOAD_KPI AS
2 /*$Header: BSCAWLKB.pls 120.24 2006/04/20 11:22 vsurendr noship $*/
3 
4 /*
5 for kpi, there are 2 ways to load
6 1 load a kpi
7 2 load base table and kpi associated with them
8 
9 in 10g, each kpi can run in parallel
10     within each kpi, each dim set can run in parallel
11 10g:each kpi may be a separate conc process
12 */
13 
14 procedure load_kpi(
15 p_kpi_list dbms_sql.varchar2_table
16 ) is
17 --
18 l_parallel boolean;
19 Begin
20   --if 10g, we can launcg parallel jobs.
21   l_parallel:=false;
22   if bsc_aw_utility.can_launch_jobs(p_kpi_list.count)='Y' then
23     l_parallel:=true;
24   end if;
25   if l_parallel=false then
26     for i in 1..p_kpi_list.count loop
27       load_kpi(p_kpi_list(i),null,null,null,null);
28     end loop;
29   else
30     --start jobs and wait
31     load_kpi_jobs(p_kpi_list,null);
32   end if;
33 Exception when others then
34   log_n('Exception in load_kpi '||sqlerrm);
35   raise;
36 End;
37 
38 --p_base_table_list : comma separated list of base tables
39 procedure load_kpi_jobs(p_kpi_list dbms_sql.varchar2_table,p_base_table_list varchar2) is
40 --
41 l_job_name varchar2(100);
42 l_process varchar2(8000);
43 l_job_status bsc_aw_utility.parallel_job_tb;
44 Begin
45   bsc_aw_utility.clean_up_jobs('all');
46   for i in 1..p_kpi_list.count loop
47     l_job_name:='bsc_aw_load_kpi_'||bsc_aw_utility.get_dbms_time||'_'||i;
48     l_process:='bsc_aw_load_kpi.load_kpi('''||p_kpi_list(i)||''','''||nvl(p_base_table_list,'null')||''','||i||','''||l_job_name||''','''||
49     bsc_aw_utility.get_option_string||''');';
50     bsc_aw_utility.start_job(l_job_name,i,l_process,null);
51   end loop;
52   bsc_aw_utility.wait_on_jobs(null,l_job_status);
53   for i in 1..l_job_status.count loop
54     if l_job_status(i).status='error' then
55       raise bsc_aw_utility.g_exception;
56     end if;
57   end loop;
58 Exception when others then
59   log_n('Exception in load_kpi_jobs '||sqlerrm);
60   raise;
61 End;
62 
63 /*
64 p_base_table_list and p_kpi_list are 1 to 1. the entries can look as
65 BSC_B_1     3014
66 BSC_B_1     4000
67 BSC_B_2     3014
68 in 10g, we can parallelize each B -> KPI load
69 */
70 procedure load_base_table(
71 p_base_table_list dbms_sql.varchar2_table,
72 p_kpi_list dbms_sql.varchar2_table
73 ) is
74 --
75 l_kpi_list dbms_sql.varchar2_table; --distinct list of kpi
76 --l_base_table_list dbms_sql.varchar2_table;--base tables in each kpi
77 l_parallel boolean;
78 Begin
79   for i in 1..p_kpi_list.count loop
80     if bsc_aw_utility.in_array(l_kpi_list,p_kpi_list(i))=false then
81       l_kpi_list(l_kpi_list.count+1):=p_kpi_list(i);
82     end if;
83   end loop;
84   l_parallel:=false;
85   if bsc_aw_utility.can_launch_jobs(l_kpi_list.count)='Y' then
86     l_parallel:=true;
87   end if;
88   --NOTE!!! >>> load_kpi loads and aggregates
89   if l_parallel=false then
90     for i in 1..l_kpi_list.count loop
91       load_kpi(l_kpi_list(i),bsc_aw_utility.make_string_from_list(p_base_table_list),null,null,null);
92     end loop;
93   else
94     load_kpi_jobs(p_kpi_list,bsc_aw_utility.make_string_from_list(p_base_table_list));
95   end if;
96 Exception when others then
97   log_n('Exception in load_base_table '||sqlerrm);
98   raise;
99 End;
100 
101 -------------private api--------------------------------------
102 /*
103 in 10g, this will be called as a dbms job (each kpi will be a conc process)
104 find out the dimsets for the kpi. each dim set in 10g can be loaded in parallel
105 dimsets can be parallelized using dbms jobs
106 
107 in 10g, the aggregation of dimsets can be in parallel, and within each dimset, the agg of cubes
108 can be in parallel
109 
110 we are going to follow this:
111 load all dimsets of the kpi.
112 then aggregate all dimsets
113 
114 when loading base tables,
115 load all base tables for a kpi.
116 then aggregate all dimsets involved
117 
118 aggregation is tricky. we have to aggregate actuals and related target dimsets together since the status
119 of limit cubes have to be kept in mind p_base_table_list is comma separated list of base tables
120 
121 This is the single point of entry for both loading kpi and base tables
122 p_base_table_list can contain all the base tables. this means base tables of the kpi and not of the kpi
123 */
124 procedure load_kpi(p_kpi varchar2,p_base_table_list varchar2,p_run_id number,p_job_name varchar2,p_options varchar2) is
125 l_dim_set dbms_sql.varchar2_table; --this is all the dimsets, actuals and targets
126 l_aggregate_dim_set dbms_sql.varchar2_table; --dimsets used in aggregation. (Targets eliminated)
127 l_oo_dimset bsc_aw_md_wrapper.bsc_olap_object_tb;
128 l_base_table_list dbms_sql.varchar2_table;
129 l_varchar_table dbms_sql.varchar2_table;--temp
130 --
131 l_parallel boolean;
132 l_aggregation aggregation_r;
133 l_dim_set_parallel dbms_sql.varchar2_table;
134 l_dim_set_r bsc_aw_adapter_kpi.dim_set_r;
135 l_pl_base_tables dbms_sql.varchar2_table;
136 Begin
137   if p_run_id is not null then
138     --this is a dbms job. we have to do the initializations since this is a new session
139     bsc_aw_utility.g_options.delete;
140     bsc_aw_utility.parse_parameter_values(p_options,',',bsc_aw_utility.g_options);
141     bsc_aw_utility.open_file('Load_kpi_'||p_kpi||'_'||p_run_id);
142     bsc_aw_utility.dmp_g_options(bsc_aw_utility.g_options);
143     bsc_aw_utility.init_all_procedures;
144   end if;
145   log_n('load_kpi, p_kpi='||p_kpi||',p_base_table_list='||p_base_table_list||
146   ', p_run_id='||p_run_id||', p_job_name='||p_job_name||', p_options='||p_options);
147   --
148   set_aggregation(p_kpi,l_aggregation);
149   --if the calendar is not loaded, load it now
150   load_calendar_if_needed(p_kpi);
151   --get md info on all dimsets
152   --l_aggregation has all the dimsets, actuals and targets
153   if p_base_table_list is not null and p_base_table_list <> 'null' then --loading base tables
154     bsc_aw_utility.parse_parameter_values(p_base_table_list,',',l_varchar_table);
155     get_kpi_base_tables(p_kpi,l_varchar_table,l_base_table_list);
156     --l_base_table_list contains base tables that belong to the kpi from "p_base_table_list"
157     get_dimset_for_base_table(p_kpi,l_base_table_list,l_dim_set);
158   else --loading kpi
159     --!!! l_oo_dimset contains all the dimset, actuals and targets
160     bsc_aw_md_api.get_kpi_dimset(p_kpi,l_oo_dimset);
161     for i in 1..l_oo_dimset.count loop
162       l_dim_set(l_dim_set.count+1):=l_oo_dimset(i).object;
163     end loop;
164   end if;
165   if g_debug then
166     log_n('Load and Aggregate the following dimsets');
167     for i in 1..l_dim_set.count loop
168       log(l_dim_set(i));
169     end loop;
170     if l_base_table_list.count>0 then
171       log('With the following base tables');
172       for i in 1..l_base_table_list.count loop
173         log(l_base_table_list(i));
174       end loop;
175     end if;
176   end if;
177   --first, load dim if needed
178   load_dim_if_needed(p_kpi,l_dim_set);
179   --
180   /*detach workspace . found an interesting issue. if a diff session updates and commits an object, then if the workspace is
181   not detached and attached again, update to that object raises error. dim load touches the LB. then when we try to save
182   these back in the load of the kpi, it raises an error
183   this is true when we try to get lock resync on a cube. the error is that dependent dim also must be relocked because they
184   have changed in the dim load. so best is to release the WS and reacquire the WS
185   ideally, we should first save lock set, commit lock set, then detach and then lock the lock set back again. since at this point, there are no
186   objects waiting to be saved or locked, we skip that step
187   */
188   bsc_aw_management.detach_workspace;
189   /*need logic to handle load. if all dimsets are parallelized and each dimset has partitions, then too many jobs are launched
190   logic:check dimsets for partitions. dimsets with partitions are launched serially. then the remaining dimsets are launched in
191   parallel if possible */
192   l_dim_set_parallel.delete;
193   for i in 1..l_dim_set.count loop
194     l_dim_set_r:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,l_dim_set(i)));
195     if l_dim_set_r.number_partitions>0 then
196       if g_debug then
197         log(l_dim_set(i)||' has partitions. Load run serially');
198       end if;
199       load_kpi_dimset(p_kpi,l_dim_set(i),l_base_table_list);/*if there are partitions for the dimset, launch them serially */
200     else
201       l_dim_set_parallel(l_dim_set_parallel.count+1):=l_dim_set(i);
202     end if;
203   end loop;
204   l_parallel:=false;
205   if l_dim_set_parallel.count>0 then
206     if bsc_aw_utility.can_launch_jobs(l_dim_set_parallel.count)='Y' then
207       l_parallel:=true;
208     end if;
209     --note>> if l_base_table_list is specified, l_dim_set_parallel will only be the dimsets which contain the base tabkes
210     --as a src. this is done in get_dimset_for_base_table above
211     /* */
212     if l_parallel then
213       l_pl_base_tables.delete;
214       get_base_table_for_dimset(p_kpi,l_base_table_list,l_dim_set_parallel,l_pl_base_tables);
215       /*l_pl_base_tables contains the B tables that belong to l_dim_set_parallel and belonging to l_base_table_list if l_base_table_list.count>0
216       to be 100 percent accurate, we have to only pick B tables that have inc data. But to keep this simple, just look at total B table
217       size*/
218       if is_parallel_load(l_pl_base_tables,bsc_aw_utility.g_parallel_load_cutoff)=false then
219         if g_debug then
220           log('Due to insufficient load, Parallel load of dimsets made serial');
221         end if;
222         l_parallel:=false;
223       end if;
224     end if;
225     if l_parallel=false then
226       for i in 1..l_dim_set_parallel.count loop
227         load_kpi_dimset(p_kpi,l_dim_set_parallel(i),l_base_table_list);
228       end loop;
229     else
230       load_kpi_dimset_job(p_kpi,l_dim_set_parallel,l_base_table_list);
231     end if;
232   end if;
233   /*must detach workspace
234   ideally, we should first save lock set, commit lock set, then detach and then lock the lock set back again. since at this point, there are no
235   objects waiting to be saved or locked, we skip that step*/
236   bsc_aw_management.detach_workspace;
237   --aggregate the dimsets
238   --aggregate_kpi_dimset will only aggregate actual dimsets if there is a corresponding target also
239   get_aggregate_dimsets(p_kpi,l_dim_set,l_aggregate_dim_set);
240   if l_aggregate_dim_set.count>0 then
241     if g_debug then
242       log_n('Aggregate the following dimsets. After eliminating targets');
243       for i in 1..l_aggregate_dim_set.count loop
244         log(l_aggregate_dim_set(i));
245       end loop;
246     end if;
247     --
248     --now, we can aggregate these dimsets
249     --aggregate_kpi_dimset handled targets. note: targets have already been loaded in load_kpi_dimset in the prev step...
250     --reset_dim_limits also happens in aggregate_kpi_dimset
251     --we will only try and aggregate the actuals. when aggregating the actuals, aggregate_kpi_dimset will also aggregate the
252     --corresponding target dimset. this is why we pass l_aggregation. we must aggregate only the actuals here. the reason is that
253     --if we try aggregating targets first, ahead of actuals, then when we aggregate the actuals, it will overwrite the target info
254     --we aggregate all measures of the dimset. cost is in composite creation. so aggregating only the measures affected by the B tables
255     --does not get better perf.
256     /*to manage load better, if dimset has partitions, we launch them serially */
257     l_dim_set_parallel.delete;
258     for i in 1..l_aggregate_dim_set.count loop
259       l_dim_set_r:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,l_aggregate_dim_set(i)));
260       if l_dim_set_r.number_partitions>0 then
261         if g_debug then
262           log(l_aggregate_dim_set(i)||' has partitions. Aggregation run serially');
263         end if;
264         aggregate_kpi_dimset(p_kpi,l_aggregate_dim_set(i),null,null,null);/*to aggregate in parallel on partitions or not is checked inside this api*/
265       else
266         l_dim_set_parallel(l_dim_set_parallel.count+1):=l_aggregate_dim_set(i);
267       end if;
268     end loop;
269     l_parallel:=false;
270     if l_dim_set_parallel.count>0 then
271       if bsc_aw_utility.can_launch_jobs(l_dim_set_parallel.count)='Y' then
272         l_parallel:=true;
273       end if;
274       /*check to see if there is sufficient load to warrant parallel load .
275       earlier we had is_parallel_aggregate test here. we cannot do this. when we aggregate, 100 nodes can explode into 100000 nodes
276       depending on the hier depths. this means we cannot disable parallel aggregation just because there are onlyu 100 nodes. load could
277       bring in 100 nodes.the only time we can do is_parallel_aggregate test is for formula and target copy because the nodes are
278       not increasing in these cases
279       */
280       if l_parallel=false then
281         for i in 1..l_dim_set_parallel.count loop
282           aggregate_kpi_dimset(p_kpi,l_dim_set_parallel(i),null,null,null);
283         end loop;
284       else
285         aggregate_kpi_dimset_job(p_kpi,l_dim_set_parallel);
286       end if;
287     end if;
288     if p_run_id is not null then
289       bsc_aw_utility.send_pipe_message(p_job_name,'status=success');
290       bsc_aw_management.detach_workspace;
291     end if;
292   else
293     if g_debug then
294       log('No dimsets to aggregate');
295     end if;
296   end if;
297   commit;
298 Exception when others then
299   log_n('Exception in load_kpi '||sqlerrm);
300   if p_run_id is not null then
301     bsc_aw_utility.send_pipe_message(p_job_name,'status=error,sqlcode='||sqlcode||',message='||sqlerrm);
302     rollback;
303     bsc_aw_management.detach_workspace;
304   else
305     raise;
306   end if;
307 End;
308 
309 /*
310 p_base_tables will be base tables belonging to the kpi
311 */
312 procedure load_kpi_dimset_job(p_kpi varchar2,p_dimset_list dbms_sql.varchar2_table,p_base_tables dbms_sql.varchar2_table) is
313 --
314 l_job_name varchar2(100);
315 l_process varchar2(8000);
316 l_job_status bsc_aw_utility.parallel_job_tb;
317 Begin
318   bsc_aw_utility.clean_up_jobs('all');
319   for i in 1..p_dimset_list.count loop
320     l_job_name:='bsc_aw_load_kpi_dimset_'||bsc_aw_utility.get_dbms_time||'_'||i;
321     l_process:='bsc_aw_load_kpi.load_kpi_dimset('''||p_kpi||''','''||p_dimset_list(i)||''','''||
322     nvl(bsc_aw_utility.make_string_from_list(p_base_tables),'null')||''','||i||','''||l_job_name||''','''||
323     bsc_aw_utility.get_option_string||''');';
324     bsc_aw_utility.start_job(l_job_name,i,l_process,null);
325   end loop;
326   bsc_aw_utility.wait_on_jobs(null,l_job_status);
327   for i in 1..l_job_status.count loop
328     if l_job_status(i).status='error' then
329       raise bsc_aw_utility.g_exception;
330     end if;
331   end loop;
332 Exception when others then
333   log_n('Exception in load_kpi_dimset_job '||sqlerrm);
334   raise;
335 End;
336 
337 --just a wrapper to call load_kpi_dimset as a job. we cannot pass dbms_sql.varchar2_table when launching a job
338 procedure load_kpi_dimset(p_kpi varchar2,p_dim_set varchar2,p_base_tables varchar2,
339 p_run_id number,p_job_name varchar2,p_options varchar2) is
340 --
341 l_base_tables dbms_sql.varchar2_table;
342 Begin
343   if p_base_tables is not null and p_base_tables <> 'null' then
344     bsc_aw_utility.parse_parameter_values(p_base_tables,',',l_base_tables);
345   end if;
346   if p_run_id is not null then
347     --this is a dbms job. we have to do the initializations since this is a new session
348     bsc_aw_utility.g_options.delete;
349     bsc_aw_utility.parse_parameter_values(p_options,',',bsc_aw_utility.g_options);
350     bsc_aw_utility.open_file('Load_KD_'||p_dim_set||'_'||p_run_id);
351     bsc_aw_utility.dmp_g_options(bsc_aw_utility.g_options);
352     bsc_aw_utility.init_all_procedures;
353   end if;
354   load_kpi_dimset(p_kpi,p_dim_set,l_base_tables);
355   if p_run_id is not null then
356     bsc_aw_utility.send_pipe_message(p_job_name,'status=success');
357     bsc_aw_management.detach_workspace;
358   end if;
359   commit;
360 Exception when others then
361   log_n('Exception in load_kpi_dimset '||sqlerrm);
362   if p_run_id is not null then
363     bsc_aw_utility.send_pipe_message(p_job_name,'status=error,sqlcode='||sqlcode||',message='||sqlerrm);
364     rollback;
365     bsc_aw_management.detach_workspace;
366   else
367     raise;
368   end if;
369 End;
370 
371 /*
372 given a dimset, find out the cubes and load them
373 also do the calculations like forecasts, aggregations etc
374 
375 this will be called as a dbms job in 10g. commit happens here
376 
377 logic:
378 find out the base tables.
379 if any of the base tables is full load, the whole dimset is full load. check the _aw tables for data.
380 if they have data, inc load. else check b table. if there are rows, then full load
381 
382 Once a dimset has been loaded, we can aggregate the cubes in the dimset
383 --
384 base tables can also have corresponding prj tables. p_base_tables will not contain the prj tables. however, get_ds_bt_parameters will grab
385 the correspnding string that contains the prj table also. so for now, we are not adding the prj tables to p_base_tables or l_oor_dimsets
386 */
387 procedure load_kpi_dimset(p_kpi varchar2,p_dim_set varchar2,p_base_tables dbms_sql.varchar2_table) is
388 --
389 l_olap_object_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
390 l_load_type varchar2(40); --initial or inc
391 l_oor_dimsets bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
392 Begin
393   --
394   l_olap_object_relation.delete;
395   --get the base tables l_olap_object_relation(i).object are the base tables
396   bsc_aw_md_api.get_dimset_base_table(p_kpi,p_dim_set,'base table dim set',l_olap_object_relation);
397   --if base tables are specified, filter out the un-needed dimsets
398   --there must be atleast one base table belonging to any dimset
399   if p_base_tables.count>0 then
400     for i in 1..l_olap_object_relation.count loop
401       if bsc_aw_utility.in_array(p_base_tables,l_olap_object_relation(i).object) then
402         l_oor_dimsets(l_oor_dimsets.count+1):=l_olap_object_relation(i);
403       end if;
404     end loop;
405   else
406     l_oor_dimsets:=l_olap_object_relation;
407   end if;
408   load_kpi_dimset_base_table(l_oor_dimsets);
409 Exception when others then
410   log_n('Exception in load_kpi_dimset '||sqlerrm);
411   raise;
412 End;
413 
414 /*
415 this is the atomic procedure. a table of olap relations are passed . this can be 2 types.
416 1. load of a kpi. im this case we will have
417 B1  dimset1     kpi1
418 B2  dimset1     kpi1
419 diff base tables for the same dimset and kpi
420 
421 2. load of a baes table. in this case we will have
422 B1  dimset1     kpi1
423 or
424 B1  dimset2     kpi1
425 or
426 B1  dimset1     kpi2
427 the reason for the or is that in caes of base table loading, there will be only 1 entry in the table. each of them
428 can be run in parallel in 10g.
429 
430 this api desiced inc vs full refresh
431 note: in this api, we load essentially one dimset for a kpi. it can be multiple base tables or single base table
432 
433 here we call the aggregate marker program. this api is running in parallel for various dim sets in the same kpi. so
434 its ok to call the aggregate marker program since this program is processing each dim set. the same dimset cannot be loaded
435 at the same time by 2 diff sessions. we have lock_dimset_objects here.
436 */
437 procedure load_kpi_dimset_base_table(
438 p_olap_object_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb
439 ) is
440 --
441 l_load_type varchar2(40); --initial or inc
442 l_dimset_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
443 l_olap_object_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
444 l_aggregate_marker_program varchar2(300);
445 l_load_program varchar2(300);
446 l_load_program_parallel varchar2(300);
447 --
448 l_kpi varchar2(300);
449 l_dimset varchar2(300);
450 --
451 l_pl_base_tables dbms_sql.varchar2_table;/*to check parallel load */
452 l_pl_change_vector dbms_sql.number_table;
453 l_min_value dbms_sql.number_table;
454 l_max_value dbms_sql.number_table;
455 l_bt_current_period dbms_sql.varchar2_table;
456 l_measures dbms_sql.varchar2_table;--to see if we can launch parallel jobs, we need the count of the no. of measures in the dimset
457 l_property varchar2(3000);
458 l_aggregation aggregation_r;
459 l_LB_resync_program varchar2(200);
460 parallel_flag varchar2(10);
461 --
462 Begin
463   if g_debug then
464     log_n('load_kpi_dimset_base_table ');
465     log('Entries');
466     for i in 1..p_olap_object_relation.count loop
467       log('kpi='||p_olap_object_relation(i).parent_object||' base table='||p_olap_object_relation(i).object
468       ||' objtype='||p_olap_object_relation(i).object_type||' reltype='||p_olap_object_relation(i).relation_type
469       ||' dimset='||p_olap_object_relation(i).relation_object||' relobjtype='||p_olap_object_relation(i).relation_object_type);
470     end loop;
471     bsc_aw_utility.clean_stats('group.load_kpi_dimset_base_table');
472     bsc_aw_utility.load_stats('Start Of Process. Load kpi dimset base table','group.load_kpi_dimset_base_table');
473   end if;
474   --note!! >> there is only 1 dimset in this api. there can be 1 or more base tables
475   l_kpi:=p_olap_object_relation(1).parent_object;
476   l_dimset:=p_olap_object_relation(1).relation_object;
477   --
478   set_aggregation(l_kpi,l_aggregation);
479   --get lock
480   lock_dimset_objects(l_kpi,l_dimset,null,null);
481   --before we call the load programs, we will first run aggregate_marker_program. this will set the limit cubes to true
482   --all the values that need to be re-aggregated because dim hierarchies changed
483   l_dimset_oor.delete;
484   l_aggregate_marker_program:=null;
485   bsc_aw_md_api.get_bsc_olap_object_relation(l_dimset,'kpi dimension set',null,l_kpi,'kpi',l_dimset_oor);
486   for i in 1..l_dimset_oor.count loop
487     if l_dimset_oor(i).relation_type='aggregate marker program' then
488       l_aggregate_marker_program:=l_dimset_oor(i).relation_object;
489       exit;
490     end if;
491   end loop;
492   if l_aggregate_marker_program is not null then
493     bsc_aw_dbms_aw.execute('call '||l_aggregate_marker_program);
494   end if;
495   --
496   --for each dim set, for each base table, see if inc or full. then
497   --if there is initial for any of the baestables for the dimset, full refresh for the dim ste
498   --a dimset is unique only in the context of a kpi. but since the kpi name is a part of the dimset, dimset name is unique
499   for i in 1..p_olap_object_relation.count loop
500     l_load_type:=check_load_type(p_olap_object_relation(i).property1);
501     if l_load_type='initial' then
502       exit;
503     end if;
504   end loop;
505   --p_olap_object_relation(1).relation_object is the dimset. we assume only 1 dimset in this api
506   l_load_program:=null;
507   l_load_program_parallel:=null;
508   for i in 1..l_dimset_oor.count loop
509     if (l_load_type='initial' and l_dimset_oor(i).relation_type='dml program initial load') or
510     (l_load_type='inc' and l_dimset_oor(i).relation_type='dml program inc load') then
511       l_load_program:=l_dimset_oor(i).relation_object;
512       exit;
513     end if;
514   end loop;
515   for i in 1..l_dimset_oor.count loop
516     if (l_load_type='initial' and l_dimset_oor(i).relation_type='dml program initial load parallel') or
517     (l_load_type='inc' and l_dimset_oor(i).relation_type='dml program inc load parallel') then
518       l_load_program_parallel:=l_dimset_oor(i).relation_object;
519       exit;
520     end if;
521   end loop;
522   --
523   for i in 1..l_dimset_oor.count loop
524     if l_dimset_oor(i).relation_type='LB resync program' then
525       l_LB_resync_program:=l_dimset_oor(i).relation_object;
526       exit;
527     end if;
528   end loop;
529   --get base table change vector value  . insert a row into bsc_aw_temp_cv for each base table
530   l_pl_base_tables.delete;
531   l_pl_change_vector.delete;
532   for i in 1..p_olap_object_relation.count loop
533     l_min_value(i):=to_number(bsc_aw_utility.get_parameter_value(p_olap_object_relation(i).property1,'current change vector',','))+1;
534     l_olap_object_relation.delete;
535     bsc_aw_md_api.get_bsc_olap_object_relation(p_olap_object_relation(i).object,'base table',null,
536     p_olap_object_relation(i).object,'base table',l_olap_object_relation);
537     l_max_value(i):=null;
538     l_bt_current_period(i):=null;
539     for j in 1..l_olap_object_relation.count loop
540       if l_olap_object_relation(j).relation_type='base table change vector' then
541         l_max_value(i):=to_number(l_olap_object_relation(j).relation_object);
542         if l_max_value(i) is not null and (l_min_value(i) is null or l_max_value(i)>=l_min_value(i)) then
543           l_pl_base_tables(l_pl_base_tables.count+1):=p_olap_object_relation(i).object;
544           l_pl_change_vector(l_pl_change_vector.count+1):=l_max_value(i);
545         end if;
546       elsif l_olap_object_relation(j).relation_type='base table current period' then
547         l_bt_current_period(i):=l_olap_object_relation(j).relation_object;
548       end if;
549     end loop;
550     if l_max_value(i) is null then
551       log_n('Error!!! No change vector value found for base table '||p_olap_object_relation(i).object);
552       raise bsc_aw_utility.g_exception;
553     end if;
554     if l_bt_current_period(i) is null then
555       l_bt_current_period(i):='null';
556     end if;
557   end loop;
558   /*
559   to see if we can parallelize, we will take a count of the number of measures in the dimset.
560   in 10g, we can parallelize load of measures if they have diff composites
561   now, for 10g, we parallelize based on partitions
562   */
563   l_measures.delete;
564   bsc_aw_md_api.get_relation_object(l_dimset_oor,'dim set measure',l_measures);
565   parallel_flag:=can_launch_jobs(l_kpi,l_aggregation.dim_set(get_dim_set_index(l_aggregation,l_dimset)),l_measures);
566   if parallel_flag='Y' then
567     if l_pl_base_tables.count>0 then
568       /*check to see if there is sufficient data load to go for parallel load. otherwise, serial load is faster */
569       if is_parallel_load(l_pl_base_tables,l_pl_change_vector,bsc_aw_utility.g_parallel_load_cutoff)=false then
570         if g_debug then
571           log('Due to insufficient load Parallel load of cubes and partitions made serial');
572         end if;
573         parallel_flag:='N';
574       end if;
575     else
576       if g_debug then
577         log('There is no new B table data to load. Parallel load of cubes and partitions made serial');
578       end if;
579       parallel_flag:='N';
580     end if;
581   end if;
582   if parallel_flag='Y' then
583     load_kpi_dimset_base_table_job(l_kpi,l_dimset,p_olap_object_relation,l_dimset_oor,l_load_program_parallel,l_LB_resync_program,
584     l_min_value,l_max_value,l_bt_current_period);
585   else
586     load_kpi_dimset_base_table(l_kpi,l_dimset,p_olap_object_relation,l_dimset_oor,l_load_program,
587     l_min_value,l_max_value,l_bt_current_period);
588   end if;
589   --base table dimset combination for a change vector is unique
590   --update the current change vector for a B table->dimset combination
591   for i in 1..p_olap_object_relation.count loop
592     l_property:=p_olap_object_relation(i).property1;
593     bsc_aw_utility.update_property(l_property,'current change vector',to_char(l_max_value(i)),',');
594     if l_bt_current_period(i) is not null and l_bt_current_period(i)<>'null' then
595       bsc_aw_utility.update_property(l_property,'base table current period',l_bt_current_period(i),',');
596     end if;
597     bsc_aw_md_api.update_olap_object_relation(p_olap_object_relation(i).object,p_olap_object_relation(i).object_type,
598     p_olap_object_relation(i).relation_type,p_olap_object_relation(i).parent_object,p_olap_object_relation(i).parent_object_type,
599     'relation_object,relation_object_type',p_olap_object_relation(i).relation_object||','||p_olap_object_relation(i).relation_object_type,
600     'property1',l_property);
601   end loop;
602   --
603   bsc_aw_management.commit_aw;--release the dimset objects
604   commit;
605   if g_debug then
606     bsc_aw_utility.load_stats('End Of Process. Load kpi dimset base table','group.load_kpi_dimset_base_table');
607     bsc_aw_utility.print_stats('group.load_kpi_dimset_base_table');
608   end if;
609 Exception when others then
610   --lock release happens in an exxeption when the workspace is eventually detached and the session ends
611   log_n('Exception in load_kpi_dimset_base_table '||sqlerrm);
612   raise;
613 End;
614 
615 /*
616 This procedure loads the base table dimset combination in single thread. this means it calls the dimset load
617 program and passes the base table as parameter
618 load_kpi_dimset_base_table_job on the other hand loads the measures in parallel
619 */
620 procedure load_kpi_dimset_base_table(
621 p_kpi varchar2,
622 p_dimset varchar2,
623 p_base_table_dimset_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb, --to get baes table, dimset and bt measures
624 p_dimset_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb, --to get measures etc
625 p_load_program varchar2,
626 p_min_value dbms_sql.number_table,
627 p_max_value dbms_sql.number_table,
628 p_bt_current_period dbms_sql.varchar2_table /*null are indicated as 'null'. this has to be in sync with p_base_table_dimset_oor*/
629 ) is
630 --
631 l_base_tables dbms_sql.varchar2_table;
632 l_bt_current_period dbms_sql.varchar2_table;
633 l_flag boolean;
634 l_ds_parameters dbms_sql.varchar2_table;
635 l_aggregation aggregation_r;
636 l_dim_set bsc_aw_adapter_kpi.dim_set_r;
637 --
638 l_cubes dbms_sql.varchar2_table;
639 l_measures dbms_sql.varchar2_table;
640 l_bt_measures dbms_sql.varchar2_table;
641 Begin
642   l_flag:=false;
643   for i in 1..p_base_table_dimset_oor.count loop
644     if p_min_value(i)<=p_max_value(i) then
645       insert_bsc_aw_temp_cv(p_min_value(i),p_max_value(i),upper(p_base_table_dimset_oor(i).object));
646       l_flag:=true;
647     end if;
648   end loop;
649   for i in 1..p_base_table_dimset_oor.count loop
650     l_base_tables(l_base_tables.count+1):=upper(p_base_table_dimset_oor(i).object);
651     l_bt_current_period(l_bt_current_period.count+1):=p_bt_current_period(i);
652   end loop;
653   set_aggregation(p_kpi,l_aggregation);
654   l_dim_set:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,p_dimset));
655   for i in 1..p_base_table_dimset_oor.count loop
656     l_bt_measures.delete;
657     bsc_aw_utility.parse_parameter_values(bsc_aw_utility.get_parameter_value(p_base_table_dimset_oor(i).property1,'measures',','),'+',
658     l_bt_measures);
659     for j in 1..l_bt_measures.count loop
660       bsc_aw_utility.merge_value(l_measures,l_bt_measures(j));
661     end loop;
662   end loop;
663   for i in 1..p_dimset_oor.count loop
664     if p_dimset_oor(i).relation_type='dim set measure' then
665       if bsc_aw_utility.in_array(l_measures,p_dimset_oor(i).relation_object) then
666         bsc_aw_utility.merge_value(l_cubes,bsc_aw_utility.get_parameter_value(p_dimset_oor(i).property1,'cube',','));
667       end if;
668     end if;
669   end loop;
670   /*see if we need to correct projection or bal values with a change to base table current period */
671   check_bt_current_period_change(p_kpi,l_dim_set,l_cubes,l_measures,l_base_tables,l_bt_current_period,null);
672   if l_flag then
673     /*set the cal end period rel for balance aggregation in the kpi load programs*/
674     limit_calendar_end_period_rel(l_dim_set.calendar);
675     get_ds_BT_parameters(p_kpi,p_dimset,p_load_program,l_base_tables,l_ds_parameters);
676     if g_debug then
677       log('Before Load, Composite Counts');
678       dmp_dimset_composite_count(l_dim_set);
679     end if;
680     for i in 1..l_ds_parameters.count loop
681       bsc_aw_dbms_aw.execute('call '||p_load_program||'('''||l_ds_parameters(i)||''')');
682     end loop;
683     if g_debug then
684       log('After Load, Composite Counts');
685       dmp_dimset_composite_count(l_dim_set);
686     end if;
687     reset_calendar_end_period_rel(l_dim_set.calendar);
688   end if;
689 Exception when others then
690   log_n('Exception in load_kpi_dimset_base_table '||sqlerrm);
691   raise;
692 End;
693 
694 /*
695 In 10g, the cubes can be loaded in parallel in addition to being aggregated in parallel. this brings true parallelization
696 each cube has its own composite. if not loaded in parallel, load takes time as N composites are created at the load time
697 in parallel, these N composites can be created in parallel.
698 this procedure is a wrapper.
699 p_olap_object_relation contains 1 dimset but multiple base tables
700 for 10g, we now have datacubes and load/aggregate in parallel
701 */
702 procedure load_kpi_dimset_base_table_job(
703 p_kpi varchar2,
704 p_dimset varchar2,
705 p_base_table_dimset_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb, --to get baes table, dimset and bt measures
706 p_dimset_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb, --to get measures etc
707 p_load_program varchar2,
708 p_LB_resync_program varchar2,
709 p_min_value dbms_sql.number_table,
710 p_max_value dbms_sql.number_table,
711 p_bt_current_period dbms_sql.varchar2_table /*null are indicated as 'null' */
712 ) is
713 --
714 l_job_name varchar2(100);
715 l_process varchar2(8000);
716 l_job_status bsc_aw_utility.parallel_job_tb;
717 --
718 l_measures dbms_sql.varchar2_table;
719 l_cubes dbms_sql.varchar2_table;
720 l_start_lock_objects varchar2(3000);
721 l_end_lock_objects varchar2(3000);
722 l_parameter_string varchar2(2000);
723 l_base_table_stmt varchar2(2000);
724 l_min_stmt varchar2(2000);
725 l_max_stmt varchar2(2000);
726 l_bt_current_period_stmt varchar2(2000);
727 l_lock_object_cubes dbms_sql.varchar2_table;
728 l_lock_object_limit_cubes dbms_sql.varchar2_table;
729 l_lock_object_reset_cubes dbms_sql.varchar2_table;
730 l_run_id number;
731 l_cubes_to_load dbms_sql.varchar2_table;
732 l_measures_to_load dbms_sql.varchar2_table;
733 --
734 l_aggregation aggregation_r;
735 l_dim_set bsc_aw_adapter_kpi.dim_set_r;
736 l_cube_pt bsc_aw_adapter_kpi.partition_template_r;
737 l_bt_considered dbms_sql.varchar2_table;
738 l_partition_options varchar2(2000); /*hold partition dim value */
739 Begin
740   bsc_aw_utility.clean_up_jobs('all');
741   for i in 1..p_dimset_oor.count loop
742     --p_dimset_oor contains info pertaining to p_dimset only
743     if p_dimset_oor(i).relation_type='dim set measure' then
744       l_measures(l_measures.count+1):=p_dimset_oor(i).relation_object;
745       l_cubes(l_cubes.count+1):=bsc_aw_utility.get_parameter_value(p_dimset_oor(i).property1,'cube',',');
746       if bsc_aw_utility.in_array(l_cubes_to_load,l_cubes(l_cubes.count))=false then
747         l_cubes_to_load(l_cubes_to_load.count+1):=l_cubes(l_cubes.count);
748       end if;
749     end if;
750   end loop;
751   /*
752   we now have support for partitions. we need to have a thread per cube...not per measure
753   then for each cube, we can parallelize based on partitions
754   technically, we need to group cubes by PT or comp.lets assume that diff cubes have diff PT or comp so they can be loaded in
755   parallel
756   for each measure, find out all base tables that carry this measure.
757   release the lock on the cubes. we release all cubes and countvar cubes of the dimset. this is ok since 2 base tables feeding a dimset
758   have to go one after the other anyway . we need l_aggregation to get the dimset info
759   */
760   --
761   set_aggregation(p_kpi,l_aggregation);
762   l_dim_set:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,p_dimset));
763   --
764   get_measure_objects_to_lock(p_kpi,p_dimset,l_measures,l_lock_object_cubes);
765   get_dimset_objects_to_lock(p_kpi,p_dimset,'dim limit cube',l_lock_object_limit_cubes);
766   get_dimset_objects_to_lock(p_kpi,p_dimset,'dim reset cube',l_lock_object_reset_cubes);
767   --we must save these objects back to the system first, especially the limit cubes that can change due to aggregate marker pgm
768   --for perf reasons, at present, we will not save the cube...it could not have changed
769   --reset cubes share the composite with limit cubes.
770   bsc_aw_management.commit_aw(l_lock_object_limit_cubes,'no release lock');
771   bsc_aw_management.release_lock(l_lock_object_cubes);
772   bsc_aw_management.release_lock(l_lock_object_limit_cubes);
773   bsc_aw_management.release_lock(l_lock_object_reset_cubes);
774   --
775   /*
776   if the cube is partitioned, we load the partitions in parallel, job per partition for the cube
777   if the cube is not partitioned, its one job per cube
778   */
779   --
780   l_run_id:=0;
781   for i in 1..l_cubes_to_load.count loop
782     l_base_table_stmt:=null;
783     l_min_stmt:=null;
784     l_max_stmt:=null;
785     l_bt_current_period_stmt:=null;
786     l_bt_considered.delete;
787     l_measures_to_load.delete;
788     for j in 1..l_measures.count loop
789       if l_cubes(j)=l_cubes_to_load(i) then
790         for k in 1..p_base_table_dimset_oor.count loop
791           if instr(p_base_table_dimset_oor(k).property1,l_measures(j)||'+')>0 then
792             bsc_aw_utility.merge_value(l_measures_to_load,l_measures(j));
793             if bsc_aw_utility.in_array(l_bt_considered,p_base_table_dimset_oor(k).object)=false then
794               l_base_table_stmt:=l_base_table_stmt||p_base_table_dimset_oor(k).object||',';
795               l_min_stmt:=l_min_stmt||p_min_value(k)||',';
796               l_max_stmt:=l_max_stmt||p_max_value(k)||',';
797               l_bt_current_period_stmt:=l_bt_current_period_stmt||p_bt_current_period(k)||',';
798               l_bt_considered(l_bt_considered.count+1):=p_base_table_dimset_oor(k).object;
799             end if;
800           end if;
801         end loop;
802       end if;
803     end loop;
804     if l_base_table_stmt is not null then
805       --l_base_table_stmt can be null. we may be loading base table that is feeding 2 out of 5 measures for example
806       --see if the cube is partitioned or not
807       l_cube_pt.template_name:=bsc_aw_adapter_kpi.get_cube_axis(l_cubes_to_load(i),l_dim_set,'partition template');
808       if l_cube_pt.template_name is not null then --this is a partitioned cube
809         l_cube_pt:=bsc_aw_adapter_kpi.get_partition_template_r(l_cube_pt.template_name,l_dim_set);
810         l_end_lock_objects:=null;
811         --end lock objects are the limit cubes
812         for j in 1..l_lock_object_limit_cubes.count loop
813           l_end_lock_objects:=l_end_lock_objects||l_lock_object_limit_cubes(j)||',';
814         end loop;
815         for j in 1..l_cube_pt.template_partitions.count loop
816           l_job_name:='bsc_aw_lc_'||p_dimset||'_'||l_cubes_to_load(i)||'_part_'||l_cube_pt.template_partitions(j).partition_name||'_'||
817           bsc_aw_utility.get_dbms_time;
818           l_parameter_string:=l_cubes_to_load(i)||','''',''''partition='||l_cube_pt.template_partitions(j).partition_name||',';
819           l_partition_options:='partition='||l_cube_pt.template_partitions(j).partition_name||',partition dim value='||
820           l_cube_pt.template_partitions(j).partition_dim_value;
821           l_start_lock_objects:=l_cubes_to_load(i)||' (partition '||l_cube_pt.template_partitions(j).partition_name||'),';
822           l_run_id:=l_run_id+1;
823           l_process:='bsc_aw_load_kpi.load_cube_base_table('''||p_kpi||''','''||p_dimset||''','''||l_parameter_string||''','''||
824           l_cubes_to_load(i)||''','''||bsc_aw_utility.make_string_from_list(l_measures_to_load)||''','''||
825           l_base_table_stmt||''','''||l_start_lock_objects||''','''||l_end_lock_objects||''','''||p_load_program||''','''||
826           p_LB_resync_program||''','''||l_min_stmt||''','''||l_max_stmt||''','''||l_bt_current_period_stmt||''','''||l_partition_options||''','||
827           l_run_id||','''||l_job_name||''','''||bsc_aw_utility.get_option_string||''');';
828           bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
829         end loop;
830       else --non partitioned cube
831         l_job_name:='bsc_aw_lc_'||p_dimset||'_'||l_cubes_to_load(i)||'_'||bsc_aw_utility.get_dbms_time;
832         l_start_lock_objects:=l_cubes_to_load(i)||',';
833         l_run_id:=l_run_id+1;
834         l_process:='bsc_aw_load_kpi.load_cube_base_table('''||p_kpi||''','''||p_dimset||''','''||l_start_lock_objects||''','''||
835         l_cubes_to_load(i)||''','''||bsc_aw_utility.make_string_from_list(l_measures_to_load)||''','''||
836         l_base_table_stmt||''','''||l_start_lock_objects||''','''||l_end_lock_objects||''','''||p_load_program||''',''null'','''||l_min_stmt||''','''||
837         l_max_stmt||''','''||l_bt_current_period_stmt||''',''null'','||
838         l_run_id||','''||l_job_name||''','''||bsc_aw_utility.get_option_string||''');';
839         bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
840       end if;
841     end if;
842   end loop;
843   /*
844   in the case where we have partitions, we do not need to launch the process for limit cubes. if we call the program with
845   LIMIT CUBE, it will do nothing. but best if we do not call the procedure at all
846   */
847   if l_dim_set.cube_design <> 'datacube' then
848     l_base_table_stmt:=null;
849     l_min_stmt:=null;
850     l_max_stmt:=null;
851     l_bt_current_period_stmt:=null;
852     for i in 1..p_base_table_dimset_oor.count loop
853       l_base_table_stmt:=l_base_table_stmt||p_base_table_dimset_oor(i).object||',';
854       l_min_stmt:=l_min_stmt||p_min_value(i)||',';
855       l_max_stmt:=l_max_stmt||p_max_value(i)||',';
856       l_bt_current_period_stmt:=l_bt_current_period_stmt||p_bt_current_period(i)||',';
857     end loop;
858     if l_base_table_stmt is not null then
859       l_job_name:='bsc_aw_load_limit_cube_'||p_dimset||'_'||bsc_aw_utility.get_dbms_time||'_'||l_run_id;
860       l_start_lock_objects:=null;
861       for i in 1..l_lock_object_limit_cubes.count loop --l_lock_object_limit_cubes are the limit cubes
862         l_start_lock_objects:=l_start_lock_objects||l_lock_object_limit_cubes(i)||',';
863       end loop;
864       l_run_id:=l_run_id+1;
865       l_process:='bsc_aw_load_kpi.load_cube_base_table('''||p_kpi||''','''||p_dimset||''',''LIMIT CUBE'','||
866       '''null'',''null'','''||
867       l_base_table_stmt||''','''||l_start_lock_objects||''',''null'','''||p_load_program||''',''null'','''||l_min_stmt||''','''||
868       l_max_stmt||''','''||l_bt_current_period_stmt||''',''null'','||l_run_id||','''||l_job_name||''','''||bsc_aw_utility.get_option_string||''');';
869       bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
870     end if;
871   end if;
872   --
873   bsc_aw_utility.wait_on_jobs(null,l_job_status);
874   for i in 1..l_job_status.count loop
875     if l_job_status(i).status='error' then
876       raise bsc_aw_utility.g_exception;
877     end if;
878   end loop;
879   --get the locks back
880   bsc_aw_management.get_lock(l_lock_object_cubes,'resync');
881   if l_lock_object_limit_cubes.count>0 then
882     bsc_aw_management.get_lock(l_lock_object_limit_cubes,'resync');
883   end if;
884   if l_lock_object_reset_cubes.count>0 then
885     bsc_aw_management.get_lock(l_lock_object_reset_cubes,'resync');
886   end if;
887 Exception when others then
888   log_n('Exception in load_kpi_dimset_base_table_job '||sqlerrm);
889   raise;
890 End;
891 
892 /*
893 this procedure is called from a dbms job. this loads a measure given the base table.
894 p_base_table is a concat list of base tables ...b1,b2, format
895 p_measure can also be "LIMIT CUBE" which is to load limit cubes
896 p_min_value is in the format 1,2, etc. min value and max value match base tables
897 if p_parameter ='LIMIT CUBE' then p_cubes will contain the limit cubes
898 p_start_lock_objects : we lock them in the begining itself
899 p_end_lock_objects : these we lock only after executing the load.
900 */
901 procedure load_cube_base_table(
902 p_kpi varchar2,
903 p_dim_set varchar2,
904 p_parameter varchar2,
905 p_cubes varchar2,--c1,c2 format
906 p_measures  varchar2,--m1,m2 format
907 p_base_table varchar2, --B1,B2,B3, format
908 p_start_lock_objects varchar2, --usually 1 cube c1, format
909 p_end_lock_objects varchar2, --in case of partitions, limit cubes
910 p_load_program varchar2,
911 p_LB_resync_program varchar2,
912 p_min_value varchar2,
913 p_max_value varchar2,
914 p_bt_current_period varchar2,
915 p_partition_options varchar2,
916 p_run_id number,p_job_name varchar2,p_options varchar2) is
917 --
918 l_cubes dbms_sql.varchar2_table;
919 l_measures dbms_sql.varchar2_table;
920 l_start_lock_objects dbms_sql.varchar2_table;
921 l_end_lock_objects dbms_sql.varchar2_table;
922 l_base_tables dbms_sql.varchar2_table;
923 l_stmt varchar2(4000);
924 l_min_values dbms_sql.varchar2_table;
925 l_max_values dbms_sql.varchar2_table;
926 l_bt_current_period dbms_sql.varchar2_table;
927 l_lock_objects dbms_sql.varchar2_table;
928 l_flag boolean;
929 l_ds_parameters dbms_sql.varchar2_table;
930 --
931 l_aggregation aggregation_r;
932 l_dim_set bsc_aw_adapter_kpi.dim_set_r;
933 l_partition_options varchar2(2000);
934 Begin
935   if p_run_id is not null then
936     --this is a dbms job. we have to do the initializations since this is a new session
937     bsc_aw_utility.g_options.delete;
938     bsc_aw_utility.parse_parameter_values(p_options,',',bsc_aw_utility.g_options);
939     bsc_aw_utility.open_file(p_job_name);
940     bsc_aw_utility.dmp_g_options(bsc_aw_utility.g_options);
941     bsc_aw_utility.init_all_procedures;
942     if g_debug then
943       bsc_aw_utility.clean_stats('group.load_cube_base_table');
944       bsc_aw_utility.load_stats('Start Of Process. Load kpi dimset cube base table','group.load_cube_base_table');
945     end if;
946   end if;
947   --
948   bsc_aw_utility.parse_parameter_values(p_start_lock_objects,',',l_start_lock_objects);
949   if p_end_lock_objects <> 'null' then
950     bsc_aw_utility.parse_parameter_values(p_end_lock_objects,',',l_end_lock_objects);
951   end if;
952   if p_cubes<>'null' then
953     bsc_aw_utility.parse_parameter_values(p_cubes,',',l_cubes);
954   end if;
955   if p_measures<>'null' then
956     bsc_aw_utility.parse_parameter_values(p_measures,',',l_measures);
957   end if;
958   l_partition_options:=p_partition_options;
959   if l_partition_options='null' then
960     l_partition_options:=null;
961   end if;
962   bsc_aw_utility.parse_parameter_values(p_base_table,',',l_base_tables);
963   bsc_aw_utility.parse_parameter_values(p_min_value,',',l_min_values);
964   bsc_aw_utility.parse_parameter_values(p_max_value,',',l_max_values);
965   bsc_aw_utility.parse_parameter_values(p_bt_current_period,',',l_bt_current_period);
966   l_base_tables:=bsc_aw_utility.make_upper(l_base_tables);
967   --
968   --get the lock. if measure, on the cube. if LIMIT CUBE, on all the limit cubes. note that when its LIMIT CUBE
969   --l_cubes will contain the limit cubes
970   bsc_aw_management.get_workspace_lock(l_start_lock_objects,null);
971   --
972   l_flag:=false;
973   for i in 1..l_base_tables.count loop
974     if to_number(l_min_values(i))<=to_number(l_max_values(i)) then
975       insert_bsc_aw_temp_cv(to_number(l_min_values(i)),to_number(l_max_values(i)),l_base_tables(i));
976       l_flag:=true;
977     end if;
978   end loop;
979   --
980   set_aggregation(p_kpi,l_aggregation);
981   l_dim_set:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,p_dim_set));
982   if p_parameter<>'LIMIT CUBE' then
983     /*see if we need to correct projection values or bal values with a change to current period in the base table */
984     check_bt_current_period_change(p_kpi,l_dim_set,l_cubes,l_measures,l_base_tables,l_bt_current_period,l_partition_options);
985   end if;
986   if l_flag then
987     /*set the cal end period rel for balance aggregation in the kpi load programs*/
988     limit_calendar_end_period_rel(l_dim_set.calendar);
989     get_ds_BT_parameters(p_kpi,p_dim_set,p_load_program,l_base_tables,l_ds_parameters);
990     if g_debug then
991       log('Before Load, Composite Counts');
992       dmp_dimset_composite_count(l_dim_set);
993     end if;
994     for i in 1..l_ds_parameters.count loop
995       --p_parameter contains cube and partition info
996       bsc_aw_dbms_aw.execute('call '||p_load_program||'('''||l_ds_parameters(i)||''','''||p_parameter||''')');
997     end loop;
998     if g_debug then
999       log('After Load, Composite Counts');
1000       dmp_dimset_composite_count(l_dim_set);
1001     end if;
1002     reset_calendar_end_period_rel(l_dim_set.calendar);
1003   end if;
1004   --get the lock on the end lock objects . must spin till lock is acquired. if not careful, wait can lead to deadlock. in this case,
1005   --all processes will start with the same order, say A then B then C. so the process to get A will get B and C. then others queue up for A.
1006   --once process 2 gets A, its guaranteed to get B and C. get_lock will also add the end lock objects to the global list, so commit_aw will
1007   --save then back to db
1008   --p_LB_resync_program is very important when we have partitions. without them, the LB will lose their information when we acquire the lock
1009   --in resync mode
1010   if l_end_lock_objects.count>0 then
1011     if p_LB_resync_program <> 'null' then
1012       bsc_aw_dbms_aw.execute('call '||p_LB_resync_program||'(''PRE'')');
1013     end if;
1014     bsc_aw_management.get_lock(l_end_lock_objects,'resync,wait type=active wait');
1015     if p_LB_resync_program <> 'null' then
1016       bsc_aw_dbms_aw.execute('call '||p_LB_resync_program||'(''POST'')');
1017     end if;
1018   end if;
1019   --
1020   --save the cubes back to database. also releases the lock
1021   bsc_aw_management.commit_aw;
1022   --
1023   if p_run_id is not null then
1024     bsc_aw_utility.send_pipe_message(p_job_name,'status=success');
1025     bsc_aw_management.detach_workspace;
1026   end if;
1027   commit;
1028   if p_run_id is not null and g_debug then
1029     bsc_aw_utility.load_stats('End Of Process. Load kpi dimset cube base table','group.load_cube_base_table');
1030     bsc_aw_utility.print_stats('group.load_cube_base_table');
1031   end if;
1032 Exception when others then
1033   log_n('Exception in load_cube_base_table '||sqlerrm);
1034   if p_run_id is not null then
1035     bsc_aw_utility.send_pipe_message(p_job_name,'status=error,sqlcode='||sqlcode||',message='||sqlerrm);
1036     rollback;
1037     bsc_aw_management.detach_workspace;
1038   else
1039     raise;
1040   end if;
1041 End;
1042 
1043 function check_load_type(p_property varchar2) return varchar2 is
1044 l_cv_value number;
1045 Begin
1046   --ping the aw table for data
1047   l_cv_value:=to_number(bsc_aw_utility.get_parameter_value(p_property,'current change vector',','));
1048   if l_cv_value=0 then
1049     return 'initial';
1050   else
1051     return 'inc';
1052   end if;
1053 Exception when others then
1054   log_n('Exception in check_load_type '||sqlerrm);
1055   raise;
1056 End;
1057 
1058 /*
1059 dbms job wrapper for aggregate_kpi_dimset
1060 */
1061 procedure aggregate_kpi_dimset_job(p_kpi varchar2,p_dim_set dbms_sql.varchar2_table) is
1062 --
1063 l_job_name varchar2(100);
1064 l_process varchar2(8000);
1065 l_job_status bsc_aw_utility.parallel_job_tb;
1066 Begin
1067   bsc_aw_utility.clean_up_jobs('all');
1068   for i in 1..p_dim_set.count loop
1069     l_job_name:='bsc_aw_aggregate_kpi_dimset_'||bsc_aw_utility.get_dbms_time||'_'||i;
1070     l_process:='bsc_aw_load_kpi.aggregate_kpi_dimset('''||p_kpi||''','''||p_dim_set(i)||''','||i||','''||l_job_name||''','''||
1071     bsc_aw_utility.get_option_string||''');';
1072     bsc_aw_utility.start_job(l_job_name,i,l_process,null);
1073   end loop;
1074   bsc_aw_utility.wait_on_jobs(null,l_job_status);
1075   for i in 1..l_job_status.count loop
1076     if l_job_status(i).status='error' then
1077       raise bsc_aw_utility.g_exception;
1078     end if;
1079   end loop;
1080 Exception when others then
1081   log_n('Exception in aggregate_kpi_dimset_job '||sqlerrm);
1082   raise;
1083 End;
1084 
1085 /*
1086 given a kpi and dimset, aggregate the cubes involved. just a wrapper for
1087 procedure aggregate_kpi_dimset(p_aggregation aggregation_r)
1088 */
1089 procedure aggregate_kpi_dimset(p_kpi varchar2,p_dim_set varchar2,
1090 p_run_id number,p_job_name varchar2,p_options varchar2) is
1091 l_aggregation aggregation_r;
1092 l_dimset_index number;
1093 Begin
1094   if p_run_id is not null then
1095     --this is a dbms job. we have to do the initializations since this is a new session
1096     bsc_aw_utility.g_options.delete;
1097     bsc_aw_utility.parse_parameter_values(p_options,',',bsc_aw_utility.g_options);
1098     bsc_aw_utility.open_file('Aggregate_KD_'||p_dim_set||'_'||p_run_id);
1099     bsc_aw_utility.dmp_g_options(bsc_aw_utility.g_options);
1100     bsc_aw_utility.init_all_procedures;
1101   end if;
1102   --l_aggregation has info on all dimsets for the kpi
1103   set_aggregation(p_kpi,l_aggregation);
1104   --find the actual and target pairs
1105   l_dimset_index:=0;
1106   l_dimset_index:=get_dim_set_index(l_aggregation,p_dim_set);
1107   aggregate_kpi_dimset(p_kpi,l_aggregation,l_aggregation.dim_set(l_dimset_index));
1108   if p_run_id is not null then
1109     bsc_aw_utility.send_pipe_message(p_job_name,'status=success');
1110     bsc_aw_management.detach_workspace;
1111   end if;
1112   commit;
1113 Exception when others then
1114   log_n('Exception in aggregate_kpi_dimset '||sqlerrm);
1115   if p_run_id is not null then
1116     bsc_aw_utility.send_pipe_message(p_job_name,'status=error,sqlcode='||sqlcode||',message='||sqlerrm);
1117     rollback;
1118     bsc_aw_management.detach_workspace;
1119   else
1120     raise;
1121   end if;
1122 End;
1123 
1124 /*
1125 this procedure looks at the list of dimsets and then eliminates targets when corresponding actuals exist
1126 when there is a pair, aggregate only the actual. aggregating actuals will aggregate targets also
1127 then we aggregate standalone targets
1128 */
1129 procedure get_aggregate_dimsets(
1130 p_kpi varchar2,
1131 p_dim_set dbms_sql.varchar2_table,
1132 p_aggregate_dimset out nocopy dbms_sql.varchar2_table
1133 ) is
1134 l_aggregation aggregation_r;
1135 l_agg_flag dbms_sql.varchar2_table;--will be used to eliminate "target" dimsets that have "actual" dimsets aggregating
1136 Begin
1137   --l_aggregation has info on all dimsets for the kpi
1138   set_aggregation(p_kpi,l_aggregation);
1139   --find the actual and target pairs
1140   for i in 1..l_aggregation.dim_set.count loop
1141     l_agg_flag(i):='N';
1142     if bsc_aw_utility.in_array(p_dim_set,l_aggregation.dim_set(i).dim_set_name) then
1143       --see if the dimset does not need agg (if pre-calc)
1144       if l_aggregation.dim_set(i).pre_calculated='N' then
1145         l_agg_flag(i):='Y';
1146       else
1147         if g_debug then
1148           log('Dimset '||p_dim_set(i)||' is pre calculated');
1149         end if;
1150       end if;
1151     end if;
1152   end loop;
1153   for i in 1..l_aggregation.dim_set.count loop
1154     if l_aggregation.dim_set(i).dim_set_type='actual' and l_agg_flag(i)='Y' then
1155       --see if there is a target too
1156       for j in 1..l_aggregation.dim_set.count loop
1157         if l_aggregation.dim_set(j).dim_set_type='target' and l_aggregation.dim_set(j).base_dim_set=l_aggregation.dim_set(i).dim_set_name then
1158           l_agg_flag(j):='N';
1159           exit;
1160         end if;
1161       end loop;
1162     end if;
1163   end loop;
1164   for i in 1..l_aggregation.dim_set.count loop
1165     if l_agg_flag(i)='Y' then
1166       p_aggregate_dimset(p_aggregate_dimset.count+1):=l_aggregation.dim_set(i).dim_set_name;
1167     end if;
1168   end loop;
1169 Exception when others then
1170   log_n('Exception in get_aggregate_dimsets '||sqlerrm);
1171   raise;
1172 End;
1173 
1174 /*
1175 This procedure is only called as a part of the kpi dimset load
1176 its also called when base tables are loaded. with B tables, we find out the affected
1177 dimsets and aggregate them
1178 logic
1179 for the dim set get the dim
1180 for the dim get the limit cubes
1181 find out the levels of the dim for this dim set
1182 get the level positions. limit levels to within adv sum profile.
1183 see if there is zero code. for the levels with zero code, get the zero code level name
1184 
1185 look at adv sum profile
1186 limit the dim to the limit cube
1187 for time, limit to all applicable periodicities
1188 for rec dim, call api to find out the levels to limit to. for now, aggregate all levels
1189 get the cubes.
1190 look at the agg formula. if its simple, we can aggregate it
1191 if its a formula, we need to execute the formula after we aggregate the cubes
1192 
1193 if there are targets at higher levels we do
1194 1. load the dimset
1195 2. aggregate the dimset
1196 3. load the targets
1197 4. aggregate the dimset
1198 
1199 after each agg, the limit cubes are reset
1200 
1201 read the measures of the dimset.
1202 in 9i, we aggregate all measures together
1203 1. we aggregate normal measures
1204 2. we aggregate balance measures
1205 3. we aggregate measures with formula. this is one by one
1206 
1207 In 10g, we can aggregate cubes in parallel
1208 in 10g, we can launch aggregate_measure in parallel
1209 
1210 if the measure is balance, we dont aggregate it with other measures, we use the agg_map_notime to aggregate
1211 balance measures
1212 
1213 if aggregation is complex with formula, then we cannot launch the aggregaqtion of the measure in paralle because
1214 this measure needs the other measures to be aggregated before it can execute the formula
1215 this means when we set the dim status, we have to set the status to the higher levels and then aggregate
1216 
1217 forecast or projections are handled like this:
1218 each kpi has default dim "projection". when we aggregate we have projection dim as Y and N.
1219 then we limit the dim to Y. then for each periodicity, for those periods where there is a mix of
1220 real and projected data, we make the cube data=0
1221 look at correct_forecast_aggregation
1222 
1223 if there are measures with average, we aggergate them separately from measures with sum or other agg functions
1224 
1225 please note that the agg maps have opvar and argvar. we aggregate sum, average measures together
1226 
1227 if dimset=actual then
1228 aggregate actuals
1229 aggregate targets of corresponding target dimset
1230 if dimset=target then
1231 aggregate targets of corresponding target dimset
1232 
1233 "aggregate targets of corresponding target dimset" has both the copy from targets to actuals and the aggregation of actuals
1234 */
1235 procedure aggregate_kpi_dimset(
1236 p_kpi varchar2,
1237 p_aggregation aggregation_r,
1238 p_dim_set bsc_aw_adapter_kpi.dim_set_r) is
1239 --
1240 l_dim_set bsc_aw_adapter_kpi.dim_set_r;
1241 Begin
1242   --get the full dimset lock
1243   lock_dimset_objects(p_kpi,p_dim_set.dim_set_name,null,null);
1244   if p_dim_set.dim_set_type='actual' then
1245     --get corresponding target dimset
1246     l_dim_set.dim_set_name:=null;
1247     for i in 1..p_aggregation.dim_set.count loop
1248       if p_aggregation.dim_set(i).dim_set_type='target' and p_aggregation.dim_set(i).base_dim_set=p_dim_set.dim_set_name then
1249         l_dim_set:=p_aggregation.dim_set(i);
1250         exit;
1251       end if;
1252     end loop;
1253     aggregate_kpi_dimset_actuals(p_kpi,p_dim_set);
1254     if l_dim_set.dim_set_name is not null then
1255       --aggregate targets only if there are targets
1256       --p_dim_set=actual, l_dim_set=target
1257       --lock the target limit cubes only!!
1258       lock_dimset_objects(p_kpi,l_dim_set.dim_set_name,'dim limit cube','inc');
1259       aggregate_kpi_dimset_targets(p_kpi,p_dim_set,l_dim_set);
1260     end if;
1261   elsif p_dim_set.dim_set_type='target' then
1262     --get corresponding actual dimset
1263     l_dim_set.dim_set_name:=null;
1264     for i in 1..p_aggregation.dim_set.count loop
1265       if p_aggregation.dim_set(i).dim_set_type='actual' and p_aggregation.dim_set(i).dim_set_name=p_dim_set.base_dim_set then
1266         l_dim_set:=p_aggregation.dim_set(i);
1267         exit;
1268       end if;
1269     end loop;
1270     --l_dim_set=actual, p_dim_set=target
1271     --if l_dim_set.dim_set_name is null, its an error, every target must have an actuals
1272     aggregate_kpi_dimset_targets(p_kpi,l_dim_set,p_dim_set);
1273   end if;
1274   --set status to allstat in reset_dim_limits. in 10g parallelism, the dim status here is not what we need.
1275   --so best to set the status to allstat. N: reset_dim_limits sets status to allstat!!
1276   --NOTE!!! reset_dim_limits is permanently changing the limit cubes
1277   reset_dim_limits(p_dim_set);
1278   if l_dim_set.dim_set_name is not null then
1279     reset_dim_limits(l_dim_set);
1280   end if;
1281   bsc_aw_management.commit_aw;
1282   commit;
1283 Exception when others then
1284   log_n('Exception in aggregate_kpi_dimset '||sqlerrm);
1285   raise;
1286 End;
1287 
1288 /*
1289 this procedure is used to aggregate actuals cubes
1290 N:
1291 assumption>>>> when we enter this procedure, all changes have been updated and commited
1292 the process before this is load. load will update and commit for every dimset/base table combination
1293 whether called as a job or in the same session
1294 must be very careful. commit_aw releases locks. so may have to call commit_aw('no release lock') option we have to
1295 do a commit here
1296 */
1297 procedure aggregate_kpi_dimset_actuals(p_kpi varchar2,p_dim_set bsc_aw_adapter_kpi.dim_set_r) is
1298 --
1299 l_measures dbms_sql.varchar2_table;
1300 l_parallel boolean;
1301 l_aggregate_option varchar2(2000);
1302 Begin
1303   --we aggregate all measures that are not BALANCE aggregation
1304   if g_debug then
1305     log_n('=====================================');
1306     log('aggregate_kpi_dimset_actuals '||p_dim_set.dim_set_name);
1307     log('=====================================');
1308   end if;
1309   --
1310   --limit the dim for aggregate_measure and aggregate_measure_formula
1311   --so these 2 api do not have to call limit of dim
1312   --aggregate_measure_job will launch jobs to aggregate the cubes. there are 2 pre-reqs
1313   --1. dim limit cubes are saved back to the database
1314   --2. the cubes are saved back to the database
1315   --at this point, we come from either load or aggregate targets. so the limit cubes and cubes are in saved state
1316   --N: the above is the assumption!!
1317   --N:when we have compressed composites, we cannot limit dim status before aggregations. also we cannot copy data into the higher
1318   --levels. this means we cannot have targets at higher levels when we have compressed composites
1319   limit_all_dim(p_dim_set);
1320   --
1321   /*new logic
1322   if there are non bal measures and bal measures then
1323     first, aggregate non bal and bal measures on notime
1324     then aggregate non bal on only time
1325   elsif there is only non bal then
1326     aggregate on dimset agg map
1327   elsif there is only bal then
1328     this is covered by first if itself
1329   end if
1330   this is 25 percent faster than old method of aggregating first the non bal on all dim and then bal mesures on notime
1331   if we take 2 years of data, only 730 distinct days can exist. an equal or more combinations belong to other dimensions. so one time
1332   aggregation on other dim for all measures is faster
1333   another approach using parallelism is to have bal measures on separate composite so bal and non bal measures can be aggregated in parallel
1334   however, this is not useful because launching 8 processes on 4 cpu box is no point. aggregation is cpu intensive. it doubles storage too
1335   --
1336   the best way would have been if we could aggregate bal and non bal measures in one shot. when the aggregation is on time, measurename dim
1337   must be limited to non bal measures. for other dim, to bal and non bal measures. tried having a diff opvar for time with entry for bal measures
1338   null. aw simply assumed SUM aggregation. so did not work. then tried placing value like NONE for bal measures. aw nulled out higher values
1339   we cannot use precompute or valueset because both can only constrain time values, not values of another dim
1340   */
1341   l_measures.delete;
1342   for i in 1..p_dim_set.measure.count loop
1343     if p_dim_set.measure(i).agg_formula.std_aggregation='Y' then
1344       l_measures(l_measures.count+1):=p_dim_set.measure(i).measure;
1345     end if;
1346   end loop;
1347   if dimset_has_bal_measures(p_dim_set) then
1348     l_aggregate_option:='notime';
1349   else
1350     l_aggregate_option:=null;
1351   end if;
1352   --aggregate these measures that are like sum etc
1353   if l_measures.count>0 then
1354     l_parallel:=false;
1355     if can_launch_jobs(p_kpi,p_dim_set,l_measures)='Y' then
1356       l_parallel:=true;
1357     end if;
1358     /*check to see if there is sufficient load to warrant parallel load .
1359     earlier we had is_parallel_aggregate test here. we cannot do this. when we aggregate, 100 nodes can explode into 100000 nodes
1360     depending on the hier depths. this means we cannot disable parallel aggregation just because there are onlyu 100 nodes. load could
1361     bring in 100 nodes. the only time we can do is_parallel_aggregate test is for formula and target copy because the nodes are
1362     not increasing in these cases
1363     */
1364     if l_parallel=false then
1365       aggregate_measure(p_kpi,p_dim_set,l_measures,l_aggregate_option);
1366     else
1367       aggregate_measure_job(p_kpi,p_dim_set,l_measures,l_aggregate_option,'normal');
1368     end if;
1369   end if;
1370   ----------------
1371   /*now we aggregate non balance measures on time alone. we do this if the dimset needs to aggregate on time
1372   if the dimset is partitioned at the lowest time, there is no agg on time*/
1373   if bsc_aw_adapter_kpi.is_calendar_aggregated(p_dim_set.calendar) then
1374     l_measures.delete;
1375     if dimset_has_bal_measures(p_dim_set) then
1376       for i in 1..p_dim_set.measure.count loop
1377         if p_dim_set.measure(i).agg_formula.std_aggregation='Y' and p_dim_set.measure(i).measure_type='NORMAL' then
1378           l_measures(l_measures.count+1):=p_dim_set.measure(i).measure;
1379         end if;
1380       end loop;
1381     end if;
1382     if l_measures.count>0 then
1383       l_aggregate_option:='onlytime';
1384       --aggregate these measures that are non BALANCE
1385       --aggregate_measure will first aggregate on all dim except time then aggregate on time for non bal measures
1386       --we will check each time l_parallel because some other load may have launched many jobs.
1387       l_parallel:=false;
1388       if can_launch_jobs(p_kpi,p_dim_set,l_measures)='Y' then
1389         l_parallel:=true;
1390       end if;
1391       /*earlier we had is_parallel_aggregate test here. we cannot do this */
1392       if l_parallel=false then
1393         aggregate_measure(p_kpi,p_dim_set,l_measures,l_aggregate_option);
1394       else
1395         aggregate_measure_job(p_kpi,p_dim_set,l_measures,l_aggregate_option,'onlytime');
1396       end if;
1397     end if;
1398   else
1399     if g_debug then
1400       log('Aggregation not specified on time');
1401     end if;
1402   end if;
1403   -----------------
1404   --now aggregate the formulas
1405   l_measures.delete;
1406   for i in 1..p_dim_set.measure.count loop
1407     if p_dim_set.measure(i).agg_formula.std_aggregation='N' and p_dim_set.measure(i).sql_aggregated='N' then
1408       l_measures(l_measures.count+1):=p_dim_set.measure(i).measure;
1409     end if;
1410   end loop;
1411   l_parallel:=false;
1412   if can_launch_jobs(p_kpi,p_dim_set,l_measures)='Y' then
1413     l_parallel:=true;
1414   end if;
1415   if l_measures.count>0 then
1416     if l_parallel and p_dim_set.compressed='N' then
1417       /*cannot do is_parallel_aggregate test for CC,comp count is compressed node count*/
1418       if is_parallel_aggregate(p_dim_set,bsc_aw_utility.g_parallel_aggregate_cutoff)=false then
1419         if g_debug then
1420           log('Due to insufficient load, Parallel aggregate for dimset measures made serial');
1421         end if;
1422         l_parallel:=false;
1423       end if;
1424     end if;
1425     if l_parallel=false then
1426       aggregate_measure_formula(p_kpi,p_dim_set,l_measures,null);
1427     else
1428       aggregate_measure_job(p_kpi,p_dim_set,l_measures,null,'formula');
1429     end if;
1430   end if;
1431 Exception when others then
1432   log_n('Exception in aggregate_kpi_dimset_actuals '||sqlerrm);
1433   raise;
1434 End;
1435 
1436 /*
1437 this procedure is used to aggregate actuals cubes
1438 procedure called when there is targets at higher levels
1439 logic:
1440 limit all dim to bool status
1441 limit all dim add ancestors
1442 limit all dim add target bool status
1443 set limit  variables to false
1444 --
1445 limit all dim -> keep only level of target
1446 set limit.bool to true by looping across target composite (take any 1 measure)
1447 limit dim to limit.bool
1448 copy data from target to actuals, across target composite
1449 aggregate again
1450 
1451 time: targets must have the same periodicity as dim set
1452 */
1453 procedure aggregate_kpi_dimset_targets(
1454 p_kpi varchar2,
1455 p_actual_dim_set bsc_aw_adapter_kpi.dim_set_r,
1456 p_target_dim_set bsc_aw_adapter_kpi.dim_set_r
1457 ) is
1458 --
1459 l_dim_set_reagg bsc_aw_adapter_kpi.dim_set_r;
1460 l_parallel boolean;
1461 l_measures dbms_sql.varchar2_table;
1462 l_lock_object_limit_cubes dbms_sql.varchar2_table;
1463 Begin
1464   if g_debug then
1465     log_n('=====================================');
1466     log('aggregate_kpi_dimset_targets');
1467     log('=====================================');
1468   end if;
1469   --
1470   --N:we cannot have compressed composites and targets at higher levels since targets need to copy data into the higher
1471   --levels.
1472   limit_all_dim(p_actual_dim_set);
1473   --
1474   limit_dim_ancestors(p_actual_dim_set.dim,'ADD');
1475   limit_calendar_ancestors(p_actual_dim_set.calendar,'ADD');
1476   --
1477   limit_dim_values(p_target_dim_set.dim,'ADD');--note>> targets abd actuals have the same dim, diff levels
1478   limit_dim_values(p_target_dim_set.std_dim,'ADD');
1479   limit_calendar_values(p_target_dim_set.calendar,'ADD');
1480   --
1481   --the status of dim is actuals + targets.
1482   --limit all dim -> keep only level of target. this does NOT limit TIME
1483   limit_dim_target_level_only(p_actual_dim_set,p_target_dim_set);
1484   --
1485   --set limit.bool to true by looping across target composite (take any 1 measure)
1486   --to get the target composite name, we can simply take 1 measure. even in 10g, we need to consider
1487   --only 1 measure. so we look at measure(1)
1488   --earlier: limit_dim_limit_cube(p_target_dim_set.dim,'TRUE',p_target_dim_set.measure(1).composite_name);
1489   limit_dim_limit_cube(p_target_dim_set.dim,'TRUE');
1490   limit_dim_limit_cube(p_target_dim_set.std_dim,'TRUE');
1491   limit_dim_limit_cube(p_target_dim_set.calendar.limit_cube,'TRUE',p_target_dim_set.calendar.limit_cube_composite);
1492   --
1493   l_measures.delete;
1494   for i in 1..p_actual_dim_set.measure.count loop
1495     l_measures(l_measures.count+1):=p_actual_dim_set.measure(i).measure;
1496   end loop;
1497   --
1498   l_parallel:=false;
1499   if can_launch_jobs(p_kpi,p_actual_dim_set,l_measures)='Y' then
1500     l_parallel:=true;
1501   end if;
1502   if l_parallel and p_target_dim_set.compressed='N' then
1503     if is_parallel_aggregate(p_target_dim_set,bsc_aw_utility.g_parallel_target_cutoff)=false then
1504       if g_debug then
1505         log('Due to insufficient load, Parallel copy from Target to Actuals made serial');
1506       end if;
1507       l_parallel:=false;
1508     end if;
1509   end if;
1510   if l_parallel=false then
1511     copy_target_to_actual_serial(p_actual_dim_set,p_target_dim_set,l_measures);
1512   else
1513     --even if there is one cube and the cube is partitioned, can_launch_jobs will return Y
1514     copy_target_to_actual_job(p_kpi,p_actual_dim_set,p_target_dim_set,l_measures);
1515   end if;
1516   --aggregate again
1517   /*
1518   once we copy the targets and we are ready to re-aggregate, we have to be careful. we cannot do
1519   aggregate_kpi_dimset_actuals(p_kpi,p_actual_dim_set);
1520   this is because the levels of p_actual_dim_set are lower than the targets. so we should do
1521   aggregate_kpi_dimset_actuals(p_kpi,target_dim_set);
1522   but this also does not work since the measures in target_dim_set is not the ones we are  aggregating.
1523   so we create a new l_dim_set_reagg. we place calendar and dim from targets and rest from actuals and aggregate it
1524   */
1525   /*
1526   we need to solve a complex issue here. say we have a hier change in the upper levels. say the dim are
1527   component > product > prod family, release and geog (city > state > country > region) now, the target is at prod, release
1528   city. there is a change to country > region hier. agg of the actuals will null out target data in the following levels
1529   prod  release   country
1530   fam  release   country
1531   prod release   region
1532   fam release   region
1533   this is because, data tries to aggregate for actuals from component level. here, there is no data for targets. so the aggregates
1534   get null. the copy in the code above will not bring in the data since grog is at country level. there is target data at
1535   prod   release   state level. we need to reaggregate this data. now, the limit cubes not only reflect the hier changes but also
1536   the inc data.so to keep symmetey, we will drill the dim down to descendents and then keep the target level. then re-aggregate the
1537   target data
1538   N:we assume that the actuals limit cubes are intact and have not been reset
1539   */
1540   --===
1541   limit_all_dim(p_actual_dim_set);
1542   limit_dim_descendents(p_actual_dim_set.dim,'ADD','DESCENDANTS');
1543   limit_calendar_descendents(p_actual_dim_set.calendar,'ADD','DESCENDANTS');
1544   limit_dim_target_level_only(p_actual_dim_set,p_target_dim_set); --does not limit TIME
1545   /*here there is no more agg for balance. balance agg has already happened in the kpi load programs
1546   both for actuals and targets. so we can limit time also to the level of target */
1547   limit_cal_target_level_only(p_actual_dim_set,p_target_dim_set);
1548   limit_dim_limit_cube(p_target_dim_set.dim,'TRUE');
1549   limit_dim_limit_cube(p_target_dim_set.std_dim,'TRUE');
1550   limit_dim_limit_cube(p_target_dim_set.calendar.limit_cube,'TRUE',p_target_dim_set.calendar.limit_cube_composite);
1551   get_dimset_objects_to_lock(p_kpi,p_target_dim_set.dim_set_name,'dim limit cube',l_lock_object_limit_cubes);
1552   bsc_aw_management.commit_aw(l_lock_object_limit_cubes,'no release lock');
1553   --===
1554   l_dim_set_reagg:=p_actual_dim_set;
1555   l_dim_set_reagg.dim:=p_target_dim_set.dim;
1556   l_dim_set_reagg.std_dim:=p_target_dim_set.std_dim;
1557   l_dim_set_reagg.calendar:=p_target_dim_set.calendar; --we need the target limit cubes, periodicities
1558   l_dim_set_reagg.calendar.agg_map:=p_actual_dim_set.calendar.agg_map;/*copy info missing in target calendar */
1559   --
1560   aggregate_kpi_dimset_actuals(p_kpi,l_dim_set_reagg);
1561   --
1562 Exception when others then
1563   log_n('Exception in aggregate_kpi_dimset_targets '||sqlerrm);
1564   raise;
1565 End;
1566 
1567 /*
1568 copy in serial or non parallel mode
1569 this converts measures to cubes and calls copy_target_to_actual
1570 */
1571 procedure copy_target_to_actual_serial(
1572 p_actual_dim_set bsc_aw_adapter_kpi.dim_set_r,
1573 p_target_dim_set bsc_aw_adapter_kpi.dim_set_r,
1574 p_actual_measures dbms_sql.varchar2_table) is
1575 --
1576 l_cubes dbms_sql.varchar2_table;
1577 Begin
1578   get_cubes_for_measures(p_actual_measures,p_actual_dim_set,l_cubes);
1579   copy_target_to_actual(p_actual_dim_set,p_target_dim_set,l_cubes,null,null);
1580 Exception when others then
1581   log_n('Exception in copy_target_to_actual_serial '||sqlerrm);
1582   raise;
1583 End;
1584 
1585 /*
1586 we commit the target limit cubes.
1587 the actual cubes are in a saved state. aggregate_kpi_dimset_actuals would have saved the cubes
1588 N: when copying targets to actuals, we cannot parallelize by partitions since target and actuals
1589 are in diff partition templates we can only parallelize by cubes
1590 when there are targets, the actuals cannot be partitioned at all. only the targets can be partitioned
1591 --
1592 the above is not true anymore. with hash partitions on time, we can partition when there are targets as long as actuals and targets have the
1593 same periodicity. this way, data at a particular period is guaranteed to be in P.x for both actuals and targets. this is true even when there
1594 are balance (end period) and the load has agg bal measure. we need to launch jobs for cubes+partitions
1595 */
1596 procedure copy_target_to_actual_job(
1597 p_kpi varchar2,
1598 p_actual_dim_set bsc_aw_adapter_kpi.dim_set_r,
1599 p_target_dim_set bsc_aw_adapter_kpi.dim_set_r,
1600 p_actual_measures dbms_sql.varchar2_table
1601 ) is
1602 --
1603 l_lock_cubes dbms_sql.varchar2_table;
1604 --
1605 l_job_name varchar2(100);
1606 l_process varchar2(8000);
1607 l_job_status bsc_aw_utility.parallel_job_tb;
1608 --
1609 l_cubes dbms_sql.varchar2_table;
1610 pt_comp dbms_sql.varchar2_table;/*for each l_cubes */
1611 pt_comp_type dbms_sql.varchar2_table;
1612 agg_cubes dbms_sql.varchar2_table;
1613 agg_pt_comp dbms_sql.varchar2_table;
1614 cube_pt bsc_aw_adapter_kpi.partition_template_r;
1615 aggregate_options varchar2(2000);
1616 l_run_id number;
1617 Begin
1618   bsc_aw_management.save_lock_set('target copy');
1619   bsc_aw_management.commit_lock_set('target copy','no release lock');
1620   --release locks on the actual cubes
1621   l_lock_cubes.delete;
1622   get_measure_objects_to_lock(p_actual_dim_set,p_actual_measures,l_lock_cubes);
1623   bsc_aw_management.release_lock(l_lock_cubes); --release lock on the cube and countvar cube. locks still exist on LB
1624   --
1625   get_cubes_for_measures(p_actual_measures,p_actual_dim_set,l_cubes);
1626   --
1627   /*we find the distinct pt_comp. then for each, partitions. then launch as many jobs */
1628   for i in 1..l_cubes.count loop
1629     pt_comp(i):=null;
1630     pt_comp_type(i):=null;
1631     pt_comp(i):=bsc_aw_adapter_kpi.get_cube_pt_comp(l_cubes(i),p_actual_dim_set,pt_comp_type(i));/*l_cubes are actuals cubes */
1632   end loop;
1633   /* */
1634   l_run_id:=0;
1635   aggregate_options:=null;
1636   bsc_aw_utility.clean_up_jobs('all');
1637   agg_cubes.delete;
1638   for i in 1..pt_comp.count loop /*first all cubes that do not have composite or PT */
1639     if pt_comp(i) is null then
1640       agg_cubes(agg_cubes.count+1):=l_cubes(i);
1641     end if;
1642   end loop;
1643   if agg_cubes.count>0 then
1644     l_run_id:=l_run_id+1;
1645     l_job_name:='bsc_aw_copy_target_actual_'||bsc_aw_utility.get_dbms_time||'_'||l_run_id;
1646     l_process:='bsc_aw_load_kpi.copy_target_to_actual_job('''||p_kpi||''','''||p_actual_dim_set.dim_set_name||''','''||
1647     p_target_dim_set.dim_set_name||''','''||bsc_aw_utility.make_string_from_list(agg_cubes)||''',''null'','||
1648     l_run_id||','''||l_job_name||''','''||bsc_aw_utility.get_option_string||''');';
1649     bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
1650   end if;
1651   for i in 1..pt_comp.count loop
1652     if pt_comp(i) is not null then
1653       bsc_aw_utility.merge_value(agg_pt_comp,pt_comp(i));
1654     end if;
1655   end loop;
1656   if agg_pt_comp.count>0 then
1657     for i in 1..agg_pt_comp.count loop
1658       agg_cubes.delete;
1659       for j in 1..pt_comp.count loop
1660         if agg_pt_comp(i)=pt_comp(j) then
1661           agg_cubes(agg_cubes.count+1):=l_cubes(j);
1662         end if;
1663       end loop;
1664       if agg_cubes.count>0 then
1665         aggregate_options:=null;
1666         cube_pt:=bsc_aw_adapter_kpi.get_partition_template_r(agg_pt_comp(i),p_actual_dim_set);
1667         if cube_pt.template_name is not null then /*this is a PT */
1668           for j in 1..cube_pt.template_partitions.count loop
1669             aggregate_options:='partition='||cube_pt.template_partitions(j).partition_name||',partition dim value='||
1670             cube_pt.template_partitions(j).partition_dim_value;
1671             l_run_id:=l_run_id+1;
1672             l_job_name:='bsc_aw_copy_target_actual_PT_'||bsc_aw_utility.get_dbms_time||'_'||l_run_id;
1673             l_process:='bsc_aw_load_kpi.copy_target_to_actual_job('''||p_kpi||''','''||p_actual_dim_set.dim_set_name||''','''||
1674             p_target_dim_set.dim_set_name||''','''||bsc_aw_utility.make_string_from_list(agg_cubes)||''','''||aggregate_options||''','||
1675             l_run_id||','''||l_job_name||''','''||bsc_aw_utility.get_option_string||''');';
1676             bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
1677           end loop;
1678         else /*this is a composite */
1679           l_run_id:=l_run_id+1;
1680           l_job_name:='bsc_aw_copy_target_actual_'||bsc_aw_utility.get_dbms_time||'_'||l_run_id;
1681           l_process:='bsc_aw_load_kpi.copy_target_to_actual_job('''||p_kpi||''','''||p_actual_dim_set.dim_set_name||''','''||
1682           p_target_dim_set.dim_set_name||''','''||bsc_aw_utility.make_string_from_list(agg_cubes)||''',''null'','||
1683           l_run_id||','''||l_job_name||''','''||bsc_aw_utility.get_option_string||''');';
1684           bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
1685         end if;
1686       end if;
1687     end loop;
1688   end if;
1689   bsc_aw_utility.wait_on_jobs(null,l_job_status);
1690   for i in 1..l_job_status.count loop
1691     if l_job_status(i).status='error' then
1692       raise bsc_aw_utility.g_exception;
1693     end if;
1694   end loop;
1695   /*we will detach and attach the workspace here to prevent errors due to resync*/
1696   bsc_aw_management.detach_workspace;
1697   bsc_aw_management.lock_lock_set('target copy',null);/*gets lock on LB and cubes. resync will be done is needed
1698   this will also attach the workspace back*/
1699 Exception when others then
1700   log_n('Exception in copy_target_to_actual_job '||sqlerrm);
1701   raise;
1702 End;
1703 
1704 /* this procedure is only launched as a job. this is just a wrapper for copy_target_to_actual*/
1705 procedure copy_target_to_actual_job(
1706 p_kpi varchar2,
1707 p_actual_dimset varchar2,
1708 p_target_dimset varchar2,
1709 p_cubes varchar2,
1710 p_aggregate_options varchar2,/*all p_actual_cubes must have the same partition value*/
1711 p_run_id number,p_job_name varchar2,p_options varchar2) is
1712 --
1713 l_aggregation aggregation_r;
1714 l_actual_dimset bsc_aw_adapter_kpi.dim_set_r;
1715 l_target_dimset bsc_aw_adapter_kpi.dim_set_r;
1716 l_cubes dbms_sql.varchar2_table;
1717 l_lock_objects dbms_sql.varchar2_table;
1718 partition_value varchar2(40);
1719 partition_dim_value varchar2(40);
1720 Begin
1721   if p_run_id is not null then
1722     --this is a dbms job. we have to do the initializations since this is a new session
1723     bsc_aw_utility.g_options.delete;
1724     bsc_aw_utility.parse_parameter_values(p_options,',',bsc_aw_utility.g_options);
1725     bsc_aw_utility.open_file('Copy_TA_'||p_target_dimset||'_'||p_run_id);
1726     bsc_aw_utility.dmp_g_options(bsc_aw_utility.g_options);
1727     bsc_aw_utility.init_all_procedures;
1728   end if;
1729   partition_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition',',');/*P.0, P.1 etc */
1730   partition_dim_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition dim value',','); /*1, 2 etc */
1731   bsc_aw_utility.parse_parameter_values(p_cubes,',',l_cubes);
1732   set_aggregation(p_kpi,l_aggregation);
1733   l_actual_dimset:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,p_actual_dimset));
1734   l_target_dimset:=l_aggregation.dim_set(get_dim_set_index(l_aggregation,p_target_dimset));
1735   --get locks
1736   l_lock_objects:=l_cubes;
1737   if partition_value is not null then
1738     for i in 1..l_lock_objects.count loop
1739       l_lock_objects(i):=l_lock_objects(i)||'(partition '||partition_value||')';
1740     end loop;
1741   end if;
1742   bsc_aw_management.get_workspace_lock(l_lock_objects,null);
1743   --
1744   copy_target_to_actual(l_actual_dimset,l_target_dimset,l_cubes,partition_value,partition_dim_value);
1745   --save the cubes back to database
1746   bsc_aw_management.commit_aw(l_lock_objects);
1747   --
1748   if p_run_id is not null then
1749     bsc_aw_utility.send_pipe_message(p_job_name,'status=success');
1750     bsc_aw_management.detach_workspace;--release the lock
1751   end if;
1752   commit;
1753 Exception when others then
1754   log_n('Exception in copy_target_to_actual_job '||sqlerrm);
1755   if p_run_id is not null then
1756     bsc_aw_utility.send_pipe_message(p_job_name,'status=error,sqlcode='||sqlcode||',message='||sqlerrm);
1757     rollback;
1758     bsc_aw_management.detach_workspace; --this will release the locks
1759   else
1760     raise;
1761   end if;
1762 End;
1763 
1764 /*
1765 copy_target_to_actual for serial processing
1766 copy_target_to_actual_job for parallel processing
1767 we pass a list of actual cubes here. from the cubes, we find the measures and then from the measures, we find the target cube
1768 N:we assume that if in actuals, measures m1,m2,m3 belong to c1, then for targets, the 3 measures belong to a corresponding
1769 cube, say c1.tgt
1770 when there are targets, actuals cannot have partitions!!
1771 */
1772 procedure copy_target_to_actual(
1773 p_actual_dim_set bsc_aw_adapter_kpi.dim_set_r,
1774 p_target_dim_set bsc_aw_adapter_kpi.dim_set_r,
1775 p_actual_cubes dbms_sql.varchar2_table,
1776 p_partition_value varchar2,
1777 p_partition_dim_value varchar2
1778 ) is
1779 --
1780 l_pt_comp varchar2(200);
1781 l_pt_comp_type varchar2(200);
1782 l_measures bsc_aw_adapter_kpi.measure_tb;
1783 l_target_cube bsc_aw_adapter_kpi.cube_r;
1784 Begin
1785   if g_debug then
1786     bsc_aw_utility.clean_stats('group.copy_target_to_actual');
1787     bsc_aw_utility.load_stats('Start Of Process. Copy Target to Actual','group.copy_target_to_actual');
1788   end if;
1789   /* */
1790   push_dim(p_target_dim_set.dim);
1791   push_dim(p_target_dim_set.std_dim);
1792   push_dim(p_target_dim_set.calendar.aw_dim);
1793   /* */
1794   limit_dim_values(p_target_dim_set.dim,'to');
1795   limit_dim_values(p_target_dim_set.std_dim,'to');
1796   limit_calendar_values(p_target_dim_set.calendar,'to');
1797   if p_partition_value is not null then /*p_partition_value P.0 etc */
1798     push_dim(p_actual_dim_set.partition_dim);
1799     limit_dim(p_actual_dim_set.partition_dim,p_partition_dim_value,'TO');
1800   end if;
1801   /* */
1802   if g_debug then
1803     dmp_dimset_dim_statlen(p_target_dim_set);
1804     log('Target Composite Counts');
1805     dmp_dimset_composite_count(p_target_dim_set);
1806     log('Actual Composite Counts');
1807     dmp_dimset_composite_count(p_actual_dim_set);
1808   end if;
1809   --copy data from target to actuals, across target composite
1810   for i in 1..p_actual_cubes.count loop
1811     --get target cube
1812     bsc_aw_adapter_kpi.get_measures_for_cube(p_actual_cubes(i),p_actual_dim_set,l_measures);
1813     --all measures must be in the same tgt cube
1814     --limit measurename dim to the measures of the cube. actual and target share the same measurename_dim
1815     /*when we have targets, we do not have compressed composite */
1816     push_dim(p_actual_dim_set.measurename_dim);
1817     limit_dim(p_actual_dim_set.measurename_dim,'NULL','TO');
1818     for j in 1..l_measures.count loop
1819       limit_dim(p_actual_dim_set.measurename_dim,''''||l_measures(j).measure||'''','ADD');
1820     end loop;
1821     --
1822     l_target_cube:=bsc_aw_adapter_kpi.get_cube_set_for_measure(l_measures(1).measure,p_target_dim_set).cube;
1823     --get target cube PT or comp
1824     l_pt_comp:=bsc_aw_adapter_kpi.get_cube_pt_comp(l_target_cube.cube_name,p_target_dim_set,l_pt_comp_type);
1825     copy_target_to_actual(p_actual_cubes(i),l_target_cube.cube_name,l_pt_comp);
1826     if g_debug then
1827       log('After Target Copy, Actual Composite Counts');
1828       dmp_dimset_composite_count(p_actual_dim_set);
1829     end if;
1830     pop_dim(p_actual_dim_set.measurename_dim);
1831   end loop;
1832   if g_debug then
1833     bsc_aw_utility.load_stats('End Of Process. Copy Target to Actual','group.copy_target_to_actual');
1834     bsc_aw_utility.print_stats('group.copy_target_to_actual');
1835   end if;
1836   if p_partition_value is not null then
1837     pop_dim(p_actual_dim_set.partition_dim);
1838   end if;
1839   pop_dim(p_target_dim_set.calendar.aw_dim);
1840   pop_dim(p_target_dim_set.std_dim);
1841   pop_dim(p_target_dim_set.dim);
1842 Exception when others then
1843   log_n('Exception in copy_target_to_actual '||sqlerrm);
1844   raise;
1845 End;
1846 
1847 /*
1848 there was a question on whether the copy from one PT to another will produce accurate results
1849 datacube.1=datacube.2 across PT.2  if they 2 PT share the same partition dim, then the operation results in loop across the
1850 partition dim. pdim=0, then execute the stmt. then pdim=2, execute the stmt etc. this will make sure that the data from one
1851 partition is taken to just another partition. since partitioning is done on an independent dim, at this point, its as if that dim is
1852 just another dim. there is no migration across partitions. tested this with a prototype.
1853 --
1854 more complications: when targets exist, actuals cannot be partitioned. targets can be. targets have diff levels. so if we partition the
1855 actuals, to which partition will we copy the target data?
1856 */
1857 procedure copy_target_to_actual(
1858 p_actual_cube varchar2,
1859 p_target_cube varchar2,
1860 p_composite varchar2
1861 ) is
1862 Begin
1863   /*now that balance aggregations are done in the kpi program itself, targets for non balance measures will be null at higher levels
1864   of time */
1865   g_stmt:=p_actual_cube||'=if '||p_target_cube||' EQ NA then '||p_actual_cube||' else '||p_target_cube;
1866   if p_composite is not null then
1867     g_stmt:=g_stmt||' across '||p_composite;
1868   end if;
1869   bsc_aw_dbms_aw.execute(g_stmt);
1870 Exception when others then
1871   log_n('Exception in copy_target_to_actual '||sqlerrm);
1872   raise;
1873 End;
1874 
1875 /*
1876 we follow this logic
1877 create a temp virtual kpi . naming: <kpi>_<dimset>_aggregate
1878 then create the metadata using bsc_aw_md_wrapper.create_kpi
1879 then the threads will use this to aggregate
1880 then delete it from bsc olap metadata bsc_aw_md_wrapper.drop_kpi;
1881 this proc has to release the locks for the measures and then re-acquire them at the end
1882 N: we do not call bsc_aw_management.commit_aw   aggregate_kpi_dimset_actuals must manage that.
1883 we have p_measure_agg_type so we do not have to create a api called "aggregate_measure_formula_job"
1884 and repeat all the code in aggregate_measure_job in aggregate_measure_formula_job
1885 */
1886 procedure aggregate_measure_job(
1887 p_kpi varchar2,
1888 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
1889 p_measures dbms_sql.varchar2_table,
1890 p_options varchar2,
1891 p_measure_agg_type varchar2 --normal, balance or formula
1892 ) is
1893 --
1894 l_kpi_r bsc_aw_adapter_kpi.kpi_r;
1895 l_lock_objects dbms_sql.varchar2_table;
1896 --
1897 l_job_name varchar2(100);
1898 l_process varchar2(8000);
1899 l_job_status bsc_aw_utility.parallel_job_tb;
1900 l_api_name varchar2(200);
1901 l_run_id number;
1902 --
1903 l_cube_set bsc_aw_adapter_kpi.cube_set_r;
1904 l_cube_pt bsc_aw_adapter_kpi.partition_template_r;
1905 l_cubes_to_aggregate dbms_sql.varchar2_table;
1906 l_aggregate_options varchar2(2000);
1907 l_cube_for_measure dbms_sql.varchar2_table;
1908 l_measures_for_cube dbms_sql.varchar2_table;
1909 Begin
1910   if g_debug then
1911     log('aggregate_measure_job '||p_kpi);
1912   end if;
1913   l_kpi_r.kpi:=p_kpi||'_'||p_dim_set.dim_set_name||'_aggregate';
1914   l_kpi_r.parent_kpi:=p_kpi;
1915   l_kpi_r.dim_set(1):=p_dim_set;
1916   --first drop the metadata if it exists
1917   bsc_aw_md_wrapper.drop_kpi(l_kpi_r.kpi);
1918   --then create
1919   bsc_aw_md_wrapper.create_kpi(l_kpi_r);
1920   commit;
1921   --release locks on the cubes, l_lock_objects are the cubes and the countvar cubes
1922   get_measure_objects_to_lock(p_dim_set,p_measures,l_lock_objects);
1923   --N:> we are making an assumption here that the cubes can be released and that there are no outstanding
1924   --changes to be updated and commited for the cubes!!!!!
1925   bsc_aw_management.release_lock(l_lock_objects);
1926   --
1927   l_api_name:='bsc_aw_load_kpi.aggregate_measure';
1928   --
1929   bsc_aw_utility.clean_up_jobs('all');
1930   for i in 1..p_measures.count loop
1931     l_cube_set:=bsc_aw_adapter_kpi.get_cube_set_for_measure(p_measures(i),p_dim_set);
1932     l_cube_for_measure(i):=l_cube_set.cube.cube_name;
1933     if bsc_aw_utility.in_array(l_cubes_to_aggregate,l_cube_set.cube.cube_name)=false then
1934       l_cubes_to_aggregate(l_cubes_to_aggregate.count+1):=l_cube_set.cube.cube_name;
1935     end if;
1936   end loop;
1937   --
1938   /*
1939   if the cube is partitioned, its a job per partition. else its a job per cube
1940   */
1941   l_run_id:=0;
1942   for i in 1..l_cubes_to_aggregate.count loop
1943     l_measures_for_cube.delete;
1944     for j in 1..p_measures.count loop
1945       if l_cube_for_measure(j)=l_cubes_to_aggregate(i) then
1946         if bsc_aw_utility.in_array(l_measures_for_cube,p_measures(j))=false then
1947           l_measures_for_cube(l_measures_for_cube.count+1):=p_measures(j);
1948         end if;
1949       end if;
1950     end loop;
1951     l_cube_pt.template_name:=bsc_aw_adapter_kpi.get_cube_axis(l_cubes_to_aggregate(i),p_dim_set,'partition template');
1952     if l_cube_pt.template_name is not null then --this is a partitioned cube. job per partition
1953       l_cube_pt:=bsc_aw_adapter_kpi.get_partition_template_r(l_cube_pt.template_name,p_dim_set);
1954       for j in 1..l_cube_pt.template_partitions.count loop
1955         l_aggregate_options:=p_options||',partition='||l_cube_pt.template_partitions(j).partition_name||',partition dim value='||
1956         l_cube_pt.template_partitions(j).partition_dim_value;
1957         l_run_id:=l_run_id+1;
1958         l_job_name:='bsc_aw_aggregate_measure_'||bsc_aw_utility.get_dbms_time||'_'||l_run_id;
1959         l_process:=l_api_name||'('''||l_kpi_r.kpi||''','''||bsc_aw_utility.make_string_from_list(l_measures_for_cube)||''','''||
1960         l_aggregate_options||''','''||p_measure_agg_type||''','||l_run_id||','''||l_job_name||''','''||
1961         bsc_aw_utility.get_option_string||''');';
1962         bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
1963       end loop;
1964     else --non partitioned cube. launch job per cube
1965       l_aggregate_options:=p_options;
1966       l_run_id:=l_run_id+1;
1967       l_job_name:='bsc_aw_aggregate_measure_'||bsc_aw_utility.get_dbms_time||'_'||l_run_id;
1968       l_process:=l_api_name||'('''||l_kpi_r.kpi||''','''||bsc_aw_utility.make_string_from_list(l_measures_for_cube)||''','''||
1969       l_aggregate_options||''','''||p_measure_agg_type||''','||l_run_id||','''||l_job_name||''','''||
1970       bsc_aw_utility.get_option_string||''');';
1971       bsc_aw_utility.start_job(l_job_name,l_run_id,l_process,null);
1972     end if;
1973   end loop;
1974   bsc_aw_utility.wait_on_jobs(null,l_job_status);
1975   for i in 1..l_job_status.count loop
1976     if l_job_status(i).status='error' then
1977       raise bsc_aw_utility.g_exception;
1978     end if;
1979   end loop;
1980   --get lock back on the cubes
1981   bsc_aw_management.get_lock(l_lock_objects,'resync');
1982   --
1983   bsc_aw_md_wrapper.drop_kpi(l_kpi_r.kpi);
1984   commit;
1985 Exception when others then
1986   log_n('Exception in aggregate_measure_job '||sqlerrm);
1987   rollback;
1988   if g_debug is null or g_debug=false then
1989     bsc_aw_md_wrapper.drop_kpi(l_kpi_r.kpi);
1990     commit;
1991   end if;
1992   raise;
1993 End;
1994 
1995 /*
1996 wrapper for aggregate_measure. this is called from aggregate_measure_job as dbms job
1997 N:!!!! p_kpi here is p_kpi||'_'||p_dim_set.dim_set_name||'_aggregate';
1998 any query going to olap metadata with p_kpi is going for p_kpi||'_'||p_dim_set.dim_set_name||'_aggregate'
1999 p_measure_agg_type is used so we dont have to un-necessarily code a wrapper for aggregate_measure_formula
2000 with the same logic as this procedure
2001 */
2002 procedure aggregate_measure(
2003 p_kpi varchar2, -- is p_kpi||'_'||p_dim_set.dim_set_name||'_aggregate';
2004 p_measures varchar2,
2005 p_aggregate_options varchar2,
2006 p_measure_agg_type varchar2, --normal, balance or formula
2007 p_run_id number,p_job_name varchar2,p_options varchar2) is
2008 --
2009 l_measures dbms_sql.varchar2_table;
2010 l_aggregation aggregation_r;
2011 l_dimset bsc_aw_adapter_kpi.dim_set_r;
2012 --
2013 l_lock_objects dbms_sql.varchar2_table;
2014 l_partition_value varchar2(200);
2015 l_partition_dim_value varchar2(200);
2016 Begin
2017   if p_run_id is not null then
2018     --this is a dbms job. we have to do the initializations since this is a new session
2019     bsc_aw_utility.g_options.delete;
2020     bsc_aw_utility.parse_parameter_values(p_options,',',bsc_aw_utility.g_options);
2021     bsc_aw_utility.open_file('Agg_M_'||p_kpi||'_'||p_measure_agg_type||'_'||bsc_aw_utility.get_dbms_time||'_'||p_run_id);
2022     bsc_aw_utility.dmp_g_options(bsc_aw_utility.g_options);
2023     bsc_aw_utility.init_all_procedures;
2024   end if;
2025   --
2026   bsc_aw_utility.parse_parameter_values(p_measures,',',l_measures);
2027   --
2028   set_aggregation(p_kpi,l_aggregation);
2029   --N: l_aggregation must have only 1 dimset
2030   l_dimset:=l_aggregation.dim_set(1);
2031   --get lock we only lock the cube and countvar cube
2032   get_measure_objects_to_lock(l_dimset,l_measures,l_lock_objects);
2033   --if there is partition, then add the partition stmt to the lock objects
2034   l_partition_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition',',');
2035   l_partition_dim_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition dim value',',');
2036   if l_partition_value is not null then
2037     for i in 1..l_lock_objects.count loop
2038       l_lock_objects(i):=l_lock_objects(i)||'(partition '||l_partition_value||')';
2039     end loop;
2040   end if;
2041   bsc_aw_management.get_workspace_lock(l_lock_objects,null);
2042   --
2043   limit_all_dim(l_dimset);
2044   --
2045   if p_measure_agg_type='formula' then
2046     aggregate_measure_formula(p_kpi,l_dimset,l_measures,p_aggregate_options);
2047   else
2048     aggregate_measure(p_kpi,l_dimset,l_measures,p_aggregate_options);
2049   end if;
2050   --release locks. cubes have already been saved in aggregate_measure and aggregate_measure_formula
2051   bsc_aw_management.release_lock(l_lock_objects);
2052   if p_run_id is not null then
2053     bsc_aw_utility.send_pipe_message(p_job_name,'status=success');
2054     bsc_aw_management.detach_workspace;--release the lock
2055   end if;
2056   commit;
2057 Exception when others then
2058   log_n('Exception in aggregate_measure '||sqlerrm);
2059   if p_run_id is not null then
2060     bsc_aw_utility.send_pipe_message(p_job_name,'status=error,sqlcode='||sqlcode||',message='||sqlerrm);
2061     rollback;
2062     bsc_aw_management.detach_workspace; --this will release the locks
2063   else
2064     raise;
2065   end if;
2066 End;
2067 
2068 /*
2069 given a dimset and a list of measures, this procedure will aggregate them
2070 in 10g, this may be called from another api which is launched as a dbms job
2071 
2072 correct_forecast_aggregation has to happen for normal measures only as these are aggregated on time
2073 and for balance measures
2074 if Jan 20 is the current period and from jan 21 we have projection, for the month of jan, we should
2075 only consider data till Jan 20 for aggregation. so for jan, we make month value with projection=Y as 0
2076 */
2077 procedure aggregate_measure(
2078 p_kpi varchar2,
2079 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
2080 p_measures dbms_sql.varchar2_table,
2081 p_aggregate_options varchar2
2082 ) is
2083 --
2084 l_agg_stmt varchar2(4000);
2085 l_flag dbms_sql.varchar2_table;
2086 l_measures bsc_aw_adapter_kpi.measure_tb;
2087 l_aggregate_flag boolean;
2088 --
2089 l_lock_objects dbms_sql.varchar2_table;
2090 l_cubes_to_aggregate dbms_sql.varchar2_table;
2091 l_cube_set bsc_aw_adapter_kpi.cube_set_tb;
2092 l_partition_value varchar2(200);
2093 l_partition_dim_value varchar2(200);
2094 l_agg_map varchar2(200);
2095 l_countvar_stmt varchar2(4000);
2096 Begin
2097   if g_debug then
2098     log_n('In aggregate_measure, kpi='||p_kpi||', dim set='||p_dim_set.dim_set_name||
2099     ', p_aggregate_options='||p_aggregate_options);
2100     log('Measures:-');
2101     for i in 1..p_measures.count loop
2102       log(p_measures(i));
2103     end loop;
2104     bsc_aw_utility.clean_stats('group.aggregate_measure');
2105     bsc_aw_utility.load_stats('Start Of Process. Aggregate Measure','group.aggregate_measure');
2106   end if;
2107   l_partition_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition',',');
2108   l_partition_dim_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition dim value',',');
2109   --first mark the measures that need to aggregated
2110   for i in 1..p_dim_set.measure.count loop
2111     if bsc_aw_utility.in_array(p_measures,p_dim_set.measure(i).measure) then
2112       l_flag(i):='Y';
2113       l_measures(l_measures.count+1):=p_dim_set.measure(i);
2114     else
2115       l_flag(i):='N';
2116     end if;
2117   end loop;
2118   --
2119   for i in 1..l_measures.count loop
2120     if bsc_aw_utility.in_array(l_cubes_to_aggregate,l_measures(i).cube)=false then
2121       l_cubes_to_aggregate(l_cubes_to_aggregate.count+1):=l_measures(i).cube;
2122       l_cube_set(l_cube_set.count+1):=bsc_aw_adapter_kpi.get_cube_set_r(l_measures(i).cube,p_dim_set);
2123     end if;
2124   end loop;
2125   --
2126   if p_dim_set.compressed='N' then /*we can limit measurename dim only when the comp is non compressed */
2127     limit_dim(p_dim_set.measurename_dim,'NULL','TO');
2128     for i in 1..l_measures.count loop
2129       limit_dim(p_dim_set.measurename_dim,''''||l_measures(i).measure||'''','ADD');
2130     end loop;
2131   end if;
2132   /*when the dimset is compressed, we cannot limit the partition dim. we need to mention the partition in the aggregate
2133   command
2134   */
2135   if p_dim_set.compressed='N' and l_partition_value is not null then
2136     push_dim(p_dim_set.partition_dim);
2137     limit_dim(p_dim_set.partition_dim,l_partition_dim_value,'TO');
2138     /*tried to see if dimensioning LB with hash partition dim can improve perf of partition aggregations. the idea was that the dim must be
2139     limited to only the values in the partition. it did not help perf. firstly, since the partition is on time, all partitions contian all other
2140     dim values. agg cost is in index traversal. if there are 1000 values of a dim in P.0, there is good change there are the same 1000 values in P.1
2141     cost in traversing the index is the same. so timing with and without partitions seem equal
2142     some more investigation into the log file found that even after limiting the partition dim, the thread aggregates all partitions.
2143     we have to specify the partition to make sure only that partition is agregated (aggregate cube (partition P.0) ...
2144     for this, we have to make entry in measuredim for cube (partition P.n)
2145     */
2146   end if;
2147   /*
2148   issue when we specify (partition P.0) with countvar cube.
2149   ORA-33852: You provided extra input starting at '('. so we will limit the partition dim and not specify the (part...) clause
2150   */
2151   l_agg_stmt:='aggregate';
2152   for i in 1..l_cube_set.count loop
2153     l_agg_stmt:=l_agg_stmt||' '||l_cube_set(i).cube.cube_name;
2154     if l_partition_value is not null then
2155       l_agg_stmt:=l_agg_stmt||' (partition '||l_partition_value||')';
2156     end if;
2157     l_lock_objects(l_lock_objects.count+1):=l_cube_set(i).cube.cube_name;
2158     if l_partition_value is not null then
2159       l_lock_objects(l_lock_objects.count):=l_lock_objects(l_lock_objects.count)||'(partition '||l_partition_value||')';
2160     end if;
2161   end loop;
2162   l_aggregate_flag:=true;
2163   if bsc_aw_utility.get_parameter_value(p_aggregate_options,'notime',',')='Y' then
2164     l_agg_map:=p_dim_set.agg_map_notime.agg_map;
2165   elsif bsc_aw_utility.get_parameter_value(p_aggregate_options,'onlytime',',')='Y' then
2166     l_agg_map:=p_dim_set.calendar.agg_map.agg_map;
2167   else
2168     l_agg_map:=p_dim_set.agg_map.agg_map;
2169   end if;
2170   if l_agg_map is null then
2171     l_aggregate_flag:=false;
2172   else
2173     l_countvar_stmt:=null;
2174     for i in 1..l_cube_set.count loop
2175       if l_cube_set(i).countvar_cube.cube_name is not null then
2176         l_countvar_stmt:=l_countvar_stmt||' '||l_cube_set(i).countvar_cube.cube_name;
2177         l_lock_objects(l_lock_objects.count+1):=l_cube_set(i).countvar_cube.cube_name;
2178         if l_partition_value is not null then
2179           l_lock_objects(l_lock_objects.count):=l_lock_objects(l_lock_objects.count)||'(partition '||l_partition_value||')';
2180         end if;
2181       end if;
2182     end loop;
2183     l_agg_stmt:=l_agg_stmt||' using '||l_agg_map;
2184     if l_countvar_stmt is not null then
2185       l_countvar_stmt:=' countvar '||l_countvar_stmt;
2186       l_agg_stmt:=l_agg_stmt||l_countvar_stmt;
2187     end if;
2188   end if;
2189   --
2190   if l_aggregate_flag then
2191     --before we execute agg maps we must limit the measuredim to the measures we are aggregating. measuredim contains all
2192     --the measures
2193     /*if we are aggregating measures in time alone, we need to limit all other dim add parents
2194     this happens when there are balance measures and non bal measures in the dimset*/
2195     if bsc_aw_utility.get_parameter_value(p_aggregate_options,'onlytime',',')='Y' then
2196       push_dim(p_dim_set.dim);
2197       limit_dim_ancestors(p_dim_set.dim,'ADD');
2198     end if;
2199     limit_measure_dim(p_dim_set.aggmap_operator,l_cubes_to_aggregate,l_partition_value);
2200     /*I:if countvar cube is needed, we cannot have partitions. cannot have stmt like
2201     aggregate datacube.4.4014 (partition P.0) using aggmap.4.4014.notime countvar test.cube (partition P.0) */
2202     if g_debug then
2203       log('Before Aggregate');
2204       dmp_dimset_dim_statlen(p_dim_set);
2205       dmp_dimset_composite_count(p_dim_set);
2206     end if;
2207     bsc_aw_dbms_aw.execute(l_agg_stmt);
2208     if bsc_aw_utility.get_parameter_value(p_aggregate_options,'onlytime',',')='Y' then
2209       pop_dim(p_dim_set.dim);
2210     end if;
2211     --if this is balance measures, then aggregate on time
2212     if bsc_aw_utility.get_parameter_value(p_aggregate_options,'notime',',')='Y' then
2213       --aggregate_balance_time(p_kpi,p_dim_set,l_measures,p_aggregate_options);
2214       /*balance aggregation are now done in the kpi load programs. we had issues aggregating balances here because looping across
2215       comp or PT is not possible to create higher periodicity balances. without looping across comp or PT, perf is very bad since
2216       its creating all logical records*/
2217       null;
2218     else
2219       --if there is forecast, we need to correct the forecast periods(periods where real and forecast data mix)
2220       --this is called for normal measures and balance measures
2221       correct_forecast_aggregation(p_kpi,p_dim_set,l_measures,p_aggregate_options);
2222     end if;
2223     --save the changes
2224     --we need to save the changes after aggregating the measures because in 10g, the subsequent operations can be in parallel
2225     --in diff sessions. for example, there can be aggregate formula which is m3=m1/m2. if we do not save m1 and m2, when
2226     --m3 is computed in a dbms job, it will not get the aggregated values of m1 and m2.
2227     bsc_aw_management.commit_aw(l_lock_objects,'no release lock');
2228     if g_debug then
2229       log('After Aggregate');
2230       dmp_dimset_composite_count(p_dim_set);
2231     end if;
2232   end if;
2233   if l_partition_value is not null then
2234     pop_dim(p_dim_set.partition_dim);
2235   end if;
2236   if g_debug then
2237     bsc_aw_utility.load_stats('End Of Process. Aggregate Measure','group.aggregate_measure');
2238     bsc_aw_utility.print_stats('group.aggregate_measure');
2239   end if;
2240 Exception when others then
2241   log_n('Exception in aggregate_measure '||sqlerrm);
2242   raise;
2243 End;
2244 
2245 /*
2246 this procedure executes agg formula. this is called only for those measures that have agg formula
2247 
2248 given a dimset and a list of measures, this procedure will aggregate them
2249 in 10g, this may be called from another api which is launched as a dbms job
2250 
2251 handles on those measure that have agg formula like average at the lowest level
2252 here we do the following
2253 limit the dim values
2254 limit the levels
2255 limit the periods and periodicities
2256 each dim, limit status to ancestors
2257 limit time values to ancestors
2258 
2259 note: when we limit dimensions, we limit all dim, whether there is rollup or not (single level).
2260 when we take the dim to the ancestors, we do so if there is a relation defined on the dim
2261 
2262 when this api is called, we have already checked and seen that p_measure has a non-std formula
2263 based agg
2264 */
2265 procedure aggregate_measure_formula(
2266 p_kpi varchar2,
2267 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
2268 p_measures dbms_sql.varchar2_table,
2269 p_aggregate_options varchar2
2270 ) is
2271 --
2272 l_agg_stmt varchar2(4000);
2273 l_flag dbms_sql.varchar2_table;
2274 l_measures bsc_aw_adapter_kpi.measure_tb;
2275 l_lock_objects dbms_sql.varchar2_table;
2276 --
2277 l_cube_set bsc_aw_adapter_kpi.cube_set_tb;
2278 l_pt_name dbms_sql.varchar2_table;
2279 l_pt_type dbms_sql.varchar2_table;
2280 l_partition_value varchar2(200);
2281 l_partition_dim_value varchar2(200);
2282 Begin
2283   --in 10g, l_aggregation will be null if this is a new thread. in 9i, this is already set in aggregate_kpi_dimset
2284   if g_debug then
2285     log_n('In aggregate_measure_formula, kpi='||p_kpi||', dim set='||p_dim_set.dim_set_name||
2286     ', p_aggregate_options='||p_aggregate_options);
2287     log('Measures:-');
2288     for i in 1..p_measures.count loop
2289       log(p_measures(i));
2290     end loop;
2291     bsc_aw_utility.clean_stats('group.aggregate_measure_formula');
2292     bsc_aw_utility.load_stats('Start Of Process. Aggregate Measure Formula','group.aggregate_measure_formula');
2293   end if;
2294   --first mark the measures that need to aggregated
2295   --take the dim values to the parents
2296   push_dim(p_dim_set.dim);
2297   push_dim(p_dim_set.calendar.aw_dim);
2298   --
2299   for i in 1..p_dim_set.measure.count loop
2300     if bsc_aw_utility.in_array(p_measures,p_dim_set.measure(i).measure) then
2301       l_flag(i):='Y';
2302       l_measures(l_measures.count+1):=p_dim_set.measure(i);
2303       l_lock_objects(l_lock_objects.count+1):=l_measures(l_measures.count).cube;
2304     else
2305       l_flag(i):='N';
2306     end if;
2307   end loop;
2308   --
2309   for i in 1..l_measures.count loop
2310     l_cube_set(i):=bsc_aw_adapter_kpi.get_cube_set_r(l_measures(i).cube,p_dim_set);
2311     l_pt_name(i):=null;
2312     l_pt_type(i):=null;
2313     l_pt_name(i):=bsc_aw_adapter_kpi.get_cube_pt_comp(l_measures(i).cube,p_dim_set,l_pt_type(i));
2314   end loop;
2315   l_partition_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition',',');
2316   l_partition_dim_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition dim value',',');
2317   /*
2318   if this procedure has been called for a partition, we limit the partition dim.
2319   if there is partitions, we must save back to the system the partitions of the cube
2320   we do not have compressed composite when we have formula
2321   */
2322   if l_partition_value is not null then
2323     push_dim(p_dim_set.partition_dim);
2324     limit_dim(p_dim_set.partition_dim,l_partition_dim_value,'TO');
2325     for i in 1..l_lock_objects.count loop
2326       l_lock_objects(i):=l_lock_objects(i)||'(partition '||l_partition_value||')';
2327     end loop;
2328   end if;
2329   /*limit measurename dim to all measures of the dimset
2330   when we have formulas that are non sql aggregated, we cannot be having compressed composites*/
2331   limit_dim(p_dim_set.measurename_dim,'NULL','TO');
2332   for i in 1..p_dim_set.measure.count loop
2333     limit_dim(p_dim_set.measurename_dim,''''||p_dim_set.measure(i).measure||'''','ADD');
2334   end loop;
2335   /*
2336   when aggregating formulas, we have to aggregate over each dim. say there is
2337   prod > cat > zero
2338   city > state > zero
2339   type
2340   day > month > year
2341   we have to aggregate over each dim, keeping other dim at all their levels.
2342   this means prod limited to cat and zero. other dim limited to all levels
2343   then geog limited to state and zero. other dim limited to all levels
2344   same for time
2345   */
2346   --across all dim except time
2347   --if this is a non rec single level dim, skpi it. no agg on this dim
2348   limit_dim_ancestors(p_dim_set.dim,'ADD');
2349   limit_calendar_ancestors(p_dim_set.calendar,'ADD');
2350   --now the dim and time are limited to all values including lowest level.
2351   --for each dim, we need to start remoivng the lowest level
2352   --dim push and pop go in pairs
2353   --d1 =1 to 10. push d1. then d1 changed to 4 to 8. then push d1. then pop d1. d1 is 4 to 8, then again pop d1
2354   --d1 is now 1 to 10
2355   --about the REMOVE...this was designed at the time we had zero code as a virtual level and we created a level for this
2356   --now, zero is only a value in the level. zero code level is a pure virtual level that just dimensions the relation, so we
2357   --can aggregate . this means, if we simply remove the level, then formula does not get calculated for zero values.
2358   --we need to do this : if there is only 1 level and there is agg, its zero code agg. so we limit the value of the level to "0"
2359   --limit dim to the level
2360   for i in 1..p_dim_set.dim.count loop
2361     if is_aggregation_on_dim(p_dim_set.dim(i)) then
2362       if p_dim_set.dim(i).levels.count=1 and p_dim_set.dim(i).recursive='N' then
2363         push_dim(p_dim_set.dim(i).dim_name);
2364         bsc_aw_dbms_aw.execute('limit '||p_dim_set.dim(i).levels(1).level_name||' to ''0''');
2365         limit_dim(p_dim_set.dim(i).dim_name,p_dim_set.dim(i).levels(1).level_name,'TO');
2366       else
2367         --remove the lowest level
2368         push_dim(p_dim_set.dim(i).dim_name);
2369         limit_dim(p_dim_set.dim(i).dim_name,p_dim_set.dim(i).levels(1).level_name,'REMOVE');
2370       end if;
2371       --aggregate
2372       /*in 10g, when this is running as a job, we cannot use across composite. the composite for the formula cube does nothave
2373       the higher dim values. this means across composite will not apply the formula to any higher level dim value
2374       say we have cube per measure in 10g and each measure has its own composite. then we cannot say across
2375       we do not have this case where we have measure cube and separate composites. if its 10g, its datacube
2376       if its 9i its measurecube with the same composite. so taking off check on db version*/
2377       for j in 1..l_measures.count loop
2378         l_agg_stmt:=l_measures(j).cube;
2379         if l_cube_set(j).cube_set_type='datacube' then
2380           l_agg_stmt:=l_agg_stmt||'('||l_cube_set(j).measurename_dim||' '''||l_measures(j).measure||''')';
2381         end if;
2382         l_agg_stmt:=l_agg_stmt||'=('||l_measures(j).agg_formula.agg_formula||')';
2383         if l_pt_name(j) is not null then
2384           l_agg_stmt:=l_agg_stmt||' across '||l_pt_name(j);
2385         end if;
2386         if g_debug then
2387           dmp_dimset_dim_statlen(p_dim_set);
2388           dmp_dimset_composite_count(p_dim_set);
2389         end if;
2390         bsc_aw_dbms_aw.execute(l_agg_stmt);
2391         if g_debug then
2392           dmp_dimset_composite_count(p_dim_set);
2393         end if;
2394       end loop;
2395       pop_dim(p_dim_set.dim(i).dim_name);
2396     end if;
2397   end loop;
2398   --now do the same on time
2399   /*there is a question here as to what we need to do if the formula is loading a balance column. balance agg on time has happened at load
2400   time. however, a user has defined a formula also. this means the formula takes precedence over the agg at load time. i dont think there is
2401   a case where balance measure has formula to aggregate */
2402   if is_aggregation_on_time(p_dim_set.calendar) then
2403     push_dim(p_dim_set.calendar.aw_dim);
2404     for i in 1..p_dim_set.calendar.periodicity.count loop
2405       if p_dim_set.calendar.periodicity(i).lowest_level='Y' then
2406         limit_dim(p_dim_set.calendar.aw_dim,p_dim_set.calendar.periodicity(i).aw_dim,'REMOVE');
2407       end if;
2408     end loop;
2409     --aggregate
2410     for i in 1..l_measures.count loop
2411       l_agg_stmt:=l_measures(i).cube;
2412       if l_cube_set(i).cube_set_type='datacube' then
2413         l_agg_stmt:=l_agg_stmt||'('||l_cube_set(i).measurename_dim||' '''||l_measures(i).measure||''')';
2414       end if;
2415       l_agg_stmt:=l_agg_stmt||'=('||l_measures(i).agg_formula.agg_formula||')';
2416       if l_pt_name(i) is not null then
2417         l_agg_stmt:=l_agg_stmt||' across '||l_pt_name(i);
2418       end if;
2419       if g_debug then
2420         dmp_dimset_dim_statlen(p_dim_set);
2421         dmp_dimset_composite_count(p_dim_set);
2422       end if;
2423       bsc_aw_dbms_aw.execute(l_agg_stmt);
2424       if g_debug then
2425         dmp_dimset_composite_count(p_dim_set);
2426       end if;
2427     end loop;
2428     pop_dim(p_dim_set.calendar.aw_dim);
2429   end if;
2430   pop_dim(p_dim_set.dim);
2431   pop_dim(p_dim_set.calendar.aw_dim);
2432   if l_partition_value is not null then
2433     pop_dim(p_dim_set.partition_dim);
2434   end if;
2435   --save the changes
2436   bsc_aw_management.commit_aw(l_lock_objects,'no release lock');
2437   if g_debug then
2438     bsc_aw_utility.load_stats('End Of Process. Aggregate Measure Formula','group.aggregate_measure_formula');
2439     bsc_aw_utility.print_stats('group.aggregate_measure_formula');
2440   end if;
2441 Exception when others then
2442   log_n('Exception in aggregate_measure_formula '||sqlerrm);
2443   raise;
2444 End;
2445 
2446 /*
2447 this procedure is called only if the measure has projections on it. in this case,
2448 we limit the projection dim to Y, then we look at each periodicity for the cube. for
2449 each periodicity, we choose the period where the real and projected data are present.
2450 we then set the value of the cube to 0
2451 
2452 at this time, all dim are limited to the values for aggregation.
2453 projection dim is limited to Y and N
2454 time is limited to all the affected lowest level values (days)
2455 
2456 we limit projection to Y
2457 we loop through each periodicity, remove the affected periods from the status of time dim
2458 
2459 then we set cube=0
2460 
2461 we set the cube=0 only for those measures that have forecast. if a measure does not have forecast, we do not
2462 have to do this.
2463 */
2464 procedure correct_forecast_aggregation(
2465 p_kpi varchar2,
2466 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
2467 p_measures bsc_aw_adapter_kpi.measure_tb,
2468 p_aggregate_options varchar2
2469 ) is
2470 --
2471 l_period varchar2(100);
2472 l_pt_comp varchar2(200);
2473 l_pt_comp_type varchar2(200);
2474 l_partition_value varchar2(200);
2475 l_partition_dim_value varchar2(200);
2476 l_cubes dbms_sql.varchar2_table;
2477 l_measures_to_limit dbms_sql.varchar2_table;
2478 Begin
2479   if bsc_aw_adapter_kpi.is_calendar_aggregated(p_dim_set.calendar) then
2480     push_dim(get_projection_dim(p_dim_set));
2481     push_dim(p_dim_set.dim);
2482     push_dim(p_dim_set.calendar.aw_dim);
2483     limit_dim_ancestors(p_dim_set.dim,'ADD');
2484     g_stmt:='limit '||get_projection_dim(p_dim_set)||' to ''Y''';
2485     bsc_aw_dbms_aw.execute(g_stmt);
2486     g_stmt:='limit '||p_dim_set.calendar.aw_dim||' to null';
2487     bsc_aw_dbms_aw.execute(g_stmt);
2488     l_partition_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition',',');
2489     l_partition_dim_value:=bsc_aw_utility.get_parameter_value(p_aggregate_options,'partition dim value',',');
2490     if l_partition_value is not null then
2491       push_dim(p_dim_set.partition_dim);
2492       limit_dim(p_dim_set.partition_dim,l_partition_dim_value,'TO');
2493     end if;
2494     --loop through each periodicity. we dont have to do this for lowest periodicity
2495     for i in 1..p_dim_set.calendar.periodicity.count loop
2496       --we specify the calendar and periodicity and expect to get back the period for which there is
2497       --real and projected data
2498       --if p_dim_set.calendar.periodicity(i).lowest_level='N' then
2499       if p_dim_set.calendar.periodicity(i).aggregated='Y' then
2500         l_period:=bsc_aw_utility.get_parameter_value(p_dim_set.calendar.periodicity(i).property,'current period',',');
2501         g_stmt:='limit '||p_dim_set.calendar.periodicity(i).aw_dim||' to '''||l_period||'''';
2502         bsc_aw_dbms_aw.execute(g_stmt);
2503         g_stmt:='limit '||p_dim_set.calendar.aw_dim||' add '||p_dim_set.calendar.periodicity(i).aw_dim;
2504         bsc_aw_dbms_aw.execute(g_stmt);
2505       end if;
2506     end loop;
2507     /*
2508     when we had =0, aw gave error that we cannot change the values when looping across PT.
2509     does this mean that this is generating new values? so far no perf issues.=na is fine
2510     we cannot have cube(partition P)=na across PT. error : DATACUBE.1.4014 is not a command.
2511     found later that having=na is fine but it does not NA the values. so we have to leave the PT out
2512     --
2513     in 10.2, found that across PT does work. data is set to na, however, composite is not cleared. since we set =na only for the actuals
2514     cube, we shound be fine.
2515     */
2516     l_measures_to_limit.delete;
2517     for i in 1..p_measures.count loop
2518       if p_measures(i).forecast='Y' then
2519         l_measures_to_limit(l_measures_to_limit.count+1):=''''||p_measures(i).measure||'''';
2520         if bsc_aw_utility.in_array(l_cubes,p_measures(i).cube)=false then
2521           l_cubes(l_cubes.count+1):=p_measures(i).cube;
2522         end if;
2523       end if;
2524     end loop;
2525     if g_debug then
2526       dmp_dimset_dim_statlen(p_dim_set);
2527     end if;
2528     --N: if the dimset has multiple cubes, the cubes cannot share measures. so we can set the measurename_dim to all
2529     --measures
2530     /*when we are here, the composite cannot be compressed */
2531     push_dim(p_dim_set.measurename_dim);
2532     limit_dim(p_dim_set.measurename_dim,'NULL','TO');
2533     limit_dim(p_dim_set.measurename_dim,l_measures_to_limit,'ADD');
2534     for i in 1..l_cubes.count loop
2535       l_pt_comp:=bsc_aw_adapter_kpi.get_cube_pt_comp(l_cubes(i),p_dim_set,l_pt_comp_type);
2536       g_stmt:=l_cubes(i)||'=NA';
2537       --if l_pt_comp is not null and l_pt_comp_type='composite' then
2538       if l_pt_comp is not null then --for both composite and PT(10.2)(composite values are not cleared)
2539         g_stmt:=g_stmt||' across '||l_pt_comp;
2540       end if;
2541       bsc_aw_dbms_aw.execute(g_stmt);
2542     end loop;
2543     pop_dim(get_projection_dim(p_dim_set));
2544     pop_dim(p_dim_set.dim);
2545     pop_dim(p_dim_set.calendar.aw_dim);
2546     if l_partition_value is not null then
2547       pop_dim(p_dim_set.partition_dim);
2548     end if;
2549     pop_dim(p_dim_set.measurename_dim);
2550   end if;
2551 Exception when others then
2552   log_n('Exception in correct_forecast_aggregation '||sqlerrm);
2553   raise;
2554 End;
2555 
2556 /*
2557 this procedure gives the period in which there is a mix of forecast and real data
2558 we need the following
2559 kpi and periodicity : we use this to hit bsc_db_tables that will indicate the current period
2560 calendar : with this, we will get the current year.
2561 then we can say make the period
2562 */
2563 procedure get_forecast_current_period(
2564 p_aggregation in out nocopy aggregation_r) is
2565 --
2566 l_period varchar2(100);
2567 Begin
2568   for i in 1..p_aggregation.dim_set.count loop
2569     for j in 1..p_aggregation.dim_set(i).calendar.periodicity.count loop
2570       get_forecast_current_period(nvl(p_aggregation.parent_kpi,p_aggregation.kpi),p_aggregation.dim_set(i).calendar.calendar,
2571       p_aggregation.dim_set(i).calendar.periodicity(j).periodicity,l_period);
2572       p_aggregation.dim_set(i).calendar.periodicity(j).property:=p_aggregation.dim_set(i).calendar.periodicity(j).property||
2573       'current period='||l_period||',';
2574     end loop;
2575   end loop;
2576 Exception when others then
2577   log_n('Exception in get_forecast_current_period '||sqlerrm);
2578   raise;
2579 End;
2580 
2581 procedure get_forecast_current_period(
2582 p_kpi varchar2,
2583 p_calendar number,
2584 p_periodicity number,
2585 p_period out nocopy varchar2
2586 ) is
2587 Begin
2588   bsc_aw_bsc_metadata.get_forecast_current_period(p_kpi,p_calendar,p_periodicity,p_period);
2589 Exception when others then
2590   log_n('Exception in get_forecast_current_period '||sqlerrm);
2591   raise;
2592 End;
2593 
2594 /*
2595 this procedure is used for aggregatingbalance measures on time
2596 this limist teh time dim end period relation to the current period
2597 Q:do we need to set the proj values for missing periodicities to null? for now, lets do this, so the data is in sync
2598 across all periodicities of the kpi
2599 */
2600 procedure limit_calendar_end_period_rel(p_calendar bsc_aw_adapter_kpi.calendar_r) is
2601 --
2602 l_period_lowest varchar2(100); --current period of the lowest periodicity
2603 l_period varchar2(100);
2604 Begin
2605   if g_debug then
2606     log('--limit_calendar_end_period_rel------');
2607   end if;
2608   push_dim(p_calendar.aw_dim);
2609   push_dim(p_calendar.end_period_level_name_dim);
2610   g_stmt:='limit '||p_calendar.aw_dim||' to null';
2611   bsc_aw_dbms_aw.execute(g_stmt);
2612   --
2613   for i in 1..p_calendar.periodicity.count loop
2614     if p_calendar.periodicity(i).lowest_level='Y' then
2615       l_period_lowest:=bsc_aw_utility.get_parameter_value(p_calendar.periodicity(i).property,'current period',',');
2616       g_stmt:='limit '||p_calendar.end_period_level_name_dim||' TO '''||p_calendar.periodicity(i).aw_dim||'''';
2617       bsc_aw_dbms_aw.execute(g_stmt);
2618       --for all upper levels, back up original relation value
2619       for j in 1..p_calendar.periodicity.count loop
2620         if p_calendar.periodicity(j).lowest_level <> 'Y' then
2621           l_period:=bsc_aw_utility.get_parameter_value(p_calendar.periodicity(j).property,'current period',',');
2622           g_stmt:='limit '||p_calendar.periodicity(j).aw_dim||' to '''||l_period||'''';
2623           bsc_aw_dbms_aw.execute(g_stmt);
2624           g_stmt:='limit '||p_calendar.aw_dim||' to '||p_calendar.periodicity(j).aw_dim;
2625           bsc_aw_dbms_aw.execute(g_stmt);
2626           --set the rel.end_period relation
2627           --save the value for use in reset_calendar_end_period_rel
2628           g_stmt:=p_calendar.end_period_relation_name||'.temp = '||p_calendar.end_period_relation_name;
2629           bsc_aw_dbms_aw.execute(g_stmt);
2630           --we have to be careful here. imagine there is a hier like this
2631           --  M >- Q >- S >- Y
2632           --  W >- BiW >- Y
2633           --if we simply put the value of l_period_lowest for all upper levels, we may put month value into BiW. this is wrong.
2634           --month does not rollup to BiW.
2635           g_stmt:=p_calendar.end_period_relation_name||'= if '||p_calendar.end_period_relation_name||' EQ NA then NA else '||
2636           p_calendar.aw_dim||'('||p_calendar.periodicity(i).aw_dim||' '''||l_period_lowest||''')';
2637           bsc_aw_dbms_aw.execute(g_stmt);
2638         end if;
2639       end loop;
2640     end if;
2641   end loop;
2642   pop_dim(p_calendar.aw_dim);
2643   pop_dim(p_calendar.end_period_level_name_dim);
2644   if g_debug then
2645     log('----');
2646   end if;
2647 Exception when others then
2648   log_n('Exception in limit_calendar_end_period_rel '||sqlerrm);
2649   raise;
2650 End;
2651 
2652 /*
2653 this procedure restores the relation p_calendar.end_period_relation_name
2654 limit_calendar_end_period_rel has altered the relation. if we commit the changes, the relation is forever
2655 altered.
2656 another strategy is not to alter the relation. in this case, we first do the copy, then for each
2657 higher periodicity, we can copy based on the current period.
2658 This has the isue that the copy occurs several times. its more efficient to do  it once.
2659 resetting the relation back is easier. we need to store the values in global variables
2660 */
2661 procedure reset_calendar_end_period_rel(p_calendar bsc_aw_adapter_kpi.calendar_r) is
2662 --
2663 l_period varchar2(100);
2664 Begin
2665   if g_debug then
2666     log('--reset_calendar_end_period_rel------');
2667   end if;
2668   push_dim(p_calendar.aw_dim);
2669   push_dim(p_calendar.end_period_level_name_dim);
2670   --we limit level name dim to the lowest level of the kpi dimset
2671   g_stmt:='limit '||p_calendar.aw_dim||' to null';
2672   bsc_aw_dbms_aw.execute(g_stmt);
2673   for i in 1..p_calendar.periodicity.count loop
2674     if p_calendar.periodicity(i).lowest_level='Y' then
2675       g_stmt:='limit '||p_calendar.end_period_level_name_dim||' to '''||p_calendar.periodicity(i).aw_dim||'''';
2676       bsc_aw_dbms_aw.execute(g_stmt);
2677       for j in 1..p_calendar.periodicity.count loop
2678         if p_calendar.periodicity(j).lowest_level <> 'Y' then
2679           l_period:=bsc_aw_utility.get_parameter_value(p_calendar.periodicity(j).property,'current period',',');
2680           g_stmt:='limit '||p_calendar.periodicity(j).aw_dim||' to '''||l_period||'''';
2681           bsc_aw_dbms_aw.execute(g_stmt);
2682           g_stmt:='limit '||p_calendar.aw_dim||' to '||p_calendar.periodicity(j).aw_dim;
2683           bsc_aw_dbms_aw.execute(g_stmt);
2684           g_stmt:=p_calendar.end_period_relation_name||'='||p_calendar.end_period_relation_name||'.temp';
2685           bsc_aw_dbms_aw.execute(g_stmt);
2686         end if;
2687       end loop;
2688     end if;
2689   end loop;
2690   --
2691   pop_dim(p_calendar.aw_dim);
2692   pop_dim(p_calendar.end_period_level_name_dim);
2693   if g_debug then
2694     log('----');
2695   end if;
2696 Exception when others then
2697   log_n('Exception in reset_calendar_end_period_rel '||sqlerrm);
2698   raise;
2699 End;
2700 
2701 --limit a given dim to a given value
2702 procedure limit_dim(p_dim varchar2,p_value varchar2,p_mode varchar2) is
2703 Begin
2704   g_stmt:='limit '||p_dim||' '||p_mode||' '||p_value;
2705   bsc_aw_dbms_aw.execute(g_stmt);
2706 Exception when others then
2707   log_n('Exception in limit_dim '||sqlerrm);
2708   raise;
2709 End;
2710 
2711 --limit a given dim to a given set of value
2712 procedure limit_dim(p_dim varchar2,p_value dbms_sql.varchar2_table,p_mode varchar2) is
2713 Begin
2714   for i in 1..p_value.count loop
2715     limit_dim(p_dim,p_value(i),p_mode);
2716   end loop;
2717 Exception when others then
2718   log_n('Exception in limit_dim '||sqlerrm);
2719   raise;
2720 End;
2721 
2722 /*
2723 this procedures limits the dim values
2724 */
2725 procedure limit_dim_values(p_dim bsc_aw_adapter_kpi.dim_tb,p_mode varchar2) is
2726 Begin
2727   for i in 1..p_dim.count loop
2728     g_stmt:='limit '||p_dim(i).dim_name||' '||p_mode||' '||p_dim(i).limit_cube;
2729     bsc_aw_dbms_aw.execute(g_stmt);
2730   end loop;
2731 Exception when others then
2732   log_n('Exception in limit_dim_values '||sqlerrm);
2733   raise;
2734 End;
2735 
2736 /*
2737 given a adv sum profile value, this procedure limits the dim levels
2738 
2739 for actuals, the dim will have all the levels
2740 when called for targets, the dim's lowest level will be the level of the target, like state for example
2741 
2742 for rec dim, we limit the levels only in the case where the rec dim is implemented as denorm hier
2743 if implemented as normal hier, we aggregate to all levels. in this case, we have to aggregate all levels
2744 to reach the top node.
2745 */
2746 procedure limit_dim_levels(p_dim bsc_aw_adapter_kpi.dim_tb) is
2747 Begin
2748   for i in 1..p_dim.count loop
2749     if p_dim(i).recursive<>'Y' then
2750       limit_dim_levels(p_dim(i));
2751     end if;
2752   end loop;
2753   --now, set the levels for the rec dim
2754   for i in 1..p_dim.count loop
2755     if p_dim(i).recursive='Y' and p_dim(i).recursive_norm_hier='N' then
2756       limit_dim_levels(p_dim(i));
2757     end if;
2758   end loop;
2759 Exception when others then
2760   log_n('Exception in limit_dim_levels '||sqlerrm);
2761   raise;
2762 End;
2763 
2764 /*
2765 the algo:
2766 start from the lowest level. limit level name dim to all parent.child and recursively go up
2767 as long as the level is a part of the dim
2768 now, in bsc_aw_md_api, when we load the parent child relation for both dim and calendar,
2769 we only bring in the parent child that belong to the kpi
2770 */
2771 procedure limit_dim_levels(p_dim bsc_aw_adapter_kpi.dim_r) is
2772 Begin
2773   if p_dim.recursive='N' then
2774     g_stmt:='limit '||p_dim.level_name_dim||' to null';
2775     bsc_aw_dbms_aw.execute(g_stmt);
2776     --we start the process with the lowest level. this procedure is called rec for the parents
2777     limit_dim_levels(p_dim,p_dim.levels(1).level_name);
2778   else --now, set the levels for the rec dim
2779     --p_dim.level_name_dim||'.position is the variable with the position for each rec dim value, larry=1, john wookey=2
2780     /*this api is only called when the rec dim is implemented in denorm fashion */
2781     g_stmt:='limit '||p_dim.level_name_dim||' to '||p_dim.level_name_dim||'.position LE '||p_dim.agg_level;
2782     bsc_aw_dbms_aw.execute(g_stmt);
2783   end if;
2784 Exception when others then
2785   log_n('Exception in limit_dim_levels '||sqlerrm);
2786   raise;
2787 End;
2788 
2789 /*
2790 this procedure fires recursively
2791 p_level is the child level
2792 parent_child will onky have the parent child belonging to the kpi
2793 */
2794 procedure limit_dim_levels(p_dim bsc_aw_adapter_kpi.dim_r,p_level varchar2) is
2795 l_this_level bsc_aw_adapter_kpi.level_r;
2796 l_parent_level bsc_aw_adapter_kpi.level_r;
2797 l_flag bsc_aw_utility.boolean_table;
2798 Begin
2799   --first the zero code
2800   l_this_level:=bsc_aw_adapter_kpi.get_dim_level_r(p_dim,p_level);
2801   if l_this_level.aggregated='Y' then
2802     if l_this_level.zero_code='Y' and l_this_level.zero_aggregated='Y' then
2803       g_stmt:='limit '||p_dim.level_name_dim||' add '''||l_this_level.zero_code_level||'.'||
2804       l_this_level.level_name||'''';
2805       bsc_aw_dbms_aw.execute(g_stmt);
2806     end if;
2807   end if;
2808   for i in 1..p_dim.parent_child.count loop
2809     l_flag(i):=false;
2810     if p_dim.parent_child(i).child_level=p_level and p_dim.parent_child(i).parent_level is not null then
2811       l_parent_level:=bsc_aw_adapter_kpi.get_dim_level_r(p_dim,p_dim.parent_child(i).parent_level);
2812       if l_parent_level.aggregated='Y' then
2813         l_flag(i):=true;
2814         g_stmt:='limit '||p_dim.level_name_dim||' add '''||p_dim.parent_child(i).parent_level||'.'||
2815         p_dim.parent_child(i).child_level||'''';
2816         bsc_aw_dbms_aw.execute(g_stmt);
2817       end if;
2818     end if;
2819   end loop;
2820   --rec do this for the parent levels
2821   for i in 1..p_dim.parent_child.count loop
2822     if l_flag(i) then
2823       limit_dim_levels(p_dim,p_dim.parent_child(i).parent_level);
2824     end if;
2825   end loop;
2826 Exception when others then
2827   log_n('Exception in limit_dim_levels '||sqlerrm);
2828   raise;
2829 End;
2830 
2831 /*
2832 limits the dim values to only the upper levels.
2833 p_operator is "to" or "add"
2834 */
2835 procedure limit_dim_ancestors(p_dim bsc_aw_adapter_kpi.dim_tb,p_operator varchar2) is
2836 Begin
2837   --we cannot use if p_dim(i).levels.count> 1 and p_dim(i).relation_name is not null then
2838   --because rec dim only have 1 level. single level dim do not have relation_name
2839   --we use an api is_aggregation_on_dim that says if there is agg on a dim or not for this dimset
2840   --i think we should not use is_aggregation_on_dim here. if we are using only 1 level of a dim here,
2841   --we would have limited the level_name_dim before.
2842   for i in 1..p_dim.count loop
2843     if p_dim(i).relation_name is not null then
2844       g_stmt:='limit '||p_dim(i).dim_name||' '||p_operator||' ancestors using '||p_dim(i).relation_name;
2845       bsc_aw_dbms_aw.execute(g_stmt);
2846     end if;
2847   end loop;
2848 Exception when others then
2849   log_n('Exception in limit_dim_ancestors '||sqlerrm);
2850   raise;
2851 End;
2852 
2853 procedure limit_dim_descendents(p_dim bsc_aw_adapter_kpi.dim_tb,p_operator varchar2,p_depth varchar2) is
2854 Begin
2855   for i in 1..p_dim.count loop
2856     if p_dim(i).relation_name is not null then
2857       g_stmt:='limit '||p_dim(i).dim_name||' '||p_operator||' '||p_depth||' using '||p_dim(i).relation_name;
2858       bsc_aw_dbms_aw.execute(g_stmt);
2859     end if;
2860   end loop;
2861 Exception when others then
2862   log_n('Exception in limit_dim_descendents '||sqlerrm);
2863   raise;
2864 End;
2865 
2866 /*
2867 after aggregation, we reset the limit cubes. at this time, when the api is called,
2868 the dim is limited to those values where the limit cube was TRUE
2869 */
2870 procedure reset_dim_limit_cubes(p_dim bsc_aw_adapter_kpi.dim_tb) is
2871 Begin
2872   for i in 1..p_dim.count loop
2873     g_stmt:=p_dim(i).limit_cube||'=FALSE';
2874     if p_dim(i).limit_cube_composite is not null then
2875       g_stmt:=g_stmt||' across '||p_dim(i).limit_cube_composite;
2876     end if;
2877     bsc_aw_dbms_aw.execute(g_stmt);
2878   end loop;
2879 Exception when others then
2880   log_n('Exception in reset_dim_limit_cubes '||sqlerrm);
2881   raise;
2882 End;
2883 
2884 /*
2885 limits the time values
2886 */
2887 procedure limit_calendar_values(p_calendar bsc_aw_adapter_kpi.calendar_r,p_mode varchar2) is
2888 Begin
2889   g_stmt:='limit '||p_calendar.aw_dim||' '||p_mode||' '||p_calendar.limit_cube;
2890   bsc_aw_dbms_aw.execute(g_stmt);
2891 Exception when others then
2892   log_n('Exception in limit_calendar_values '||sqlerrm);
2893   raise;
2894 End;
2895 
2896 /*
2897 this procedure limits the periodities to the ones applicable to the dim set for agg
2898 */
2899 procedure limit_calendar_levels(p_calendar bsc_aw_adapter_kpi.calendar_r) is
2900 Begin
2901   g_stmt:='limit '||p_calendar.level_name_dim||' to null';
2902   bsc_aw_dbms_aw.execute(g_stmt);
2903   --start the process. this procedure is called  rec for parent levels
2904   --start from the child and go all the way up
2905   for i in 1..p_calendar.periodicity.count loop
2906     if p_calendar.periodicity(i).lowest_level='Y' then
2907       limit_calendar_levels(p_calendar,p_calendar.periodicity(i).aw_dim);
2908     end if;
2909   end loop;
2910 Exception when others then
2911   log_n('Exception in limit_calendar_levels '||sqlerrm);
2912   raise;
2913 End;
2914 
2915 /*
2916 this procedure is called rec for the parent levels
2917 */
2918 procedure limit_calendar_levels(
2919 p_calendar bsc_aw_adapter_kpi.calendar_r,
2920 p_periodicity_dim varchar2) is
2921 --
2922 l_flag bsc_aw_utility.boolean_table;
2923 l_periodicity bsc_aw_adapter_kpi.periodicity_r;
2924 Begin
2925   l_periodicity:=bsc_aw_adapter_kpi.get_periodicity_r(p_calendar.periodicity,p_periodicity_dim);
2926   if l_periodicity.aggregated='Y' then
2927     for i in 1..p_calendar.parent_child.count loop
2928       l_flag(i):=false;
2929       if p_calendar.parent_child(i).child_dim_name=p_periodicity_dim and p_calendar.parent_child(i).parent_dim_name is not null then
2930         l_periodicity:=bsc_aw_adapter_kpi.get_periodicity_r(p_calendar.periodicity,p_calendar.parent_child(i).parent_dim_name);
2931         if l_periodicity.aggregated='Y' then
2932           l_flag(i):=true;
2933           g_stmt:='limit '||p_calendar.level_name_dim||' add '''||p_calendar.parent_child(i).parent_dim_name||'.'||
2934           p_calendar.parent_child(i).child_dim_name||'''';
2935           bsc_aw_dbms_aw.execute(g_stmt);
2936         end if;
2937       end if;
2938     end loop;
2939     for i in 1..p_calendar.parent_child.count loop
2940       if l_flag(i) then
2941         limit_calendar_levels(p_calendar,p_calendar.parent_child(i).parent_dim_name);
2942       end if;
2943     end loop;
2944   end if;
2945 Exception when others then
2946   log_n('Exception in limit_calendar_levels '||sqlerrm);
2947   raise;
2948 End;
2949 
2950 --limit the calendar vlues to the parents
2951 --p_operator is "to" or "add"
2952 procedure limit_calendar_ancestors(p_calendar bsc_aw_adapter_kpi.calendar_r,p_operator varchar2) is
2953 Begin
2954   g_stmt:='limit '||p_calendar.aw_dim||' '||p_operator||' ancestors using '||p_calendar.relation_name;
2955   bsc_aw_dbms_aw.execute(g_stmt);
2956 Exception when others then
2957   log_n('Exception in limit_calendar_ancestors '||sqlerrm);
2958   raise;
2959 End;
2960 
2961 procedure limit_calendar_descendents(p_calendar bsc_aw_adapter_kpi.calendar_r,p_operator varchar2,p_depth varchar2) is
2962 Begin
2963   g_stmt:='limit '||p_calendar.aw_dim||' '||p_operator||' '||p_depth||' using '||p_calendar.relation_name;
2964   bsc_aw_dbms_aw.execute(g_stmt);
2965 Exception when others then
2966   log_n('Exception in limit_calendar_descendents '||sqlerrm);
2967   raise;
2968 End;
2969 
2970 /*
2971 reset the calendar limit cube after aggregation. at this time calendar is limited to those
2972 values where the limit cube was TRUE
2973 */
2974 procedure reset_calendar_limit_cubes(p_calendar bsc_aw_adapter_kpi.calendar_r) is
2975 Begin
2976   g_stmt:=p_calendar.limit_cube||'=FALSE';
2977   if p_calendar.limit_cube_composite is not null then
2978     g_stmt:=g_stmt||' across '||p_calendar.limit_cube_composite;
2979   end if;
2980   bsc_aw_dbms_aw.execute(g_stmt);
2981 Exception when others then
2982   log_n('Exception in reset_calendar_limit_cubes '||sqlerrm);
2983   raise;
2984 End;
2985 
2986 procedure set_aggregation(p_kpi varchar2,p_aggregation out nocopy aggregation_r) is
2987 l_cache_aggregation_r aggregation_r;
2988 Begin
2989   p_aggregation:=null;
2990   p_aggregation.kpi:=p_kpi;
2991   l_cache_aggregation_r:=get_cache_aggregation_r(p_kpi);
2992   if l_cache_aggregation_r.kpi is null then
2993     bsc_aw_md_api.get_aggregation_r(p_aggregation);
2994     get_forecast_current_period(p_aggregation);
2995     dmp_aggregation_r(p_aggregation);
2996     g_cache_aggregation_r(g_cache_aggregation_r.count+1):=p_aggregation;
2997   else
2998     p_aggregation:=l_cache_aggregation_r;
2999   end if;
3000 Exception when others then
3001   log_n('Exception in set_aggregation '||sqlerrm);
3002   raise;
3003 End;
3004 
3005 procedure dmp_aggregation_r(p_aggregation aggregation_r) is
3006 Begin
3007   log_n('Dmp Aggregation:- KPI='||p_aggregation.kpi);
3008   for i in 1..p_aggregation.dim_set.count loop
3009     bsc_aw_adapter_kpi.dmp_dimset(p_aggregation.dim_set(i));
3010   end loop;
3011 Exception when others then
3012   log_n('Exception in dmp_aggregation_r '||sqlerrm);
3013   raise;
3014 End;
3015 
3016 procedure push_dim(p_dim bsc_aw_adapter_kpi.dim_tb) is
3017 Begin
3018   for i in 1..p_dim.count loop
3019     push_dim(p_dim(i).dim_name);
3020   end loop;
3021 Exception when others then
3022   log_n('Exception in push_dim '||sqlerrm);
3023   raise;
3024 End;
3025 
3026 procedure push_dim(p_dim varchar2) is
3027 Begin
3028   bsc_aw_dbms_aw.execute('push '||p_dim);
3029 Exception when others then
3030   log_n('Exception in push_dim '||sqlerrm);
3031   raise;
3032 End;
3033 
3034 procedure pop_dim(p_dim bsc_aw_adapter_kpi.dim_tb) is
3035 Begin
3036   for i in 1..p_dim.count loop
3037     pop_dim(p_dim(i).dim_name);
3038   end loop;
3039 Exception when others then
3040   log_n('Exception in pop_dim '||sqlerrm);
3041   raise;
3042 End;
3043 
3044 procedure pop_dim(p_dim varchar2) is
3045 Begin
3046   bsc_aw_dbms_aw.execute('pop '||p_dim);
3047 Exception when others then
3048   log_n('Exception in pop_dim '||sqlerrm);
3049   raise;
3050 End;
3051 
3052 /*
3053 during aggregation, we set the status of various dimensions. we want to be able to restore the
3054 status of the dim back to the level before the agg. otherwise it becomes hard to track the dim
3055 status from api to api
3056 
3057 in push level, we must use context. the reason is that pushlevel command in aw can be used only within programs
3058 " You can use PUSHLEVEL only within programs"
3059 
3060 For now, not used.
3061 */
3062 procedure push_level(p_marker varchar2) is
3063 Begin
3064   null;
3065 Exception when others then
3066   log_n('Exception in push_level '||sqlerrm);
3067   raise;
3068 End;
3069 
3070 procedure pop_level(p_marker varchar2) is
3071 Begin
3072   null;
3073 Exception when others then
3074   log_n('Exception in pop_level '||sqlerrm);
3075   raise;
3076 End;
3077 
3078 /*
3079 this procedure purges the kpi. i
3080 */
3081 procedure purge_kpi(p_kpi varchar2) is
3082 --
3083 l_bsc_olap_object bsc_aw_md_wrapper.bsc_olap_object_tb;
3084 Begin
3085   if g_debug then
3086     log_n('Purge KPI '||p_kpi);
3087   end if;
3088   --for purge, we need exclusive lock. else we cannot delete composites
3089   bsc_aw_management.get_workspace_lock('rw',null);
3090   bsc_aw_md_api.get_bsc_olap_object(null,null,p_kpi,'kpi',l_bsc_olap_object);
3091   --clear the cubes
3092   for i in 1..l_bsc_olap_object.count loop
3093     if l_bsc_olap_object(i).object_type='data cube' then
3094       bsc_aw_dbms_aw.execute('clear all from '||l_bsc_olap_object(i).olap_object);
3095     end if;
3096   end loop;
3097   --clear fcst cubes if there are any
3098   --clear the limit cubes
3099   for i in 1..l_bsc_olap_object.count loop
3100     if l_bsc_olap_object(i).object_type='dim limit cube' then
3101       bsc_aw_dbms_aw.execute('clear all from '||l_bsc_olap_object(i).olap_object);
3102     end if;
3103   end loop;
3104   for i in 1..l_bsc_olap_object.count loop
3105     if l_bsc_olap_object(i).object_type='limit cube composite' then
3106       bsc_aw_dbms_aw.execute('maintain '||l_bsc_olap_object(i).olap_object||' delete all');
3107     end if;
3108   end loop;
3109   --clear the countvar cubes
3110   for i in 1..l_bsc_olap_object.count loop
3111     if l_bsc_olap_object(i).object_type='countvar cube' then
3112       bsc_aw_dbms_aw.execute('clear all from '||l_bsc_olap_object(i).olap_object);
3113     end if;
3114   end loop;
3115   --clear the composites
3116   for i in 1..l_bsc_olap_object.count loop
3117     if l_bsc_olap_object(i).object_type='measure composite' then
3118       bsc_aw_dbms_aw.execute('maintain '||l_bsc_olap_object(i).olap_object||' delete all');
3119     end if;
3120   end loop;
3121   --set the dimset current change vector to 0 for all dimsets of the kpi
3122   reset_dimset_change_vector(p_kpi);
3123   --
3124   bsc_aw_management.commit_aw;
3125   commit;
3126 Exception when others then
3127   log_n('Exception in purge_kpi '||sqlerrm);
3128   raise;
3129 End;
3130 
3131 procedure get_dimset_objects(p_kpi varchar2,p_dim_set varchar2,p_oo out nocopy bsc_aw_md_wrapper.bsc_olap_object_tb) is
3132 l_oo_kpi bsc_aw_md_wrapper.bsc_olap_object_tb;
3133 Begin
3134   bsc_aw_md_api.get_bsc_olap_object(null,null,p_kpi,'kpi',l_oo_kpi);
3135   for i in 1..l_oo_kpi.count loop
3136     if bsc_aw_utility.get_parameter_value(l_oo_kpi(i).property1,'dim set name',',')=p_dim_set then
3137       p_oo(p_oo.count+1):=l_oo_kpi(i);
3138     end if;
3139   end loop;
3140 Exception when others then
3141   log_n('Exception in get_dimset_objects '||sqlerrm);
3142   raise;
3143 End;
3144 
3145 /*
3146 this will set the current change vector to 0. called from purge_kpi
3147 */
3148 procedure reset_dimset_change_vector(p_kpi varchar2) is
3149 l_olap_object_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3150 l_property varchar2(4000);
3151 Begin
3152   bsc_aw_md_api.get_bsc_olap_object_relation(null,null,'base table dim set',p_kpi,'kpi',l_olap_object_relation);
3153   for i in 1..l_olap_object_relation.count loop
3154     --we must not lose the measures=m1,m2, from the property of base table dim set after we reset the change vector
3155     l_property:=l_olap_object_relation(i).property1;
3156     bsc_aw_utility.update_property(l_property,'current change vector','0',',');
3157     bsc_aw_md_api.update_olap_object_relation(l_olap_object_relation(i).object,l_olap_object_relation(i).object_type,
3158     l_olap_object_relation(i).relation_type,l_olap_object_relation(i).parent_object,l_olap_object_relation(i).parent_object_type,
3159     'relation_object,relation_object_type',l_olap_object_relation(i).relation_object||','||l_olap_object_relation(i).relation_object_type,
3160     'property1',l_property);
3161   end loop;
3162 Exception when others then
3163   log_n('Exception in reset_dimset_change_vector '||sqlerrm);
3164   raise;
3165 End;
3166 
3167 --limit the measuredim to the measures that we are going to aggregate
3168 procedure limit_measure_dim(
3169 p_aggmap_operator bsc_aw_adapter_kpi.aggmap_operator_r,
3170 p_cubes dbms_sql.varchar2_table,
3171 p_partition_value varchar2
3172 ) is
3173 Begin
3174   bsc_aw_dbms_aw.execute('limit '||p_aggmap_operator.measure_dim||' to NULL');
3175   for i in 1..p_cubes.count loop
3176     if p_partition_value is not null then
3177       bsc_aw_dbms_aw.execute('limit '||p_aggmap_operator.measure_dim||' add '''||p_cubes(i)||' (PARTITION '||p_partition_value||')''');
3178     else
3179       bsc_aw_dbms_aw.execute('limit '||p_aggmap_operator.measure_dim||' add '''||p_cubes(i)||'''');
3180     end if;
3181   end loop;
3182 Exception when others then
3183   log_n('Exception in limit_measure_dim '||sqlerrm);
3184   raise;
3185 End;
3186 
3187 procedure reset_dim_limits(p_dim_set bsc_aw_adapter_kpi.dim_set_r) is
3188 Begin
3189   --now, we reset the limit cubes
3190   --in 10g, we call these 2 api to after all the threads have completed
3191   --set status to allstat in reset_dim_limits. in 10g parallelism, the dim status here is not what we need.
3192   --so best to set the status to allstat
3193   bsc_aw_dbms_aw.execute('allstat');
3194   reset_dim_limit_cubes(p_dim_set.dim);
3195   reset_dim_limit_cubes(p_dim_set.std_dim);
3196   reset_calendar_limit_cubes(p_dim_set.calendar);
3197 Exception when others then
3198   log_n('Exception in reset_dim_limits '||sqlerrm);
3199   raise;
3200 End;
3201 
3202 /*
3203 this procedure limits the dim to the lowest level of targets. say target is at state level.
3204 say geog is at city, state and vountry level. when this procedure executes, geog is
3205 limited to "KEEP" state
3206 p_actual_dim_set and p_target_dim_set will be related actual and target dim sets
3207 */
3208 procedure limit_dim_target_level_only(
3209 p_actual_dim_set bsc_aw_adapter_kpi.dim_set_r,
3210 p_target_dim_set bsc_aw_adapter_kpi.dim_set_r
3211 ) is
3212 Begin
3213   for i in 1..p_actual_dim_set.dim.count loop
3214     for j in 1..p_target_dim_set.dim.count loop
3215       if p_target_dim_set.dim(j).dim_name=p_actual_dim_set.dim(i).dim_name then
3216         /*
3217         This is critical...if p_actual_dim_set.dim(i).levels.count>1 then
3218         encountered the issue when we did the change for handling hanging parent values. ie initial hier:
3219           A     C       changing to    A     C
3220           a    b  d                         a b d
3221         in this case the value for A in the cubes must be reset to 0. during this time,it was found that when we limit
3222         a concat dim to its level, when the concat had only 1 level, limited the value of the dim to the first value in
3223         the status. release dim had values 0,1,2 in status. then "KEEP" made the value of the concat dim just 0. this meant
3224         that the copy from the target to the actual did not happen and the values in the cube for targets were nulled out.
3225         they get nulled out before this during the actual agg. the copy is supposed to restore the value and then the
3226         subsequent agg will correct the higher level rollup for targets. if the copy does not happen, data is messed up for targets
3227         we keep the level if this is a normal dom with more than 1 level or if this is a rec dim implemented with denorm hier
3228         */
3229         if p_actual_dim_set.dim(i).levels.count>1 or
3230         (p_actual_dim_set.dim(i).recursive='Y' and p_actual_dim_set.dim(i).recursive_norm_hier='N') then
3231           limit_dim(p_actual_dim_set.dim(i).dim_name,p_target_dim_set.dim(j).levels(1).level_name,'KEEP');
3232         end if;
3233         exit;
3234       end if;
3235     end loop;
3236   end loop;
3237 Exception when others then
3238   log_n('Exception in limit_dim_target_level_only '||sqlerrm);
3239   raise;
3240 End;
3241 
3242 procedure limit_cal_target_level_only(
3243 p_actual_dim_set bsc_aw_adapter_kpi.dim_set_r,
3244 p_target_dim_set bsc_aw_adapter_kpi.dim_set_r
3245 ) is
3246 Begin
3247   --calendar
3248   --remove all levels that are not the lowest level
3249   for i in 1..p_target_dim_set.calendar.periodicity.count loop
3250     if p_target_dim_set.calendar.periodicity(i).lowest_level='Y' then
3251       limit_dim(p_target_dim_set.calendar.periodicity(i).aw_dim,p_actual_dim_set.calendar.aw_dim,'TO');
3252     end if;
3253   end loop;
3254   limit_dim(p_actual_dim_set.calendar.aw_dim,'NULL','TO');
3255   for i in 1..p_target_dim_set.calendar.periodicity.count loop
3256     if p_target_dim_set.calendar.periodicity(i).lowest_level='Y' then
3257       limit_dim(p_actual_dim_set.calendar.aw_dim,p_target_dim_set.calendar.periodicity(i).aw_dim,'ADD');
3258     end if;
3259   end loop;
3260 Exception when others then
3261   log_n('Exception in limit_cal_target_level_only '||sqlerrm);
3262   raise;
3263 End;
3264 
3265 /*
3266 this procedure sets the limit cubes of dimensions to the value , if a composite is specified, it loops
3267 across it. used in aggregate_kpi_dimset_targets
3268 --
3269 earlier, we passed p_composite_name varchar2 as a parameter. this used to be p_target_dim_set.measure(1).composite_name
3270 but, each limit cube has its own composite. so its best to limit the limit cube to its own composite.
3271 9i and 10g, both have limit cube composite. N: limit cube composite has values only for the level at which data
3272 is loaded. but this is ok since we do not aggregate target cubes
3273 */
3274 procedure limit_dim_limit_cube(
3275 p_dim bsc_aw_adapter_kpi.dim_tb,
3276 p_value varchar2
3277 ) is
3278 Begin
3279   for i in 1..p_dim.count loop
3280     limit_dim_limit_cube(p_dim(i).limit_cube,p_value,p_dim(i).limit_cube_composite);
3281   end loop;
3282 Exception when others then
3283   log_n('Exception in limit_dim_limit_cube '||sqlerrm);
3284   raise;
3285 End;
3286 
3287 --have to specify p_composite_name. limit cubes use named composites
3288 procedure limit_dim_limit_cube(
3289 p_limit_cube varchar2,
3290 p_value varchar2,
3291 p_composite_name varchar2
3292 ) is
3293 Begin
3294   g_stmt:=p_limit_cube||'='||p_value;
3295   if p_composite_name is not null then
3296     g_stmt:=g_stmt||' across '||p_composite_name;
3297   end if;
3298   bsc_aw_dbms_aw.execute(g_stmt);
3299 Exception when others then
3300   log_n('Exception in limit_dim_limit_cube '||sqlerrm);
3301   raise;
3302 End;
3303 
3304 function get_projection_dim(p_dim_set bsc_aw_adapter_kpi.dim_set_r) return varchar2 is
3305 Begin
3306   return bsc_aw_adapter_kpi.get_projection_dim(p_dim_set);
3307 Exception when others then
3308   log_n('Exception in get_projection_dim '||sqlerrm);
3309   raise;
3310 End;
3311 
3312 /*
3313 this function sees if agg is implemented on a dim. if this is a non rec dim and there is only 1 level and no zero code,
3314 there is no agg on it
3315 */
3316 function is_aggregation_on_dim(p_dim bsc_aw_adapter_kpi.dim_r) return boolean is
3317 Begin
3318   if p_dim.recursive='N' and p_dim.levels.count=1 then
3319     if p_dim.levels(1).zero_code='Y' then
3320       return true;
3321     else
3322       return false;
3323     end if;
3324   else
3325     return true;
3326   end if;
3327 Exception when others then
3328   log_n('Exception in is_aggregation_on_dim '||sqlerrm);
3329   raise;
3330 End;
3331 
3332 /*
3333 this procedure sees if there is agg on calendar.
3334 looks to see if there is more than 1 periodicity
3335 */
3336 function is_aggregation_on_time(p_calendar bsc_aw_adapter_kpi.calendar_r) return boolean is
3337 Begin
3338   if p_calendar.periodicity.count>1 then
3339     return true;
3340   else
3341     return false;
3342   end if;
3343 Exception when others then
3344   log_n('Exception in is_aggregation_on_time '||sqlerrm);
3345   raise;
3346 End;
3347 
3348 /*
3349 given a dim table and a dim, returns the index of where the dim was found
3350 */
3351 function get_dim_index(
3352 p_dim bsc_aw_adapter_kpi.dim_tb,
3353 p_dim_name varchar2
3354 )return number is
3355 Begin
3356   for i in 1..p_dim.count loop
3357     if p_dim(i).dim_name=p_dim_name then
3358       return i;
3359     end if;
3360   end loop;
3361   return null;
3362 Exception when others then
3363   log_n('Exception in get_dim_index '||sqlerrm);
3364   raise;
3365 End;
3366 
3367 /*
3368 given a table of measure and a measure name, returns the index where it occured
3369 */
3370 function get_measure_index(
3371 p_measure bsc_aw_adapter_kpi.measure_tb,
3372 p_measure_name varchar2
3373 )return number is
3374 Begin
3375   for i in 1..p_measure.count loop
3376     if p_measure(i).measure=p_measure_name then
3377       return i;
3378     end if;
3379   end loop;
3380   return null;
3381 Exception when others then
3382   log_n('Exception in get_measure_index '||sqlerrm);
3383   raise;
3384 End;
3385 
3386 /*
3387 when we use compressed composites, we cannot limit the dim values that make up the composite.
3388 we can limit the levels of the relation. its important to do so since we can have a kpi at the month level. without limiting
3389 the levels, aggregation will start from day. this means aggregate values will all be null
3390 */
3391 procedure limit_all_dim(p_dim_set bsc_aw_adapter_kpi.dim_set_r) is
3392 Begin
3393   if p_dim_set.compressed='N' then
3394     limit_dim_values(p_dim_set.dim,'to');
3395     limit_dim_values(p_dim_set.std_dim,'to');
3396     limit_calendar_values(p_dim_set.calendar,'to');
3397   end if;
3398   limit_dim_levels(p_dim_set.dim); --l_aggregation.dim has the agg_level in it
3399   limit_calendar_levels(p_dim_set.calendar);--limit the periodicities
3400 Exception when others then
3401   log_n('Exception in limit_all_dim '||sqlerrm);
3402   raise;
3403 End;
3404 
3405 /*
3406 calendar is getting special treatment compared to other dim. we do not load other dim when we load kpi.
3407 we need to handle the case where a user has already bsc implemented. they make no changes to their calendars. in this case,
3408 we need to create and load the calendars. its best if we load the calendar here
3409 */
3410 procedure load_calendar_if_needed(p_kpi varchar2) is
3411 --
3412 l_olap_object_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3413 l_calendar number;
3414 l_lock_name varchar2(40);
3415 Begin
3416   bsc_aw_md_api.get_bsc_olap_object_relation(null,null,null,p_kpi,'kpi',l_olap_object_relation);
3417   for i in 1..l_olap_object_relation.count loop
3418     if l_olap_object_relation(i).relation_type='dim set calendar' then
3419       l_calendar:=to_number(bsc_aw_utility.get_parameter_value(l_olap_object_relation(i).property1,'calendar',','));
3420       exit;
3421     end if;
3422   end loop;
3423   l_lock_name:='lock_aw_calendar_'||l_calendar;
3424   if l_calendar is not null then
3425     --serialize access
3426     bsc_aw_utility.get_db_lock(l_lock_name);
3427     if bsc_aw_calendar.check_calendar_loaded(l_calendar)='N' then
3428       --get lock for calendar
3429       bsc_aw_calendar.lock_calendar_objects(l_calendar);
3430       bsc_aw_calendar.load_calendar(l_calendar);
3431       bsc_aw_management.commit_aw;
3432       commit;
3433     end if;
3434     bsc_aw_utility.release_db_lock(l_lock_name);
3435   else
3436     log_n('Could not locate kpi calendar in load_calendar_if_needed');
3437   end if;
3438 Exception when others then
3439   bsc_aw_utility.release_db_lock(l_lock_name);
3440   log_n('Exception in load_calendar_if_needed '||sqlerrm);
3441   raise;
3442 End;
3443 
3444 /*enh needed to load dim on demand. two scenarios. aw kpi created fresh. aw dim are empty. case II view based dim.
3445 */
3446 procedure load_dim_if_needed(p_kpi varchar2,p_dim_set dbms_sql.varchar2_table) is
3447 l_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3448 l_dim dbms_sql.varchar2_table;
3449 Begin
3450   bsc_aw_md_api.get_bsc_olap_object_relation(null,null,null,p_kpi,'kpi',l_oor);
3451   for i in 1..l_oor.count loop
3452     if l_oor(i).object_type='kpi dimension set' and (l_oor(i).relation_type='dim set dim' or l_oor(i).relation_type='dim set std dim')
3453     and l_oor(i).relation_object_type='dimension' then
3454       if bsc_aw_utility.in_array(p_dim_set,l_oor(i).object) then
3455         bsc_aw_utility.merge_value(l_dim,l_oor(i).relation_object);
3456       end if;
3457     end if;
3458   end loop;
3459   --
3460   bsc_aw_load_dim.load_dim_if_needed(l_dim);
3461   --
3462 Exception when others then
3463   log_n('Exception in load_dim_if_needed '||sqlerrm);
3464   raise;
3465 End;
3466 
3467 procedure dmp_kpi_cubes_into_table(
3468 p_kpi varchar2,
3469 p_dimset varchar2,
3470 p_dim_levels dbms_sql.varchar2_table,
3471 p_table_name varchar2) is
3472 --
3473 l_name varchar2(200);
3474 Begin
3475   l_name:='dmp_kpi_'||p_kpi||'_'||p_dimset;
3476   bsc_aw_dbms_aw.execute_ne('delete '||l_name);
3477   bsc_aw_adapter_kpi.create_dmp_program(p_kpi,p_dimset,p_dim_levels,l_name,p_table_name);
3478   bsc_aw_dbms_aw.execute('call '||l_name);
3479   bsc_aw_dbms_aw.execute('delete '||l_name);
3480 Exception when others then
3481   log_n('Exception in dmp_kpi_cubes_into_table '||sqlerrm);
3482   raise;
3483 End;
3484 
3485 /*
3486 pass a kpi. this will loop over all dimset, all dim and levels. it eill create tables
3487 as p_table_name||dimset||1,2 etc. then these table names will be returned in p_tables
3488 */
3489 procedure dmp_kpi_cubes_into_table(
3490 p_kpi varchar2,
3491 p_table_name varchar2,
3492 p_tables out nocopy dbms_sql.varchar2_table
3493 ) is
3494 --
3495 l_oo_dimset bsc_aw_md_wrapper.bsc_olap_object_tb;
3496 l_oo_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3497 l_dimset varchar2(100);
3498 l_dimset_name varchar2(200);
3499 l_combinations dbms_sql.varchar2_table;
3500 l_combinations_copy dbms_sql.varchar2_table;
3501 l_dimensions dbms_sql.varchar2_table;
3502 l_levels dbms_sql.varchar2_table;
3503 l_values bsc_aw_utility.value_tb;
3504 l_count number;
3505 Begin
3506   bsc_aw_md_api.get_kpi_dimset_actual(p_kpi,l_oo_dimset);
3507   --loop across the dimset
3508   for i in 1..l_oo_dimset.count loop
3509     l_dimset:=bsc_aw_utility.get_parameter_value(l_oo_dimset(i).property1,'dim set',',');
3510     l_dimset_name:=l_oo_dimset(i).object;
3511     l_count:=0;
3512     --get the dim
3513     l_combinations.delete;
3514     l_dimensions.delete;
3515     --
3516     l_oo_relation.delete;
3517     bsc_aw_md_api.get_bsc_olap_object_relation(l_dimset_name,'kpi dimension set','dim set dim',p_kpi,'kpi',l_oo_relation);
3518     for j in 1..l_oo_relation.count loop
3519       l_dimensions(l_dimensions.count+1):=l_oo_relation(j).relation_object;
3520     end loop;
3521     l_oo_relation.delete;
3522     bsc_aw_md_api.get_bsc_olap_object_relation(l_dimset_name,'kpi dimension set','dim set std dim',p_kpi,'kpi',l_oo_relation);
3523     for j in 1..l_oo_relation.count loop
3524       l_dimensions(l_dimensions.count+1):=l_oo_relation(j).relation_object;
3525     end loop;
3526     l_oo_relation.delete;
3527     bsc_aw_md_api.get_bsc_olap_object_relation(null,null,'dim set dim level',p_kpi,'kpi',l_oo_relation);
3528     --loop over each dim
3529     for j in 1..l_dimensions.count loop
3530       l_levels.delete;
3531       for k in 1..l_oo_relation.count loop
3532         if l_oo_relation(k).object=l_dimensions(j)||'+'||l_dimset_name then
3533           l_levels(l_levels.count+1):=l_oo_relation(k).relation_object;
3534         end if;
3535       end loop;
3536       --
3537       if l_combinations.count=0 then
3538         --add the levels to the combinations
3539         for k in 1..l_levels.count loop
3540           l_combinations(l_combinations.count+1):=l_levels(k);
3541         end loop;
3542       else
3543         l_combinations_copy.delete;
3544         l_combinations_copy:=l_combinations;
3545         --now cartesian
3546         for k in 1..l_levels.count loop
3547           for m in 1..l_combinations_copy.count loop
3548             l_combinations(l_combinations.count+1):=l_combinations_copy(m)||','||l_levels(k);
3549           end loop;
3550         end loop;
3551       end if;
3552     end loop;
3553     --
3554     for j in 1..l_combinations.count loop
3555       l_levels.delete;
3556       l_values.delete;
3557       bsc_aw_utility.parse_parameter_values(l_combinations(j),',',l_values);
3558       for k in 1..l_values.count loop
3559         l_levels(l_levels.count+1):=l_values(k).parameter;
3560       end loop;
3561       if l_levels.count=l_dimensions.count then
3562         --each combination is a table
3563         l_count:=l_count+1;
3564         p_tables(p_tables.count+1):=p_table_name||'_'||l_dimset||'_'||l_count;
3565         dmp_kpi_cubes_into_table(p_kpi,l_dimset,l_levels,p_tables(p_tables.count));
3566         p_tables(p_tables.count):=p_tables(p_tables.count)||' ('||l_dimset_name||')-> '||l_combinations(j);
3567       end if;
3568     end loop;
3569   end loop;
3570 Exception when others then
3571   log_n('Exception in dmp_kpi_cubes_into_table '||sqlerrm);
3572   raise;
3573 End;
3574 
3575 /*p_dimset of null means all dimsets
3576 null p_object_type means any object
3577 p_lock_type of inc means do not call bsc_aw_management.get_workspace_lock(). get_workspace_lock will do g_lockec_objects.delete
3578 it refreshes the internal info on current locked objects.
3579 inc means only go for additional locks.let the existing locks remain as they are
3580 */
3581 procedure lock_dimset_objects(p_kpi varchar2,p_dimset varchar2,p_object_type varchar2,p_lock_type varchar2) is
3582 --
3583 l_lock_objects dbms_sql.varchar2_table;
3584 Begin
3585   get_dimset_objects_to_lock(p_kpi,p_dimset,p_object_type,l_lock_objects);
3586   if p_lock_type='inc' then
3587     bsc_aw_management.get_lock(l_lock_objects,null);
3588   else
3589     bsc_aw_management.get_workspace_lock(l_lock_objects,null);
3590   end if;
3591 Exception when others then
3592   log_n('Exception in lock_dimset_objects '||sqlerrm);
3593   raise;
3594 End;
3595 
3596 procedure get_dimset_objects_to_lock(
3597 p_kpi varchar2,
3598 p_dimset varchar2,
3599 p_object_type varchar2,
3600 p_lock_objects out nocopy dbms_sql.varchar2_table) is
3601 l_bsc_olap_object bsc_aw_md_wrapper.bsc_olap_object_tb;
3602 l_objects dbms_sql.varchar2_table;
3603 Begin
3604   bsc_aw_md_api.get_bsc_olap_object(null,null,p_kpi,'kpi',l_bsc_olap_object);
3605   for i in 1..l_bsc_olap_object.count loop
3606     if l_bsc_olap_object(i).olap_object_type='dimension' and l_bsc_olap_object(i).object_type<>'agg map measure dim'
3607     and l_bsc_olap_object(i).object_type<>'measurename dim' and
3608     (p_dimset is null or bsc_aw_utility.get_parameter_value(l_bsc_olap_object(i).property1,'dim set name',',')=p_dimset) and
3609     (p_object_type is null or nvl(bsc_aw_utility.get_parameter_value(p_object_type,l_bsc_olap_object(i).object_type,','),'N')='Y') then
3610       l_objects(l_objects.count+1):=l_bsc_olap_object(i).olap_object;
3611     end if;
3612   end loop;
3613   for i in 1..l_bsc_olap_object.count loop
3614     if l_bsc_olap_object(i).olap_object_type='cube' and
3615     (p_dimset is null or bsc_aw_utility.get_parameter_value(l_bsc_olap_object(i).property1,'dim set name',',')=p_dimset) and
3616     (p_object_type is null or nvl(bsc_aw_utility.get_parameter_value(p_object_type,l_bsc_olap_object(i).object_type,','),'N')='Y') then
3617       l_objects(l_objects.count+1):=l_bsc_olap_object(i).olap_object;
3618     end if;
3619   end loop;
3620   for i in 1..l_bsc_olap_object.count loop
3621     if l_bsc_olap_object(i).olap_object_type='variable' and
3622     (p_dimset is null or bsc_aw_utility.get_parameter_value(l_bsc_olap_object(i).property1,'dim set name',',')=p_dimset) and
3623     (p_object_type is null or nvl(bsc_aw_utility.get_parameter_value(p_object_type,l_bsc_olap_object(i).object_type,','),'N')='Y') then
3624       l_objects(l_objects.count+1):=l_bsc_olap_object(i).olap_object;
3625     end if;
3626   end loop;
3627   --
3628   for i in 1..l_objects.count loop
3629     if bsc_aw_utility.in_array(p_lock_objects,l_objects(i))=false then
3630       p_lock_objects(p_lock_objects.count+1):=l_objects(i);
3631     end if;
3632   end loop;
3633 Exception when others then
3634   log_n('Exception in get_dimset_objects_to_lock '||sqlerrm);
3635   raise;
3636 End;
3637 
3638 /*
3639 given a base table list, get the list of dimsets
3640 p_base_table_list may not be a part of the kpi.
3641 in that case l_oor_dimset.count will be 0
3642 */
3643 procedure get_dimset_for_base_table(
3644 p_kpi varchar2,
3645 p_base_table_list dbms_sql.varchar2_table,
3646 p_dim_set out nocopy dbms_sql.varchar2_table
3647 ) is
3648 --
3649 l_oor_dimset bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3650 Begin
3651   for i in 1..p_base_table_list.count loop
3652     l_oor_dimset.delete;
3653     bsc_aw_md_api.get_base_table_dimset(p_kpi,p_base_table_list(i),'base table dim set',l_oor_dimset);
3654     for j in 1..l_oor_dimset.count loop
3655       if bsc_aw_utility.in_array(p_dim_set,l_oor_dimset(j).relation_object)=false then
3656         p_dim_set(p_dim_set.count+1):=l_oor_dimset(j).relation_object;
3657       end if;
3658     end loop;
3659   end loop;
3660 Exception when others then
3661   log_n('Exception in get_dimset_for_base_table '||sqlerrm);
3662   raise;
3663 End;
3664 
3665 /*given a set of dimsets and B tables, finds out the list of B tables from p_base_table_list that belong to the dimset */
3666 procedure get_base_table_for_dimset(p_kpi varchar2,p_base_table_list dbms_sql.varchar2_table,p_dim_set dbms_sql.varchar2_table,
3667 p_dimset_base_tables out nocopy dbms_sql.varchar2_table) is
3668 l_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3669 Begin
3670   for i in 1..p_dim_set.count loop
3671     l_oor.delete;
3672     bsc_aw_md_api.get_dimset_base_table(p_kpi,p_dim_set(i),'base table dim set',l_oor);
3673     for j in 1..l_oor.count loop
3674       if p_base_table_list.count=0 or bsc_aw_utility.in_array(p_base_table_list,l_oor(j).object) then
3675         bsc_aw_utility.merge_value(p_dimset_base_tables,l_oor(j).object);
3676       end if;
3677     end loop;
3678   end loop;
3679 Exception when others then
3680   log_n('Exception in get_base_table_for_dimset '||sqlerrm);
3681   raise;
3682 End;
3683 
3684 /*
3685 we have this procedure so we can eliminate all base tables that are not part of the kpi
3686 p_base_table_list will have all the base tables.
3687 */
3688 procedure get_kpi_base_tables(
3689 p_kpi varchar2,
3690 p_base_table_list dbms_sql.varchar2_table,
3691 p_kpi_base_tables out nocopy dbms_sql.varchar2_table
3692 ) is
3693 --
3694 l_olap_object_relation bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
3695 l_base_table varchar2(100);
3696 Begin
3697   bsc_aw_md_api.get_bsc_olap_object_relation(null,null,'base table dim set',p_kpi,'kpi',l_olap_object_relation);
3698   for i in 1..l_olap_object_relation.count loop
3699     l_base_table:=null;
3700     if p_base_table_list.count>0 then
3701       if bsc_aw_utility.in_array(p_base_table_list,l_olap_object_relation(i).object) then
3702         l_base_table:=l_olap_object_relation(i).object;
3703       end if;
3704     else
3705       l_base_table:=l_olap_object_relation(i).object;
3706     end if;
3707     if l_base_table is not null and bsc_aw_utility.in_array(p_kpi_base_tables,l_base_table)=false then
3708       p_kpi_base_tables(p_kpi_base_tables.count+1):=l_base_table;
3709     end if;
3710   end loop;
3711 Exception when others then
3712   log_n('Exception in get_kpi_base_tables '||sqlerrm);
3713   raise;
3714 End;
3715 
3716 function get_cache_aggregation_r(p_kpi varchar2) return aggregation_r is
3717 Begin
3718   for i in 1..g_cache_aggregation_r.count loop
3719     if g_cache_aggregation_r(i).kpi=p_kpi then
3720       return g_cache_aggregation_r(i);
3721     end if;
3722   end loop;
3723   return null;
3724 Exception when others then
3725   log_n('Exception in get_cache_aggregation_r '||sqlerrm);
3726   raise;
3727 End;
3728 
3729 function get_dim_set_index(p_aggregation aggregation_r,p_dim_set varchar2) return number is
3730 Begin
3731   for i in 1..p_aggregation.dim_set.count loop
3732     if p_aggregation.dim_set(i).dim_set_name=p_dim_set then
3733       return i;
3734     end if;
3735   end loop;
3736   log_n('Could not locate dimset '||p_dim_set||' in kpi ');
3737   raise bsc_aw_utility.g_exception;
3738 Exception when others then
3739   log_n('Exception in get_dim_set_index '||sqlerrm);
3740   raise;
3741 End;
3742 
3743 /*
3744 used by aggregate_measure in dbms job mode to get the locks on the cubes given the measures
3745 we return
3746 cube of the measure
3747 N: in the case of partitions, we make the assumption that the objects returned from this api can lock the same
3748 partition with the same name. example cube(partition P0) and countvarcube(partition P0). P0 can belong to diff PT
3749 (compressed)
3750 */
3751 procedure get_measure_objects_to_lock(
3752 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
3753 p_measures dbms_sql.varchar2_table,
3754 p_lock_objects out nocopy dbms_sql.varchar2_table
3755 ) is
3756 Begin
3757   for i in 1..p_dim_set.measure.count loop
3758     if bsc_aw_utility.in_array(p_measures,p_dim_set.measure(i).measure) then
3759       if p_dim_set.measure(i).cube is not null then
3760         if bsc_aw_utility.in_array(p_lock_objects,p_dim_set.measure(i).cube)=false then
3761           p_lock_objects(p_lock_objects.count+1):=p_dim_set.measure(i).cube;
3762         end if;
3763       end if;
3764       if p_dim_set.measure(i).countvar_cube is not null then
3765         if bsc_aw_utility.in_array(p_lock_objects,p_dim_set.measure(i).countvar_cube)=false then
3766           p_lock_objects(p_lock_objects.count+1):=p_dim_set.measure(i).countvar_cube;
3767         end if;
3768       end if;
3769     end if;
3770   end loop;
3771 Exception when others then
3772   log_n('Exception in get_measure_objects_to_lock '||sqlerrm);
3773   raise;
3774 End;
3775 
3776 --overloaded. first find dim_set_r, then call get_measure_objects_to_lock
3777 procedure get_measure_objects_to_lock(
3778 p_kpi varchar2,
3779 p_dimset varchar2,
3780 p_measures dbms_sql.varchar2_table,
3781 p_lock_objects out nocopy dbms_sql.varchar2_table
3782 ) is
3783 --
3784 l_aggregation aggregation_r;
3785 l_index number;
3786 Begin
3787   set_aggregation(p_kpi,l_aggregation);
3788   l_index:=get_dim_set_index(l_aggregation,p_dimset);
3789   if l_index is null then
3790     log_n('Could not find dimset '||p_dimset||', kpi '||p_kpi||' in aggregation_r. Fatal!');
3791     raise bsc_aw_utility.g_exception;
3792   end if;
3793   get_measure_objects_to_lock(l_aggregation.dim_set(l_index),p_measures,p_lock_objects);
3794 Exception when others then
3795   log_n('Exception in get_measure_objects_to_lock '||sqlerrm);
3796   raise;
3797 End;
3798 
3799 procedure insert_bsc_aw_temp_cv(p_min_value number,p_max_value number,p_base_table varchar2) is
3800 l_stmt varchar2(3000);
3801 Begin
3802   l_stmt:='insert into bsc_aw_temp_cv(change_vector_min_value,change_vector_max_value,change_vector_base_table) values(:1,:2,:3)';
3803   if g_debug then
3804     log(l_stmt||' using '||p_min_value||','||p_max_value||','||p_base_table);
3805   end if;
3806   execute immediate l_stmt using p_min_value,p_max_value,p_base_table;
3807 Exception when others then
3808   log_n('Exception in insert_bsc_aw_temp_cv '||sqlerrm);
3809   raise;
3810 End;
3811 
3812 /*
3813 we will look at how many cubes are involved and whether partitions are involved
3814 */
3815 function can_launch_jobs(p_kpi varchar2,p_dimset bsc_aw_adapter_kpi.dim_set_r,p_measures dbms_sql.varchar2_table) return varchar2 is
3816 l_cubes dbms_sql.varchar2_table;
3817 l_pt_comp varchar2(200);
3818 l_pt_comp_type varchar2(200);
3819 l_number_jobs number;
3820 l_cube_set bsc_aw_adapter_kpi.cube_set_r;
3821 Begin
3822   if p_dimset.cube_design='single composite' then
3823     return 'N';
3824   else
3825     /*for the measures, get cubes. for the cubes get pt comps. we can parallelize for each distinct pt comp */
3826     for i in 1..p_measures.count loop
3827       l_cube_set:=bsc_aw_adapter_kpi.get_cube_set_for_measure(p_measures(i),p_dimset);
3828       if bsc_aw_utility.in_array(l_cubes,l_cube_set.cube.cube_name)=false then
3829         l_cubes(l_cubes.count+1):=l_cube_set.cube.cube_name;
3830       end if;
3831     end loop;
3832     l_number_jobs:=0;
3833     bsc_aw_utility.init_is_new_value(1);
3834     for i in 1..l_cubes.count loop
3835       l_pt_comp:=bsc_aw_adapter_kpi.get_cube_pt_comp(l_cubes(i),p_dimset,l_pt_comp_type);
3836       if l_pt_comp is not null then
3837         if bsc_aw_utility.is_new_value(l_pt_comp,1) then
3838           l_number_jobs:=l_number_jobs+1;
3839         end if;
3840       end if;
3841       if p_dimset.number_partitions>0 then
3842         l_number_jobs:=l_number_jobs*p_dimset.number_partitions;
3843       end if;
3844     end loop;
3845     return bsc_aw_utility.can_launch_jobs(l_number_jobs);
3846   end if;
3847 Exception when others then
3848   log_n('Exception in can_launch_jobs '||sqlerrm);
3849   raise;
3850 End;
3851 
3852 procedure get_cubes_for_measures(
3853 p_measures dbms_sql.varchar2_table,
3854 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
3855 p_cubes out nocopy dbms_sql.varchar2_table) is
3856 l_cube_set bsc_aw_adapter_kpi.cube_set_r;
3857 Begin
3858   for i in 1..p_measures.count loop
3859     l_cube_set:=bsc_aw_adapter_kpi.get_cube_set_for_measure(p_measures(i),p_dim_set);
3860     if bsc_aw_utility.in_array(p_cubes,l_cube_set.cube.cube_name)=false then
3861       p_cubes(p_cubes.count+1):=l_cube_set.cube.cube_name;
3862     end if;
3863   end loop;
3864 Exception when others then
3865   log_n('Exception in get_cubes_for_measures '||sqlerrm);
3866   raise;
3867 End;
3868 
3869 procedure dmp_dimset_dim_statlen(p_dim_set bsc_aw_adapter_kpi.dim_set_r) is
3870 l_val varchar2(2000);
3871 Begin
3872   for i in 1..p_dim_set.dim.count loop
3873     l_val:=bsc_aw_dbms_aw.interp('show statlen('||p_dim_set.dim(i).dim_name||')');
3874   end loop;
3875   for i in 1..p_dim_set.std_dim.count loop
3876     l_val:=bsc_aw_dbms_aw.interp('show statlen('||p_dim_set.std_dim(i).dim_name||')');
3877   end loop;
3878   l_val:=bsc_aw_dbms_aw.interp('show statlen('||p_dim_set.calendar.aw_dim||')');
3879 Exception when others then
3880   log_n('Exception in dmp_dimset_dim_statlen '||sqlerrm);
3881   raise;
3882 End;
3883 
3884 /*
3885 given a list of base tables and the load program, api decides how many times to call the load program and what B table parameters to
3886 pass. if the DS has these entries : DS=B1+B2+B3+B1^B2^B3 then if the B tables to load are B2,B1 and B3, we will use B1,B2,B3 parameter
3887 the DS tables are stored in sorted order.
3888 */
3889 procedure get_ds_BT_parameters(
3890 p_kpi varchar2,
3891 p_dimset varchar2,
3892 p_load_program varchar2,
3893 p_b_tables dbms_sql.varchar2_table,
3894 p_ds_parameters out nocopy dbms_sql.varchar2_table) is
3895 --
3896 l_bt_string varchar2(4000);
3897 l_oo bsc_aw_md_wrapper.bsc_olap_object_tb;
3898 l_ds_parameters dbms_sql.varchar2_table;
3899 l_ds_tables dbms_sql.varchar2_table;
3900 l_flag boolean;
3901 l_bt_flag dbms_sql.varchar2_table;
3902 Begin
3903   l_bt_string:=bsc_aw_utility.make_string_from_list(bsc_aw_utility.order_array(p_b_tables));
3904   --
3905   bsc_aw_md_api.get_bsc_olap_object(p_load_program,'dml program',p_kpi,'kpi',l_oo);
3906   bsc_aw_utility.parse_parameter_values(bsc_aw_utility.get_parameter_value(l_oo(1).property1,'DS',','),'+',l_ds_parameters);
3907   for i in 1..l_ds_parameters.count loop
3908     l_ds_parameters(i):=replace(l_ds_parameters(i),'^',',');
3909   end loop;
3910   if g_debug then
3911     log('For load program '||p_load_program||', DS Parameters found');
3912     for i in 1..l_ds_parameters.count loop
3913       log(l_ds_parameters(i));
3914     end loop;
3915     log('BT string='||l_bt_string);
3916   end if;
3917   --
3918   if l_ds_parameters.count=0 then
3919     p_ds_parameters(p_ds_parameters.count+1):=l_bt_string;
3920   end if;
3921   --
3922   --quick search to see if we can find the string in the DS
3923   if p_ds_parameters.count=0 then
3924     for i in 1..l_ds_parameters.count loop
3925       if l_ds_parameters(i)=l_bt_string then
3926         p_ds_parameters(p_ds_parameters.count+1):=l_ds_parameters(i);
3927         exit;
3928       end if;
3929     end loop;
3930   end if;
3931   --
3932   --see if all tables are in any DS
3933   if p_ds_parameters.count=0 then --more involved search..
3934     for i in 1..l_ds_parameters.count loop
3935       l_ds_tables.delete;
3936       l_flag:=true;
3937       bsc_aw_utility.parse_parameter_values(l_ds_parameters(i),',',l_ds_tables);
3938       for j in 1..p_b_tables.count loop
3939         if bsc_aw_utility.in_array(l_ds_tables,p_b_tables(j))=false then
3940           l_flag:=false;
3941           exit;
3942         end if;
3943       end loop;
3944       if l_flag then
3945         p_ds_parameters(p_ds_parameters.count+1):=l_ds_parameters(i);
3946         exit;
3947       end if;
3948     end loop;
3949   end if;
3950   --
3951   /*
3952   more complex. no DS contains all the tables in p_b_tables. this can be very common. dimset contains B1 and B2. B1 and B2 are
3953   week and month.
3954   the for i in reverse 1..l_ds_parameters.count loop is a quick fix. ideally, we need to find the DS with the best match
3955   */
3956   if p_ds_parameters.count=0 then
3957     for i in 1..p_b_tables.count loop
3958       l_bt_flag(i):='N';
3959     end loop;
3960     for i in reverse 1..l_ds_parameters.count loop
3961       l_ds_tables.delete;
3962       l_flag:=false;
3963       bsc_aw_utility.parse_parameter_values(l_ds_parameters(i),',',l_ds_tables);
3964       for j in 1..p_b_tables.count loop
3965         if l_bt_flag(j)='N' and bsc_aw_utility.in_array(l_ds_tables,p_b_tables(j)) then
3966           l_bt_flag(j):='Y';
3967           l_flag:=true;
3968         end if;
3969       end loop;
3970       if l_flag then
3971         p_ds_parameters(p_ds_parameters.count+1):=l_ds_parameters(i);
3972       end if;
3973       l_flag:=false;
3974       for j in 1..l_bt_flag.count loop
3975         if l_bt_flag(j)='N' then
3976           l_flag:=true;
3977           exit;
3978         end if;
3979       end loop;
3980       if l_flag=false then --we are done
3981         exit;
3982       end if;
3983     end loop;
3984   end if;
3985   if g_debug then
3986     log('get_ds_BT_parameters, DS parameters');
3987     for i in 1..p_ds_parameters.count loop
3988       log(p_ds_parameters(i));
3989     end loop;
3990   end if;
3991 Exception when others then
3992   log_n('Exception in get_ds_BT_parameters '||sqlerrm);
3993   raise;
3994 End;
3995 
3996 /*this will check the base tables given and see if there is a current period change in the base table. if there is, we need to set the projection
3997 and balance measures to na
3998 we fire this for actual and target dimsets*/
3999 procedure check_bt_current_period_change(
4000 p_kpi varchar2,
4001 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
4002 p_cubes dbms_sql.varchar2_table,
4003 p_measures dbms_sql.varchar2_table,
4004 p_base_tables dbms_sql.varchar2_table,
4005 p_bt_current_period dbms_sql.varchar2_table,
4006 p_options varchar2
4007 ) is
4008 --
4009 l_oor bsc_aw_md_wrapper.bsc_olap_object_relation_tb;
4010 l_cube_measures bsc_aw_adapter_kpi.measure_tb;
4011 l_bt_periodicity dbms_sql.number_table;
4012 l_start_period dbms_sql.varchar2_table;
4013 l_end_period dbms_sql.varchar2_table;
4014 --
4015 type bt_r is record(
4016 table_name varchar2(100),
4017 periodicity number,
4018 current_period varchar2(40),
4019 ds_current_period varchar2(40),
4020 measures dbms_sql.varchar2_table
4021 );
4022 type bt_tb is table of bt_r index by pls_integer;
4023 l_base_tables bt_tb;
4024 --
4025 Begin
4026   if g_debug then
4027     log('check_bt_current_period_change for kpi '||p_kpi||', dimset '||p_dim_set.dim_set_name);
4028     log('Cubes');
4029     for i in 1..p_cubes.count loop
4030       log(p_cubes(i));
4031     end loop;
4032     log('Measures');
4033     for i in 1..p_measures.count loop
4034       log(p_measures(i));
4035     end loop;
4036     log('Base Tables and current period');
4037     for i in 1..p_base_tables.count loop
4038       log(p_base_tables(i)||' '||p_bt_current_period(i));
4039     end loop;
4040     log('Options '||p_options);
4041     log('--');
4042   end if;
4043   /*we do not have base table info in p_dim_set. so we have to go back to olap metadata */
4044   bsc_aw_md_api.get_dimset_base_table(p_kpi,p_dim_set.dim_set_name,'base table dim set',l_oor);
4045   /*load base table structures */
4046   for i in 1..p_base_tables.count loop
4047     l_base_tables(i).table_name:=p_base_tables(i);
4048     if p_bt_current_period(i) is not null and p_bt_current_period(i)<>'null' then
4049       l_base_tables(i).current_period:=p_bt_current_period(i);
4050     end if;
4051     for j in 1..l_oor.count loop
4052       if l_oor(j).object=p_base_tables(i) then
4053         l_base_tables(i).periodicity:=to_number(bsc_aw_utility.get_parameter_value(l_oor(j).property1,'base table periodicity',','));
4054         l_base_tables(i).ds_current_period:=bsc_aw_utility.get_parameter_value(l_oor(j).property1,'base table current period',',');
4055         bsc_aw_utility.parse_parameter_values(bsc_aw_utility.get_parameter_value(l_oor(j).property1,'measures',','),'+',
4056         l_base_tables(i).measures);
4057         exit;
4058       end if;
4059     end loop;
4060   end loop;
4061   /*we have to drive from cubes */
4062   for i in 1..p_cubes.count loop
4063     l_cube_measures.delete;
4064     l_bt_periodicity.delete;
4065     l_start_period.delete;
4066     l_end_period.delete;
4067     for j in 1..p_dim_set.measure.count loop
4068       if p_dim_set.measure(j).cube=p_cubes(i) and bsc_aw_utility.in_array(p_measures,p_dim_set.measure(j).measure) then
4069         l_cube_measures(l_cube_measures.count+1):=p_dim_set.measure(j);
4070       end if;
4071     end loop;
4072     for j in 1..l_base_tables.count loop
4073       if l_base_tables(j).current_period is not null and l_base_tables(j).ds_current_period is not null
4074       and l_base_tables(j).ds_current_period<>l_base_tables(j).current_period then
4075         for k in 1..l_cube_measures.count loop
4076           if bsc_aw_utility.in_array(l_base_tables(j).measures,l_cube_measures(k).measure) then
4077             l_bt_periodicity(l_bt_periodicity.count+1):=l_base_tables(j).periodicity;
4078             l_start_period(l_start_period.count+1):=l_base_tables(j).ds_current_period;
4079             l_end_period(l_end_period.count+1):=l_base_tables(j).current_period;
4080             exit;
4081           end if;
4082         end loop;
4083       end if;
4084     end loop;
4085     if l_cube_measures.count>0 and l_bt_periodicity.count>0 then
4086       check_bt_current_period_change(p_kpi,p_dim_set,p_cubes(i),l_cube_measures,l_bt_periodicity,l_start_period,l_end_period,p_options);
4087     else
4088       if g_debug then
4089         log('For cube '||p_cubes(i)||', no forecast or balance correction required for current period change in B table');
4090       end if;
4091     end if;
4092   end loop;
4093 Exception when others then
4094   log_n('Exception in check_bt_current_period_change '||sqlerrm);
4095   raise;
4096 End;
4097 
4098 /*when we have projections or balance, we cannot have compressed composites */
4099 procedure check_bt_current_period_change(
4100 p_kpi varchar2,
4101 p_dim_set bsc_aw_adapter_kpi.dim_set_r,
4102 p_cube varchar2,
4103 p_measures bsc_aw_adapter_kpi.measure_tb,
4104 p_bt_periodicity dbms_sql.number_table, /*p_bt_periodicity,p_start_period and p_end_period match in count*/
4105 p_start_period dbms_sql.varchar2_table,
4106 p_end_period dbms_sql.varchar2_table,
4107 p_options varchar2 /*contains partition info */
4108 ) is
4109 --
4110 l_prj_measures bsc_aw_adapter_kpi.measure_tb;
4111 l_bal_measures bsc_aw_adapter_kpi.measure_tb;
4112 l_partition_dim_value varchar2(100);
4113 l_bt_periodicity bsc_aw_adapter_kpi.periodicity_tb;
4114 l_pt_name varchar2(100);
4115 l_pt_type varchar2(100);
4116 l_stmt varchar2(2000);
4117 Begin
4118   /*correct projections if the measures have projections in them
4119   when this api is called, locks are already in place
4120   */
4121   for i in 1..p_measures.count loop
4122     if p_measures(i).forecast='Y' then
4123       l_prj_measures(l_prj_measures.count+1):=p_measures(i);
4124     end if;
4125     /*if we have default balance columns ie balance based on last period, have to na the old current period */
4126     if p_measures(i).measure_type='BALANCE' then
4127       l_bal_measures(l_bal_measures.count+1):=p_measures(i);
4128     end if;
4129   end loop;
4130   l_partition_dim_value:=bsc_aw_utility.get_parameter_value(p_options,'partition dim value',',');
4131   for i in 1..p_bt_periodicity.count loop
4132     l_bt_periodicity(i):=bsc_aw_adapter_kpi.get_periodicity_r(p_dim_set.calendar.periodicity,p_bt_periodicity(i));
4133   end loop;
4134   l_pt_name:=bsc_aw_adapter_kpi.get_cube_pt_comp(p_cube,p_dim_set,l_pt_type);
4135   if l_prj_measures.count>0 or l_bal_measures.count>0 then
4136     push_dim(p_dim_set.calendar.aw_dim);
4137     bsc_aw_utility.init_is_new_value(1);
4138     for i in 1..l_bt_periodicity.count loop
4139       if bsc_aw_utility.is_new_value(l_bt_periodicity(i).aw_dim,1) then
4140         push_dim(l_bt_periodicity(i).aw_dim);
4141       end if;
4142     end loop;
4143     push_dim(get_projection_dim(p_dim_set));
4144     push_dim(p_dim_set.measurename_dim);
4145     if l_partition_dim_value is not null and p_dim_set.partition_dim is not null then
4146       push_dim(p_dim_set.partition_dim);
4147     end if;
4148     if l_partition_dim_value is not null and p_dim_set.partition_dim is not null then
4149       limit_dim(p_dim_set.partition_dim,l_partition_dim_value,'TO');
4150     end if;
4151     /*all other dimensions are limited to all */
4152   end if;
4153   /*set the projections */
4154   if l_prj_measures.count>0 then
4155     limit_dim(p_dim_set.calendar.aw_dim,'NULL','TO');
4156     for i in 1..l_bt_periodicity.count loop
4157       bsc_aw_dbms_aw.execute('limit '||l_bt_periodicity(i).aw_dim||' TO '''||p_start_period(i)||''' TO '''||p_end_period(i)||'''');
4158       limit_dim(p_dim_set.calendar.aw_dim,l_bt_periodicity(i).aw_dim,'ADD');
4159     end loop;
4160     limit_calendar_ancestors(p_dim_set.calendar,'ADD');
4161     bsc_aw_dbms_aw.execute('limit '||get_projection_dim(p_dim_set)||' to ''Y''');
4162     /*when we are here, there cannot be compressed composite.CC cannot be when there are projections or balance */
4163     limit_dim(p_dim_set.measurename_dim,'NULL','TO');
4164     for i in 1..l_prj_measures.count loop
4165       limit_dim(p_dim_set.measurename_dim,''''||l_prj_measures(i).measure||'''','ADD');
4166     end loop;
4167     if g_debug then
4168       dmp_dimset_dim_statlen(p_dim_set);
4169     end if;
4170     l_stmt:=p_cube||'=NA';
4171     if l_pt_name is not null then
4172       l_stmt:=l_stmt||' across '||l_pt_name; /*both pt and composite */
4173     end if;
4174     bsc_aw_dbms_aw.execute(l_stmt);
4175   end if;
4176   /*set the balance */
4177   if l_bal_measures.count>0 then
4178     limit_dim(p_dim_set.calendar.aw_dim,'NULL','TO');
4179     for i in 1..l_bt_periodicity.count loop
4180       bsc_aw_dbms_aw.execute('limit '||l_bt_periodicity(i).aw_dim||' TO '''||p_start_period(i)||'''');
4181       limit_dim(p_dim_set.calendar.aw_dim,l_bt_periodicity(i).aw_dim,'ADD');
4182     end loop;
4183     limit_calendar_ancestors(p_dim_set.calendar,'ADD');
4184     /*for balance, we cannot limit projection dim to Y */
4185     limit_dim(p_dim_set.measurename_dim,'NULL','TO');
4186     for i in 1..l_bal_measures.count loop
4187       limit_dim(p_dim_set.measurename_dim,''''||l_bal_measures(i).measure||'''','ADD');
4188     end loop;
4189     if g_debug then
4190       dmp_dimset_dim_statlen(p_dim_set);
4191     end if;
4192     l_stmt:=p_cube||'=NA';
4193     if l_pt_name is not null then
4194       l_stmt:=l_stmt||' across '||l_pt_name; /*both pt and composite */
4195     end if;
4196     bsc_aw_dbms_aw.execute(l_stmt);
4197   end if;
4198   /*restore dim status */
4199   if l_prj_measures.count>0 or l_bal_measures.count>0 then
4200     pop_dim(p_dim_set.calendar.aw_dim);
4201     bsc_aw_utility.init_is_new_value(1);
4202     for i in 1..l_bt_periodicity.count loop
4203       if bsc_aw_utility.is_new_value(l_bt_periodicity(i).aw_dim,1) then
4204         pop_dim(l_bt_periodicity(i).aw_dim);
4205       end if;
4206     end loop;
4207     pop_dim(get_projection_dim(p_dim_set));
4208     pop_dim(p_dim_set.measurename_dim);
4209     if l_partition_dim_value is not null and p_dim_set.partition_dim is not null then
4210       pop_dim(p_dim_set.partition_dim);
4211     end if;
4212   end if;
4213 Exception when others then
4214   log_n('Exception in check_bt_current_period_change '||sqlerrm);
4215   raise;
4216 End;
4217 
4218 function dimset_has_bal_measures(p_dim_set bsc_aw_adapter_kpi.dim_set_r) return boolean is
4219 Begin
4220   for i in 1..p_dim_set.measure.count loop
4221     if substr(p_dim_set.measure(i).measure_type,1,7)='BALANCE' then
4222       return true;
4223     end if;
4224   end loop;
4225   return false;
4226 Exception when others then
4227   log_n('Exception in dimset_has_bal_measures '||sqlerrm);
4228   raise;
4229 End;
4230 
4231 procedure dmp_dimset_composite_count(p_dim_set bsc_aw_adapter_kpi.dim_set_r) is
4232 l_val varchar2(2000);
4233 Begin
4234   for i in 1..p_dim_set.composite.count loop
4235     l_val:=bsc_aw_dbms_aw.interp('show obj(dimmax '''||p_dim_set.composite(i).composite_name||''')');
4236   end loop;
4237 Exception when others then
4238   log_n('Exception in dmp_dimset_composite_count '||sqlerrm);
4239   raise;
4240 End;
4241 
4242 /*need ability to control when to turn on parallel threads. parallelism enabled for large objects */
4243 /*for loads, we look at stats for the B tables and check the total count . if the stats is current, get an idea of count for this load */
4244 function is_parallel_load(p_base_tables dbms_sql.varchar2_table,p_cutoff number) return boolean is
4245 change_vector dbms_sql.number_table;
4246 Begin
4247   if p_base_tables.count>0 then
4248     for i in 1..p_base_tables.count loop
4249       change_vector(i):=bsc_aw_md_api.get_bt_change_vector(p_base_tables(i));
4250     end loop;
4251     return is_parallel_load(p_base_tables,change_vector,p_cutoff);
4252   else
4253     if g_debug then
4254       log('is_parallel_load(1), p_base_tables.count=0. Go for parallel');
4255     end if;
4256     return true;/*default is to parallelize */
4257   end if;
4258 Exception when others then
4259   log_n('Exception in is_parallel_load '||sqlerrm);
4260   raise;
4261 End;
4262 
4263 function is_parallel_load(p_base_tables dbms_sql.varchar2_table,p_change_vector dbms_sql.number_table,p_cutoff number) return boolean is
4264 table_count number;
4265 total_table_count number;
4266 Begin
4267    if p_base_tables.count=0 then
4268      if g_debug then
4269        log('is_parallel_load(2), p_base_tables.count=0. Go for parallel');
4270      end if;
4271      return true;
4272    end if;
4273    total_table_count:=0;
4274    for i in 1..p_base_tables.count loop
4275      table_count:=get_table_load_count(p_base_tables(i),p_change_vector(i));
4276      if g_debug then
4277        log('For table '||p_base_tables(i)||' with change vector '||p_change_vector(i)||', Table Count='||table_count);
4278      end if;
4279      if table_count<0 then /*could not get the count */
4280        if g_debug then
4281          log('is_parallel_load, table_count<0. Go for parallel');
4282        end if;
4283        return true;/*go for parallel load. dont know if the table is small */
4284      end if;
4285      total_table_count:=total_table_count+table_count;
4286    end loop;
4287    if g_debug then
4288      log('is_parallel_load, total_table_count='||total_table_count||' and p_cutoff='||p_cutoff);
4289    end if;
4290    if total_table_count<=p_cutoff then
4291      return false;
4292    end if;
4293    return true;
4294 Exception when others then
4295   log_n('Exception in is_parallel_load '||sqlerrm);
4296   raise;
4297 End;
4298 
4299 /*tries to get this load count. if the stats are old, returns -1 */
4300 function get_table_load_count(p_table varchar2,p_change_vector number) return number is
4301 all_tables bsc_aw_utility.all_tables_tb;
4302 table_count number;
4303 Begin
4304   all_tables:=bsc_aw_utility.get_db_table_parameters(p_table,bsc_aw_utility.get_table_owner(p_table));
4305   if all_tables(1).last_analyzed is null or (sysdate-all_tables(1).last_analyzed)>30 then
4306     return -1;/*no stats or too old stats. cannot get count */
4307   end if;
4308   if p_change_vector is null or p_change_vector=1 then /*for initial load, just get the table row count */
4309     return nvl(all_tables(1).NUM_ROWS,-1);
4310   else
4311     /*assume there is bitmap on the change vector */
4312     table_count:=bsc_aw_utility.get_table_count(p_table,'change_vector='||p_change_vector);
4313     return nvl(table_count,-1);
4314   end if;
4315   return -1;
4316 Exception when others then
4317   log_n('Exception in get_table_load_count '||sqlerrm);
4318   raise;
4319 End;
4320 
4321 /*given a set of dimsets, checks total comp count to determine if parallel is true or false */
4322 function is_parallel_aggregate(p_dim_set bsc_aw_adapter_kpi.dim_set_tb,p_cutoff number) return boolean is
4323 comp_count number;
4324 total_comp_count number;
4325 Begin
4326   total_comp_count:=0;
4327   for i in 1..p_dim_set.count loop
4328     comp_count:=get_dimset_composite_count(p_dim_set(i));
4329     if comp_count<0 then
4330       if g_debug then
4331         log('is_parallel_aggregate, comp_count<0. Go for parallel');
4332       end if;
4333       return true;
4334     else
4335       total_comp_count:=total_comp_count+comp_count;
4336     end if;
4337   end loop;
4338   if g_debug then
4339     log('is_parallel_aggregate, total_comp_count='||total_comp_count||' and p_cutoff='||p_cutoff);
4340   end if;
4341   if total_comp_count<=p_cutoff then
4342     return false;
4343   end if;
4344   return true;
4345 Exception when others then
4346   log_n('Exception in is_parallel_aggregate '||sqlerrm);
4347   raise;
4348 End;
4349 
4350 function is_parallel_aggregate(p_dim_set bsc_aw_adapter_kpi.dim_set_r,p_cutoff number) return boolean is
4351 comp_count number;
4352 Begin
4353   comp_count:=get_dimset_composite_count(p_dim_set);
4354   if comp_count<0 then /*could not determine comp count */
4355     if g_debug then
4356       log('is_parallel_aggregate, comp_count<0. Go for parallel aggregate');
4357     end if;
4358     return true;/*try parallel */
4359   end if;
4360   if g_debug then
4361     log('is_parallel_aggregate, comp_count='||comp_count||' and p_cutoff='||p_cutoff);
4362   end if;
4363   if comp_count<=p_cutoff then
4364     return false;
4365   end if;
4366   return true;
4367 Exception when others then
4368   log_n('Exception in is_parallel_aggregate '||sqlerrm);
4369   raise;
4370 End;
4371 
4372 function get_dimset_composite_count(p_dim_set bsc_aw_adapter_kpi.dim_set_r) return number is
4373 l_val varchar2(2000);
4374 comp_count number;
4375 Begin
4376   comp_count:=0;
4377   for i in 1..p_dim_set.composite.count loop
4378     l_val:=bsc_aw_dbms_aw.interp('show obj(dimmax '''||p_dim_set.composite(i).composite_name||''')');
4379     if bsc_aw_utility.is_number(l_val) then
4380       comp_count:=comp_count+to_number(l_val);
4381     else
4382       return -1;
4383     end if;
4384   end loop;
4385   return comp_count;
4386 Exception when others then
4387   log_n('Exception in get_dimset_composite_count '||sqlerrm);
4388   raise;
4389 End;
4390 
4391 ----------------------------------------------------
4392 procedure init_all is
4393 Begin
4394   g_debug:=bsc_aw_utility.g_debug;
4395 Exception when others then
4396   null;
4397 End;
4398 
4399 procedure log(p_message varchar2) is
4400 Begin
4401   bsc_aw_utility.log(p_message);
4402 Exception when others then
4403   null;
4404 End;
4405 
4406 procedure log_n(p_message varchar2) is
4407 Begin
4408   log('  ');
4409   log(p_message);
4410 Exception when others then
4411   null;
4412 End;
4413 
4414 END BSC_AW_LOAD_KPI;