DBA Data[Home] [Help]

PACKAGE BODY: APPS.HRI_OPL_MULTI_THREAD

Source


1 PACKAGE BODY hri_opl_multi_thread AS
2 /* $Header: hriomthd.pkb 120.8 2007/01/18 07:24:56 anmajumd noship $ */
3 --
4 -- Main Entry Point from Concurren Manager
5 --
6 g_chunk                                NUMBER := 20;
7 g_slave_count                          NUMBER := 8;
8 g_errbuf                               VARCHAR2(2000);
9 g_retcode                              NUMBER;
10 --
11 -- Global variable used for caching mthd_range_id
12 --
13 g_mthd_range_id                        NUMBER;
14 --
15 -- Variable for setting the debug mode
16 --
17 g_debug_flag                           VARCHAR2(30) := NVL(fnd_profile.value('HRI_ENBL_DTL_LOG'),'N');
18 g_program                              VARCHAR2(30) ;
19 g_mode                                 VARCHAR2(30) := 'N';
20 g_mthd_action_id                       NUMBER;
21 g_error_request_id                     NUMBER;
22 --
23 -- Table Type for storing the request_id's for all child threads.
24 --
25 TYPE g_num_tab is table  of NUMBER INDEX BY BINARY_INTEGER;
26 --
27 -- Table for storing the concurrent request ID's
28 --
29 g_child_request_tab  g_num_tab;
30 --
31 -- EXCEPTIONS
32 --
33 error_launching_thread                 EXCEPTION;
34 no_sql_returned                        EXCEPTION;
35 no_ranges_to_process                   EXCEPTION;
36 invalid_program                        EXCEPTION;
37 --
38 PRAGMA EXCEPTION_INIT(invalid_program, -6550);
39 --
40 -- -----------------------------------------------------------------------------
41 -- Procedure Call Sequence (Forward Declaration)
42 -- The PROCESS rotine is invoked from the concurrent manager. The following
43 -- procedure are called sequence to multithreading the collection process
44 -- by the process routine
45 --
46 PROCEDURE process;
47   --
48   -- 1. Start Multithreading Process
49   --
50   PROCEDURE do_multithreading;
51     --
52     -- 1.1 Generate Multi Thread Action
53     --
54     PROCEDURE gen_multi_thread_action;
55     --
56     -- 1.2 Generate Object Ranges (public in package spec)
57     --
58     -- PROCEDURE gen_object_range(g_mthd_action_array.mthd_action_id);
59     --
60     -- 1.3 Start Threads
61     --
62     PROCEDURE start_threads;
63   --
64   -- 2. Process Range
65   --
66   PROCEDURE process_range(p_program                IN            VARCHAR2,
67                           p_mthd_action_id         IN            NUMBER,
68                           p_errbuf                    OUT NOCOPY VARCHAR2,
69                           p_retcode                   OUT NOCOPY NUMBER );
70   --
71   -- 3. Wait for Child Threads to complete Processing
72   --
73   PROCEDURE watch_child_processes(p_slave_errored out nocopy boolean);
74   --
75   -- 4. End Multithreading Process
76   --
77   PROCEDURE end_multithreading;
78     --
79     -- 4.1 Run the post process
80     --
81     PROCEDURE run_program_post_process;
82     --
83     -- 4.2 Update Multithread Action Status
84     --
85     PROCEDURE set_action_status;
86   --
87   PROCEDURE set_range_error(p_mthd_action_id NUMBER);
88   --
89 -- -----------------------------------------------------------------------------
90 -- Inserts row into concurrent program log
91 -- -----------------------------------------------------------------------------
92 --
93 PROCEDURE output(p_text  VARCHAR2) IS
94 --
95 BEGIN
96   --
97   -- Bug 4105868: Collection Diagnostic
98   --
99   HRI_BPL_CONC_LOG.output(p_text);
100   --
101 END output;
102 --
103 -- -----------------------------------------------------------------------------
104 -- Inserts row into concurrent program log if debugging is enabled
105 -- -----------------------------------------------------------------------------
106 --
107 PROCEDURE dbg(p_text  VARCHAR2) IS
108 --
109 BEGIN
110   --
111   -- Bug 4105868: Collection Diagnostic
112   --
113   HRI_BPL_CONC_LOG.dbg(p_text);
114   --
115 END dbg;
116 --
117 -- ----------------------------------------------------------------------------
118 -- Runs given sql statement dynamically
119 -- ----------------------------------------------------------------------------
120 --
121 PROCEDURE run_sql_stmt_noerr( p_sql_stmt   VARCHAR2 )
122 IS
123 --
124 BEGIN
125   --
126   dbg(p_sql_stmt);
127   --
128   EXECUTE IMMEDIATE p_sql_stmt;
129   --
130 EXCEPTION
131   --
132   WHEN OTHERS THEN
133     --
134     output('Error running sql:');
135     output(SUBSTR(p_sql_stmt,1,230));
136     --
137   --
138 END run_sql_stmt_noerr;
139 --
140 -- ----------------------------------------------------------------------------
141 -- Procedure   : GET_NEXT_MTHD_RANGE_ID
142 -- Description : This procedure is used for classifying the object records into
143 --               a range. Based on this classification the object records are
144 --               grouped into ranges. This logic is that for every nth rownum
145 --               the function will get a new range id otherwise it will return
146 --               the value in cache. Here n is the chunk size option value.
147 -- ----------------------------------------------------------------------------
148 --
149 FUNCTION get_next_mthd_range_id
150                  (p_rownum number
151                  ,p_chunk  number
152                  )
153 RETURN NUMBER IS
154 --
155 BEGIN
156   --
157   -- only when the rownum is a multiple of the chunk read a seqence
158   -- otherwise return the previous range stored in the cache.
159   --
160   IF mod(p_rownum,p_chunk) = 0 OR
161      g_mthd_range_id is null THEN
162     --
163     select hri_adm_mthd_ranges_s.nextval
164     into   g_mthd_range_id
165     from   dual;
166     --
167   END IF;
168   --
169   RETURN g_mthd_range_id;
170   --
171 END get_next_mthd_range_id;
172 --
173 -- ----------------------------------------------------------------------------
174 -- Procedure   : GET_ERROR_REQUEST
175 -- Description : This procedure populate the global variable with the request_id
176 --               of the erroring thread
177 -- ----------------------------------------------------------------------------
178 --
179 FUNCTION get_error_request(p_mthd_action_id  NUMBER)
180 RETURN NUMBER IS
181   --
182   l_error_request_id NUMBER;
183   --
184   CURSOR c_error_request IS
185   SELECT err_thread_request_id
186   FROM   hri_adm_mthd_ranges
187   WHERE  mthd_action_id = p_mthd_action_id
188   AND    status = 'ERROR'
189   AND    rownum = 1;
190   --
191 BEGIN
192   --
193   IF g_error_request_id is null THEN
194     --
195     OPEN   c_error_request;
196     FETCH  c_error_request INTO l_error_request_id;
197     CLOSE  c_error_request;
198     --
199   END IF;
200   --
201   RETURN  l_error_request_id;
202   --
203 END get_error_request;
204 --
205 -- ----------------------------------------------------------------------------
206 -- Waits for previously submitted multi-thread processes within same request
207 -- set to complete before continuing with the current one
208 -- ----------------------------------------------------------------------------
209 --
210 PROCEDURE wait_for_other_mthds IS
211 
212   -- Return count of unfinished multi-threading processes
213   -- that have been submitted before the current process
214   -- within the same request set
215   CURSOR mtmasters_cur IS
216   SELECT count(req.request_id) count
217   FROM fnd_concurrent_programs cp
218     ,fnd_executables ex
219     ,fnd_concurrent_requests req
220   WHERE ex.executable_id       = cp.executable_id
221   AND cp.concurrent_program_id = req.concurrent_program_id
222   AND ex.application_id        = cp.EXECUTABLE_APPLICATION_ID
223   AND cp.application_id        = req.PROGRAM_APPLICATION_ID
224 -- Restrict to non-complete multi-threading processes
225   AND ex.executable_name       = 'HRI_OPL_MULTI_THREAD'
226   AND req.PHASE_CODE <> 'C'
227 -- Requests previously submitted within the same request set
228   AND req.request_id IN
229          (SELECT req1.request_id
230           FROM fnd_concurrent_requests req1
231           WHERE req1.priority_request_id =
232 -- Subquery for requests in request set
233                    (SELECT req2.priority_request_id
234                     FROM fnd_concurrent_requests req2
235                     WHERE req2.request_id = fnd_global.conc_request_id)
236 -- Filter on previously submitted requests only
237           AND req1.request_id < fnd_global.conc_request_id);
238 
239   l_priority_count  NUMBER;
240   l_sleep           NUMBER;
241 
242 BEGIN
243 
244    -- Take a count of the requests of higher priority at hand
245    OPEN mtmasters_cur;
246    FETCH mtmasters_cur INTO l_priority_count;
247    CLOSE mtmasters_cur;
248 
249    -- Author  : SMOHAPAT
250    -- IF there are requests of higher priority
251    -- THEN Sleep and dont start the process at hand,
252    -- A l_priority_count = 0 means this is the request to be run immediately.
253    -- A l_priority_count > 0 means its not of higher priority.
254    -- So wait for higher priority requests to run and hibernate the one at hand.
255    WHILE (l_priority_count > 0) LOOP
256 
257        l_sleep := dbms_pipe.receive_message
258                   (pipename => 'non-existant pipe to force timeout',
259                    timeout  => 20);
260 
261    -- Is the higher priority req. count zero now, take a poll here.
262        OPEN mtmasters_cur;
263        FETCH mtmasters_cur INTO l_priority_count;
264        CLOSE mtmasters_cur;
265 
266    END LOOP;
267 
268 END wait_for_other_mthds;
269 --
270 --
271 -- ----------------------------------------------------------------------------
272 -- Procedure   : WAIT_FOR_LOWER_LEVELS
273 -- Description : Checks processing is complete for lower hierarchy levels
274 -- ----------------------------------------------------------------------------
275 --
276 PROCEDURE wait_for_lower_levels(p_mthd_action_id       IN NUMBER
277                                ,p_mthd_range_lvl_order IN NUMBER) IS
278 
279   CURSOR lower_hrchy_req_cur IS
280   SELECT count(*)
281   FROM hri_adm_mthd_ranges
282   WHERE mthd_action_id = p_mthd_action_id
283   AND status IN ('UNPROCESSED','PROCESSING')
284   AND mthd_range_lvl_order < p_mthd_range_lvl_order;
285 
286   l_lower_hrchy_count      NUMBER;
287   l_sleep                  VARCHAR2(3000);
288 
289 BEGIN
290 
291    -- Take a count of the requests for lower hierarchy levels still in progress
292    OPEN lower_hrchy_req_cur;
293    FETCH lower_hrchy_req_cur INTO l_lower_hrchy_count;
294    CLOSE lower_hrchy_req_cur;
295 
296    -- If any are found, enter a wait loop until they are complete
297    WHILE (l_lower_hrchy_count > 0) LOOP
298 
299        l_sleep := dbms_pipe.receive_message
300                   (pipename => 'non-existant pipe to force timeout',
301                    timeout  => 10);
302 
303    -- Recheck until the requests are completed
304        OPEN lower_hrchy_req_cur;
305        FETCH lower_hrchy_req_cur INTO l_lower_hrchy_count;
306        CLOSE lower_hrchy_req_cur;
307 
308    END LOOP;
309 
310 END wait_for_lower_levels;
311 --
312 -- ----------------------------------------------------------------------------
313 -- Procedure   : PROCESS
314 -- Description : Overloaded procedure which acts as the main controller for the
315 --               utility. It calls the various procedure for performing different
316 --               task.
317 -- ----------------------------------------------------------------------------
318 --
319 PROCEDURE process
320 IS
321   --
322   l_request_data           VARCHAR2(240);
323   l_slave_errored          BOOLEAN;
324   --
325 BEGIN
326   --
327   -- Wait for any other multi-threading processes previously submitted
328   -- in the same request set to complete
329   --
330   wait_for_other_mthds;
331   --
332   -- Bug 5023754, removing the alter session statement
333   --
334   -- execute immediate 'ALTER SESSION ENABLE PARALLEL DML';
335   --
336   BEGIN
337     --
338     -- 1. Start Multithreading Process
339     -- Start the multithreading process by performing the following tasks
340     --    a. Invoke the program pre processor
341     --    b. create the object ranges
342     --    c. spawn the child threads
343     --
344     -- If there are no ranges created then the no_ranges_to_process
345     -- exception will be raised. In that case no processing is required
346     -- but the post_process should still be called
347     -- if the pre-processor does not return any SQL, then no processing
348     -- is required and also the post processor need not be called.
349     --
350     do_multithreading;
351     dbg('done do_multithreading, calling process_range');
352     --
353     -- 2. Process Range
354     --
355     process_range(p_program                 => g_program
356                   ,p_mthd_action_id         => g_mthd_action_id
357                   ,p_errbuf                 => g_errbuf
358                   ,p_retcode                => g_retcode) ;
359     dbg('done process_range, calling watch_child_processes');
360     --
361     -- 3. Wait for Child Threads to complete Processing
362     --
363     watch_child_processes(l_slave_errored);
364     --
365   EXCEPTION
366     --
367     WHEN no_ranges_to_process THEN
368       --
369       -- There are no ranges to be processed, therefore only call the post_process
370       --
371       dbg('The program pre_processor returned a SQL, for which no range could be created.');
372       null;
373     WHEN no_sql_returned THEN
374       --
375       -- The pre processor may not return a SQL when the processing is done by it. This may be
376       -- required in case of processes which have a special processing logic in Foundation HR
377       -- mode. The following should happen in such a case
378       -- 1. No error should be raised
379       -- 2. the post processing routine should not be invoked.
380       -- Raise an error which will be handled at the end of this procedure. This will prevent
381       -- the call to post_processor and the process will not error out.
382       --
383       RAISE;
384   END;
385   --
386   dbg('Finished processing ranges, determining how to end the process');
387   --
388   IF l_slave_errored THEN
389     --
390     -- One of the child thread encountered an error, an error should be raised
391     --
392     dbg('An error condition was encountered in one of the child process');
393     --
394     -- Get the request id of the erroring thread
395     --
396     g_error_request_id := GET_ERROR_REQUEST(p_mthd_action_id => g_mthd_action_id);
397     --
398     fnd_message.set_name('HRI','HRI_MLT_OTHER_THREAD_IN_ERR');
399     fnd_message.set_token('REQUEST_ID',g_error_request_id);
400     RAISE other_thread_in_error;
401     --
402   ELSE
403     --
404     -- 4. End Multithreading Process
405     --
406     dbg('calling end_multithreading');
407     end_multithreading;
408     --
409   END IF;
410   --
411   dbg('Done processing exiting now');
412   --
413 EXCEPTION
414   WHEN no_sql_returned THEN
415     --
416     dbg('Inside no_sql_returned exceotion..Not doing anything');
417     RETURN;
418     --
419     -- Any other exception should be handled the the calling routine
420     --
421 END PROCESS;
422 --
423 -- ----------------------------------------------------------------------------
424 -- 1. Start Multithreading Process
425 -- Procedure   : DO_MULTITHREADING
426 -- Description : This procedure does the tast of starting the  Multithreading
427 --               Process. It invokes the process to call ther
428 --               Pre Processor
429 --               Create the Ranges based on the SQL returned by the the pre_process
430 --               Start the Child threads for processing the ranges
431 -- ----------------------------------------------------------------------------
432 --
433 PROCEDURE  do_multithreading
434 IS
435   --
436   l_process_action_id     NUMBER;
437   l_from_date             DATE;
438   l_to_date               DATE;
439   l_sql                   VARCHAR2(1000);
440   l_program               VARCHAR2(100);
441   --
442 BEGIN
443   --
444   dbg('Inside do_multithreading');
445   --
446   -- 1.1 Generate Multi Thread Action
447   --
448   gen_multi_thread_action;
449   --
450   -- 1.2 Generate Object Ranges
451   --
452   gen_object_range(g_mthd_action_array.mthd_action_id);
453   --
454   -- 1.3 Start Threads
455   --
456   start_threads;
457   --
458   COMMIT;
459   --
460   dbg('Exiting do_multithreading');
461   --
462 END do_multithreading;
463 --
464 -- ----------------------------------------------------------------------------
465 -- 1.1 Generate Multi Thread Action
466 -- Procedure   : GEN_MULTI_THREAD_ACTION
467 -- Description : This procedure inserts the parameters passed to the utility
468 --               by the concurrent request, into the action table.
469 -- ----------------------------------------------------------------------------
470 --
471 PROCEDURE gen_multi_thread_action IS
472   --
473   -- curosor to get the short name of the concurrent program
474   -- which is being run
475   --
476   CURSOR c_process_name IS
477     SELECT concurrent_program_name
478     FROM   fnd_concurrent_programs prg,
479            fnd_concurrent_requests req
480     WHERE  prg.concurrent_program_id = req.concurrent_program_id
481     AND    req.request_id = fnd_global.conc_request_id;
482   --
483   l_process_name    VARCHAR2(30);
484   --
485 BEGIN
486   --
487   -- mthd_action_id for the current process is not defined. A new row will have to be created
488   -- in hri_adm_mthd_action for this process
489   --
490   IF hr_general.chk_product_installed(800) = 'FALSE' THEN
491     --
492     g_mthd_action_array.foundation_hr_flag  :=  'Y';
493     --
494   ELSIF NVL(fnd_profile.value('HRI_DBI_FORCE_SHARED_HR'),'N') = 'Y' THEN
495     --
496     g_mthd_action_array.foundation_hr_flag  :=  'Y';
497     --
498   ELSE
499     --
500     g_mthd_action_array.foundation_hr_flag  :=  'N';
501     --
502   END IF;
503   --
504   -- Get the short name of the concurrent program that is running
505   --
506   OPEN  c_process_name;
507   FETCH c_process_name INTO l_process_name;
508   CLOSE c_process_name;
509   --
510   -- Initialize the record
511   --
512   g_mthd_action_array.request_id             :=  fnd_global.conc_request_id;
513   g_mthd_action_array.debug_flag             :=  g_debug_flag;
514   g_mthd_action_array.process_name           :=  l_process_name;
515   --
516   INSERT into HRI_ADM_MTHD_ACTIONS
517     ( MTHD_ACTION_ID,
518       PROGRAM,
519       REQUEST_ID,
520       COLLECT_FROM_DATE,
521       COLLECT_TO_DATE,
522       BUSINESS_GROUP_ID,
523       FULL_REFRESH_FLAG,
524       DEBUG_FLAG,
525       FOUNDATION_HR_FLAG,
526       HIERARCHICAL_PROCESS_FLAG,
527       HIERARCHICAL_PROCESS_TYPE,
528       ATTRIBUTE1,
529       ATTRIBUTE2,
530       ATTRIBUTE3,
531       ATTRIBUTE4,
532       ATTRIBUTE5,
533       ATTRIBUTE6,
534       ATTRIBUTE7,
535       ATTRIBUTE8,
536       ATTRIBUTE9,
537       ATTRIBUTE10,
538       ATTRIBUTE11,
539       ATTRIBUTE12,
540       ATTRIBUTE13,
541       ATTRIBUTE14,
542       ATTRIBUTE15,
543       ATTRIBUTE16,
544       ATTRIBUTE17,
545       ATTRIBUTE18,
546       ATTRIBUTE19,
547       ATTRIBUTE20,
548       STATUS ,
549       START_TIME ,
550       END_TIME ,
551       PROCESS_TYPE ,
552       PROCESS_NAME)
553 VALUES
554      (hri_adm_mthd_actions_s.nextval,
555       g_mthd_action_array.PROGRAM,
556       g_mthd_action_array.REQUEST_ID,
557       g_mthd_action_array.COLLECT_FROM_DATE,
558       g_mthd_action_array.COLLECT_TO_DATE,
559       g_mthd_action_array.BUSINESS_GROUP_ID,
560       g_mthd_action_array.FULL_REFRESH_FLAG,
561       g_mthd_action_array.DEBUG_FLAG,
562       g_mthd_action_array.FOUNDATION_HR_FLAG,
563       g_mthd_action_array.HIERARCHICAL_PROCESS_FLAG,
564       g_mthd_action_array.HIERARCHICAL_PROCESS_TYPE,
565       g_mthd_action_array.ATTRIBUTE1,
566       g_mthd_action_array.ATTRIBUTE2,
567       g_mthd_action_array.ATTRIBUTE3,
568       g_mthd_action_array.ATTRIBUTE4,
569       g_mthd_action_array.ATTRIBUTE5,
570       g_mthd_action_array.ATTRIBUTE6,
571       g_mthd_action_array.ATTRIBUTE7,
572       g_mthd_action_array.ATTRIBUTE8,
573       g_mthd_action_array.ATTRIBUTE9,
574       g_mthd_action_array.ATTRIBUTE10,
575       g_mthd_action_array.ATTRIBUTE11,
576       g_mthd_action_array.ATTRIBUTE12,
577       g_mthd_action_array.ATTRIBUTE13,
578       g_mthd_action_array.ATTRIBUTE14,
579       g_mthd_action_array.ATTRIBUTE15,
580       g_mthd_action_array.ATTRIBUTE16,
581       g_mthd_action_array.ATTRIBUTE17,
582       g_mthd_action_array.ATTRIBUTE18,
583       g_mthd_action_array.ATTRIBUTE19,
584       g_mthd_action_array.ATTRIBUTE20,
585       g_mthd_action_array.STATUS ,
586       g_mthd_action_array.START_TIME ,
587       g_mthd_action_array.END_TIME ,
588       g_mthd_action_array.PROCESS_TYPE ,
589       g_mthd_action_array.PROCESS_NAME)
590  RETURNING mthd_action_id INTO g_mthd_action_id;
591  --
592  g_mthd_action_array.mthd_action_id         :=  g_mthd_action_id;
593  --
594 EXCEPTION
595   WHEN others THEN
596   RAISE;
597 END gen_multi_thread_action;
598 --
599 -- ----------------------------------------------------------------------------
600 -- 1.2 Generate Object Ranges
601 -- Procedure   : GEN_OBJECT_RANGE
602 -- Description : This procedure invokes the program pre_processor and generates
603 --               the object range based on the SQL returned by the pre-processor
604 -- ----------------------------------------------------------------------------
605 --
606 PROCEDURE gen_object_range (p_mthd_action_id  NUMBER)
607 IS
608   --
609   l_dyn_pre_process_sql varchar2(4000);
610   l_dyn_range_sql varchar2(4000);
611   l_sqlstr       varchar2(2000);
612   l_mthd_action_id NUMBER;
613   --
614 BEGIN
615   dbg('inside gen_object_range');
616   --
617   --
618   --
619   l_mthd_action_id := nvl(p_mthd_action_id,
620                           g_mthd_action_array.mthd_action_id);
621   --
622   -- Read the profile value HRI:Multithreading Chunk Size
623   --
624   g_chunk := NVL(fnd_profile.value('HRI_MTHD_CHUNK_SIZE'),20);
625   dbg('chunk size = '||g_chunk);
626   --
627   -- Create the dynamic SQL for calling the pre_processor of the program.
628   -- Based on the sql returned by the pre_process the ranges will be created.
629   -- However, if no SQL is returned, the processing should be stopped without
630   -- returning any error. This is for supporting cases when the program does
631   -- not need to do any processing e.g. Assignment events in shared HR mode
632   --
633   l_dyn_pre_process_sql := 'BEGIN '||
634                               g_program||'.PRE_PROCESS(:l_mthd_action_id, :l_sqlstr);
635                             END;';
636   --
637   dbg('l_dyn_pre_process_sql='||l_dyn_pre_process_sql);
638   --
639   EXECUTE IMMEDIATE l_dyn_pre_process_sql
640   USING   p_mthd_action_id,
641           OUT l_sqlstr;
642   --
643   -- The pre_processor can update some of the action column so repopulate the
644   -- action array
645   --
646   g_mthd_action_array := get_mthd_action_array(p_mthd_action_id => l_mthd_action_id);
647   --
648   IF l_sqlstr is null THEN
649     --
650     -- The program did not return any SQL. The processing has already be done therefore
651     --   skip creating the ranges
652     --   skip creating the Child threads for processing the ranges
653     --   skip calling the process_range
654     --   skip calling the post_process
655     -- Raise an exception which will be handled in the calling procedure to gracefully
656     -- skip all the above listed steps
657     --
658     raise_application_error(-20996,'Multi threading Pre Processor did not return any SQL');
659     --
660   END IF;
661   --
662   -- Create the dynamic sql for creating the multithread ranges.
663   -- This SQL will create the ranges based on the SQL returned by the pre-processor
664   --
665   IF (g_mthd_action_array.hierarchical_process_flag = 'Y' AND
666       g_mthd_action_array.hierarchical_process_type = 'TOP_DOWN') THEN
667 
668     l_dyn_range_sql :=
669 'INSERT /*+ append parallel(range, default,default) */ INTO
670   hri_adm_mthd_ranges range
671    (mthd_range_id
672    ,mthd_range_lvl
673    ,mthd_range_lvl_order
674    ,mthd_action_id
675    ,start_object_id
676    ,end_object_id
677    ,status)
678    SELECT
679     mthd_range_id
680    ,object_lvl
681    ,object_lvl
682    ,:mthd_action_id
683    ,min(object_id)
684    ,max(object_id)
685    ,''UNPROCESSED''
686    FROM
687     (SELECT
688       object_lvl + CEIL(ROWNUM / :chunk_size)  mthd_range_id
689      ,object_lvl
690      ,object_id
691      FROM
692       (SELECT object_id, object_lvl
693        FROM (' || l_sqlstr || ')
694        ORDER BY object_lvl, object_id))
695    GROUP BY
696     mthd_range_id
697    ,object_lvl';
698 
699   ELSIF (g_mthd_action_array.hierarchical_process_flag = 'Y' AND
700          g_mthd_action_array.hierarchical_process_type = 'BOTTOM_UP') THEN
701 
702     l_dyn_range_sql :=
703 'INSERT /*+ append parallel(range, default,default) */ INTO
704   hri_adm_mthd_ranges range
705    (mthd_range_id
706    ,mthd_range_lvl
707    ,mthd_range_lvl_order
708    ,mthd_action_id
709    ,start_object_id
710    ,end_object_id
711    ,status)
712    SELECT
713     mthd_range_id
714    ,object_lvl
715    ,0 - object_lvl
716    ,:mthd_action_id
717    ,min(object_id)
718    ,max(object_id)
719    ,''UNPROCESSED''
720    FROM
721     (SELECT
722       1000 - object_lvl + CEIL(ROWNUM / :chunk_size)  mthd_range_id
723      ,object_lvl
724      ,object_id
725      FROM
726       (' || l_sqlstr || ')
727     )
728    GROUP BY
729     mthd_range_id
730    ,object_lvl';
731 
732   ELSE
733 
734     l_dyn_range_sql :=
735 'INSERT /*+ append parallel(range, default,default) */ INTO
736   hri_adm_mthd_ranges range
737    (mthd_range_id
738    ,mthd_range_lvl
739    ,mthd_range_lvl_order
740    ,mthd_action_id
741    ,start_object_id
742    ,end_object_id
743    ,status)
744    SELECT
745     mthd_range_id
746    ,1
747    ,1
748    ,:mthd_action_id
749    ,min(object_id)
750    ,max(object_id)
751    ,''UNPROCESSED''
752    FROM
753     (SELECT
754       CEIL(ROWNUM / :chunk_size)  mthd_range_id
755      ,object_id
756      FROM
757       (' || l_sqlstr || ')
758     )
759    GROUP BY
760     mthd_range_id';
761 
762   END IF;
763 
764   dbg('l_dyn_range_sql='||l_dyn_range_sql);
765   --
766   -- Bug 5023754
767   --
768   COMMIT;
769   --
770   -- Enable parallel session
771   --
772   EXECUTE IMMEDIATE 'ALTER SESSION ENABLE PARALLEL DML';
773   --
774   -- Run the dynamic SQL for creating the range
775   --
776   EXECUTE IMMEDIATE l_dyn_range_sql USING l_mthd_action_id, g_chunk;
777   --
778   COMMIT;
779   --
780   -- Disable parallel session
781   --
782   EXECUTE IMMEDIATE 'ALTER SESSION DISABLE PARALLEL DML';
783   --
784   dbg('exiting gen_object_range');
785 EXCEPTION
786   --
787   WHEN invalid_program THEN
788     --
789     dbg('Invalid program name passsed to the pre process='||g_program);
790     fnd_message.set_name('HRI','HRI_MLT_INVALID_PGM_NAME');
791     RAISE invalid_program;
792     --
793   WHEN OTHERS THEN
794     IF SQLCODE = - 20997 THEN
795       --
796       dbg('Invalid SQL returned by the pre process='||SQLCODE);
797       dbg(l_dyn_range_sql);
798       fnd_message.set_name('HRI', 'HRI_MLT_INVALID_SQL');
799       RAISE invalid_sql;
800       --
801     ELSIF SQLCODE = - 20996 THEN
802       --
803       dbg(fnd_message.get);
804       dbg('No SQL returned by the pre process=');
805       RAISE no_sql_returned;
806       --
807     ELSE
808       --
809       RAISE;
810       --
811     END IF;
812     --
813 END gen_object_range;
814 --
815 -- ----------------------------------------------------------------------------
816 -- 1.3 Start Threads
817 -- Procedure   : START_THREAD
818 -- Description : This procedure spawns the child threads for processing the ranges
819 --               The number of threads to be spawned is controlled by the profile
820 --               HRI: Number of Threads to Launch. However, if the number of ranges
821 --               is less then the profile then only (no of ranges -1) threads will
822 --               be launched. -1 as the master thread also helps in processing
823 -- ----------------------------------------------------------------------------
824 --
825 PROCEDURE start_threads
826 IS
827   --
828   l_request_id  NUMBER;
829   l_slave_count NUMBER;
830   --
831   CURSOR c_get_range_count IS
832   SELECT count(*)
833   FROM   hri_adm_mthd_ranges
834   WHERE  mthd_action_id = g_mthd_action_id;
835   --
836 BEGIN
837   dbg('Inside start_threads');
838   --
839   -- Read the profile value HRI:Number of Threads to Launch
840   --
841   g_slave_count := NVL(fnd_profile.value('HRI_NO_THRDS_LAUNCH'),8);
842   dbg('threads to launch = '||g_slave_count);
843   --
844   -- The number of threads to be launched is controlled by the profile option
845   -- HRI: Number of Threads to Launch. However in case the total
846   -- number of range records created is less than the value for this profile
847   -- then only limited number of threads should be started. This will avoid
848   -- situations where a thread doesn't get any ranges to process
849   --
850   OPEN  c_get_range_count;
851   FETCH c_get_range_count into l_slave_count;
852   CLOSE c_get_range_count;
853   --
854   IF l_slave_count is null THEN
855     --
856     -- No range records have been created, so raise an error
857     --
858     RAISE no_ranges_to_process;
859     --
860   ELSIF l_slave_count = 1 THEN
861     --
862     -- There is only one range to be processed, which will be processed by
863     -- the master thread so do not launch any child threads.
864     --
865     RETURN;
866     --
867   ELSIF l_slave_count > g_slave_count THEN
868     --
869     -- There are a number of ranges to be processed, so start the threads
870     --
871     l_slave_count := g_slave_count;
872     --
873   ELSE
874     --
875     l_slave_count := l_slave_count - 1;
876     --
877   END IF;
878   --
879   -- Start the child processes
880   --
881   FOR l_count in 1..l_slave_count loop
882     --
883     l_request_id :=
884       fnd_request.submit_request
885         (
886          application             => 'HRI'
887         ,program                 => 'HRI_MTHD_CHILD_PROCESS'
888         ,sub_request             => FALSE      -- Indicates that the request will not be
889                                                -- executed as a sub process.
890         ,argument1               => fnd_global.conc_request_id
891         ,argument2               => g_program
892         ,argument3               => g_mthd_action_id
893         ,argument4               => l_count + 1  -- worker_id
894       );
895     --
896     IF l_request_id = 0 then
897       --
898       -- If request submission failed, raise exception
899       --
900       dbg('Error launching thread.');
901       RAISE error_launching_thread;
902       --
903     ELSE
904       --
905       -- Store the request_id of the submitted request in an array. This
906       -- array is used in checking if all the child threads have finished
907       -- processing the range.
908       --
909       g_child_request_tab(l_count) := l_request_id;
910       --
911     END IF;
912     --
913   END LOOP;
914   --
915   dbg('Exiting start_threads');
916   --
917 EXCEPTION
918   --
919   WHEN others THEN
920     --
921     fnd_message.set_name('HRI', 'HRI_MLT_START_THREAD');
922     dbg('Exception encountered in starting thread');
923     dbg(sqlerrm);
924     RAISE;
925     --
926 END start_threads;
927 --
928 -- ----------------------------------------------------------------------------
929 -- 2. Process Range
930 -- Procedure   : PROCESS_RANGE
931 -- Description : This procedure dynamically invokes the entry point in the
932 --               collection programs package to process the ranges.
933 -- ----------------------------------------------------------------------------
934 --
935 PROCEDURE process_range(p_program                IN            VARCHAR2,
936                         p_mthd_action_id         IN            NUMBER,
937                         p_errbuf                    OUT NOCOPY VARCHAR2,
938                         p_retcode                   OUT NOCOPY NUMBER )
939 IS
940   --
941   l_step        NUMBER;
942   l_dyn_sql     VARCHAR2(4000);
943   l_lvl_param   VARCHAR2(100);
944   --
945 BEGIN
946   --
947   dbg('inside process_range');
948   --
949   l_step := 10;
950   --
951   IF (g_mthd_action_array.hierarchical_process_flag = 'Y') THEN
952     l_lvl_param := '
953                 ,p_mthd_range_lvl           => l_mthd_range_lvl';
954   END IF;
955   --
956   -- Construct the dynamic query to invoke the process_range procedure
957   -- The dynamic SQL gets the next unprocessed range and invokes the
958   -- process_range procedure to process the range. The allocation of
959   -- next range is included in the dyn sql so that the session remains
960   -- the same and the child thread does not have to re-initialize
961   -- globals variables again.
962   --
963   dbg('processing p_mthd_action_id ='||p_mthd_action_id);
964   l_dyn_sql :=
965     'DECLARE
966        l_errbuf                VARCHAR2(1000);
967        l_retcode               NUMBER;
968        l_mthd_range_id         NUMBER;
969        l_mthd_range_lvl        NUMBER;
970        l_mthd_range_lvl_nxt    NUMBER;
971        l_mthd_range_lvl_order  NUMBER;
972        l_start_object_id       NUMBER;
973        l_end_object_id         NUMBER;
974        l_mthd_action_id        NUMBER := '||p_mthd_action_id||';
975      BEGIN
976        LOOP
977          hri_opl_multi_thread.get_next_range
978                 (p_mthd_action_id         => l_mthd_action_id
979                 ,p_mthd_range_id          => l_mthd_range_id
980                 ,p_mthd_range_lvl         => l_mthd_range_lvl_nxt
981                 ,p_mthd_range_lvl_order   => l_mthd_range_lvl_order
982                 ,p_start_object_id        => l_start_object_id
983                 ,p_end_object_id          => l_end_object_id);
984          hri_opl_multi_thread.dbg(''l_mthd_range_id  =''||l_mthd_range_id );
985          IF l_mthd_range_id is null THEN
986            EXIT;
987          ELSE
988            IF (l_mthd_range_lvl <> l_mthd_range_lvl_nxt) THEN
989              hri_opl_multi_thread.wait_for_lower_levels
990               (p_mthd_action_id         => l_mthd_action_id
991               ,p_mthd_range_lvl_order   => l_mthd_range_lvl_order);
992            END IF;
993            l_mthd_range_lvl := l_mthd_range_lvl_nxt;
994            '||p_program||'.process_range(
995                  errbuf                     => :l_errbuf
996                 ,retcode                    => :l_retcode
997                 ,p_mthd_action_id           => l_mthd_action_id
998                 ,p_mthd_range_id            => l_mthd_range_id' || l_lvl_param || '
999                 ,p_start_object_id          => l_start_object_id
1000                 ,p_end_object_id            => l_end_object_id);
1001          END IF;
1002        END LOOP;
1003      EXCEPTION
1004        WHEN OTHERS THEN
1005          RAISE;
1006      END;';
1007   --
1008   l_step := 20;
1009   dbg('process_range sql ='||l_dyn_sql);
1010   --
1011   -- execute the dynamic sql to process the ranges.
1012   --
1013   BEGIN
1014     --
1015     EXECUTE IMMEDIATE l_dyn_sql
1016     USING   OUT p_errbuf,
1017             OUT p_retcode;
1018     --
1019     -- p_retcode = 0 means the process completed successfully
1020     -- p_retcode = 1 means that the process encountered a warning
1021     -- p_retcode > 2 means that the process encountered an ERROR.
1022     -- So if the retcode returned is is greater than 1 then raise an exception
1023     --
1024     dbg('finished processing the ranges');
1025     IF p_retcode >= 2 THEN
1026       --
1027       -- Store the error code in the global error buffer and raise the error
1028       --
1029       g_errbuf   := p_errbuf;
1030       g_retcode  := p_retcode;
1031       dbg('The process_range routine returned a error code of '||p_retcode);
1032       raise_application_error(-20998,'CHILD_PROCESS_FAILURE');
1033       --
1034     END IF;
1035     --
1036   EXCEPTION
1037     WHEN others THEN
1038       l_step := 30;
1039       --
1040       -- When an exception is raised, then the status of the ranges and the action
1041       -- records should changed to Error. However, this should not be done when
1042       -- the application error - 20999 is raised; done when a parallel thread
1043       -- encounters an error because of which the processing of this thread should stop.
1044       -- In such a case the set_range_error procedure should not be called.
1045       -- Why try to mark the ranges as error when it has already be done by
1046       -- the thread which encountered the error.
1047       --
1048       dbg('Error raised by the process, marking the ranges as error');
1049       IF SQLCODE <> - 20999 THEN
1050         --
1051         hri_opl_multi_thread.set_range_error(p_mthd_action_id);
1052         fnd_message.set_name('HRI','HRI_MLT_CHILD_THREAD_IN_ERR');
1053         --
1054         RAISE;
1055         -- raise_application_error(- 20998,'CHILD_PROCESS_FAILURE');
1056         --
1057       END IF;
1058       RAISE;
1059       --
1060   END;
1061   --
1062   dbg('exiting process_range');
1063   --
1064 EXCEPTION
1065   --
1066   WHEN invalid_program THEN
1067     --
1068     -- Raised when the package or package.procedure does not exist
1069     --
1070     dbg('Invalid program name passsed to process_range='||g_program);
1071     fnd_message.set_name('HRI','HRI_MLT_INVALID_PGM_NAME');
1072     --
1073     RAISE invalid_program;
1074     --
1075   WHEN others THEN
1076     --
1077     dbg('error in process_range ='||sqlerrm);
1078     dbg('SQLCODE = '||SQLCODE);
1079     --
1080     IF SQLCODE = - 20999 THEN
1081       --
1082       -- An error was encountered by a parallel thread, so all the child threads
1083       -- should stop processing and be marked as errored
1084       --
1085       dbg('An error condition was encountered in one of the child process');
1086       --
1087       -- Get the request id of the erroring thread
1088       --
1089       g_error_request_id := GET_ERROR_REQUEST(p_mthd_action_id => p_mthd_action_id);
1090       --
1091       fnd_message.set_name('HRI','HRI_MLT_OTHER_THREAD_IN_ERR');
1092       fnd_message.set_token('REQUEST_ID',g_error_request_id);
1093       RAISE other_thread_in_error;
1094       --
1095     END IF;
1096     --
1097     -- If any other exception is encountered, stop processing.
1098     --
1099     RAISE;
1100     --
1101 END process_range;
1102 --
1103 -- ----------------------------------------------------------------------------
1104 -- 3. Wait for Child Threads to complete Processing
1105 -- Procedure   : WATCH_CHILD_PROCESSES
1106 -- Description : Wait for Child Threads to complete Processing (watch_child_processes)
1107 --               The master thread should not finish processing until all the
1108 --               child threads have finished processing. This procedure keeps a
1109 --               watch on all the child threads that have launched for processing.
1110 --               In case an error in encountered by any of the threads, the
1111 --               p_slave_errored will be returned as TRUE. The procedure also
1112 --               detects warnings raised by any child process, so that the master
1113 --               process also reports a warning
1114 -- ----------------------------------------------------------------------------
1115 --
1116 PROCEDURE watch_child_processes(p_slave_errored out nocopy boolean)
1117 IS
1118   --
1119   l_no_slaves      boolean;
1120   l_poll_loops     pls_integer;
1121   l_sleep          NUMBER;
1122   --
1123   CURSOR c_slaves (c_request_id number) IS
1124   SELECT phase_code,
1125          status_code
1126   FROM   fnd_concurrent_requests fnd
1127   WHERE  fnd.request_id = c_request_id;
1128   --
1129   l_slaves c_slaves%rowtype;
1130   --
1131 BEGIN
1132   --
1133   dbg('Inside watch_child_processes');
1134   dbg(g_child_request_tab.count||' threads to watch for');
1135   --
1136   -- watch the child threads only if the process has spawned threads
1137   -- otherwise return
1138   --
1139   IF g_child_request_tab.count > 0 then
1140     --
1141     l_no_slaves := TRUE;
1142     --
1143     WHILE l_no_slaves LOOP
1144       --
1145       l_no_slaves := false;
1146       --
1147       FOR elenum in 1..g_child_request_tab.count LOOP
1148         --
1149         -- Open the cursor to determine if the request has completed processing
1150         --
1151         OPEN  c_slaves (g_child_request_tab(elenum));
1152         FETCH c_slaves into l_slaves;
1153         CLOSE c_slaves;
1154         --
1155         IF l_slaves.phase_code <> 'C' THEN
1156           --
1157           l_no_slaves := TRUE;
1158           --
1159         END IF;
1160         --
1161         IF l_slaves.status_code = 'E' THEN
1162           --
1163           p_slave_errored    :=  TRUE;
1164           g_error_request_id :=  g_child_request_tab(elenum);
1165           --
1166         ELSIF l_slaves.status_code = 'G' THEN
1167           --
1168           dbg('Warning raised by request '||g_child_request_tab(elenum));
1169           --
1170           g_errbuf   := 'WARNING';
1171           g_retcode  := 1;
1172           --
1173         END IF;
1174         --
1175       END LOOP;
1176       --
1177       -- Pause to avoid over polling of fnd_concurrent_requests
1178       --
1179       l_sleep := dbms_pipe.receive_message
1180                   (pipename => 'non-existant pipe to force timeout',
1181                    timeout  => 10);
1182       --
1183     END LOOP;
1184     --
1185   END IF;
1186   --
1187   dbg('Exiting watch_child_processes');
1188   --
1189 EXCEPTION
1190   WHEN OTHERS THEN
1191     dbg('error in watch_child_processes'||SQLERRM);
1192     RAISE;
1193 END watch_child_processes;
1194 --
1195 -- ----------------------------------------------------------------------------
1196 -- 4. End Multithreading Process
1197 -- Procedure   : WATCH_CHILD_PROCESSES
1198 -- Description : End Multithreading Process (end_multithreading) This procedure
1199 --               calls the post-processor to wind up the collection processing
1200 --               and updates status of the action record to PROCESSED
1201 -- ----------------------------------------------------------------------------
1202 --
1203 PROCEDURE end_multithreading
1204 IS
1205   --
1206   --
1207 BEGIN
1208   --
1209   dbg('Inside end_multithreading');
1210   --
1211   -- 4.1 Run the post process
1212   --
1213   run_program_post_process;
1214   --
1215   -- 4.2 Update Multithread Action Status
1216   --
1217   set_action_status;
1218   --
1219   dbg('Exiting end_multithreading');
1220   --
1221 EXCEPTION
1222   --
1223   WHEN invalid_program THEN
1224     --
1225     dbg('Invalid program name passsed to the post process='||g_program);
1226     fnd_message.set_name('HRI','HRI_MLT_INVALID_PGM_NAME');
1227     raise invalid_program;
1228     --
1229   WHEN others THEN
1230     --
1231     dbg('Exception encountered in running the post processing');
1232     dbg(sqlerrm);
1233     RAISE;
1234     --
1235 END end_multithreading;
1236 --
1237 -- ----------------------------------------------------------------------------
1238 -- 4.1 Run Program Post-Processor
1239 -- Procedure   : RUN_PROGRAM_POST_PROCESS
1240 -- Description : This procdure invokes the post_process of the collection
1241 --               program
1242 -- ----------------------------------------------------------------------------
1243 --
1244 PROCEDURE run_program_post_process
1245 IS
1246   --
1247   l_sql               VARCHAR2(1000);
1248   --
1249 BEGIN
1250   --
1251   dbg('inside run_program_post_process');
1252   --
1253   -- Build the dynamic SQL
1254   --
1255   l_sql := 'BEGIN '||g_program ||'.POST_PROCESS('||g_mthd_action_id||'); END;';
1256   --
1257   dbg('post processing sql is ='||l_sql );
1258   --
1259   EXECUTE IMMEDIATE l_sql;
1260   --
1261   dbg('exiting run_program_post_process');
1262   --
1263 END run_program_post_process;
1264 --
1265 -- ----------------------------------------------------------------------------
1266 -- 4.2 Update Multithread Action Status
1267 -- Procedure   : SET_ACTION_STATUS
1268 -- Description : This procdure marks the status of the action record to
1269 --               processed
1270 -- ----------------------------------------------------------------------------
1271 --
1272 PROCEDURE set_action_status
1273 IS
1274 BEGIN
1275   --
1276   dbg('inside set_action_status');
1277   --
1278   -- Update the status of the mulithread action as processed
1279   --
1280   UPDATE hri_adm_mthd_actions
1281   SET    end_time        = SYSDATE,
1282          status          = 'PROCESSED'
1283   WHERE  mthd_action_id  = g_mthd_action_id;
1284   --
1285   dbg('exiting set_action_status');
1286   --
1287 END set_action_status;
1288 --
1289 -- ----------------------------------------------------------------------------
1290 -- 7.1.1         PROCESS
1291 -- Procedure   : PROCESS
1292 -- Description : Main Entry Point for the Multithreading Utility. The concurrent
1293 --               program will invoke this program for processing. The procedure
1294 --               puts all the parameters passed to it into an array and calls the
1295 --               overloaded PROCESS procedure
1296 -- ----------------------------------------------------------------------------
1297 --
1298 PROCEDURE process  (errbuf                             OUT NOCOPY  VARCHAR2,
1299                     retcode                            OUT NOCOPY  NUMBER,
1300                     p_program                       IN             VARCHAR2,
1301                     p_business_group_id             IN             NUMBER,
1302                     p_collect_from_date             IN             VARCHAR2,
1303                     p_collect_to_date               IN             VARCHAR2,
1304                     p_full_refresh_flag             IN             VARCHAR2,
1305                     p_hierarchical_process          IN             VARCHAR2 DEFAULT 'N',
1306                     p_hierarchical_type             IN             VARCHAR2 DEFAULT NULL,
1307                     p_attribute1                    IN             VARCHAR2 DEFAULT NULL,
1308                     p_attribute2                    IN             VARCHAR2 DEFAULT NULL,
1309                     p_attribute3                    IN             VARCHAR2 DEFAULT NULL,
1310                     p_attribute4                    IN             VARCHAR2 DEFAULT NULL,
1311                     p_attribute5                    IN             VARCHAR2 DEFAULT NULL,
1312                     p_attribute6                    IN             VARCHAR2 DEFAULT NULL,
1313                     p_attribute7                    IN             VARCHAR2 DEFAULT NULL,
1314                     p_attribute8                    IN             VARCHAR2 DEFAULT NULL,
1315                     p_attribute9                    IN             VARCHAR2 DEFAULT NULL,
1316                     p_attribute10                   IN             VARCHAR2 DEFAULT NULL,
1317                     p_attribute11                   IN             VARCHAR2 DEFAULT NULL,
1318                     p_attribute12                   IN             VARCHAR2 DEFAULT NULL,
1319                     p_attribute13                   IN             VARCHAR2 DEFAULT NULL,
1320                     p_attribute14                   IN             VARCHAR2 DEFAULT NULL,
1321                     p_attribute15                   IN             VARCHAR2 DEFAULT NULL,
1322                     p_attribute16                   IN             VARCHAR2 DEFAULT NULL,
1323                     p_attribute17                   IN             VARCHAR2 DEFAULT NULL,
1324                     p_attribute18                   IN             VARCHAR2 DEFAULT NULL,
1325                     p_attribute19                   IN             VARCHAR2 DEFAULT NULL,
1326                     p_attribute20                   IN             VARCHAR2 DEFAULT NULL)
1327 IS
1328   --
1329   l_slave_errored          BOOLEAN;
1330   l_hr_installed           VARCHAR2(30); -- Stores HR installed or not
1331   l_frc_shrd_hr_prfl_val   VARCHAR2(30); -- store Profile HRI:DBI Force Foundation HR Processes
1332   l_message                fnd_new_messages.message_text%type;
1333   --
1334 BEGIN
1335   dbg('Starting HRI Multithreading Utility');
1336   --
1337   -- Debugging on the process is enabled by profile HRI:Enable Detailed Logging
1338   --
1339   g_program := p_program;
1340   --
1341   g_mthd_action_array.program                   :=  p_program;
1342   g_mthd_action_array.collect_from_date         :=  fnd_date.canonical_to_date(p_collect_from_date);
1343   g_mthd_action_array.collect_to_date           :=  fnd_date.canonical_to_date(p_collect_to_date);
1344   g_mthd_action_array.business_group_id         :=  p_business_group_id;
1345   g_mthd_action_array.full_refresh_flag         :=  p_full_refresh_flag;
1346   g_mthd_action_array.hierarchical_process_flag :=  p_hierarchical_process;
1347   g_mthd_action_array.hierarchical_process_type :=  p_hierarchical_type;
1348   g_mthd_action_array.attribute1                :=  p_attribute1;
1349   g_mthd_action_array.attribute2                :=  p_attribute2;
1350   g_mthd_action_array.attribute3                :=  p_attribute3;
1351   g_mthd_action_array.attribute4                :=  p_attribute4;
1352   g_mthd_action_array.attribute5                :=  p_attribute5;
1353   g_mthd_action_array.attribute6                :=  p_attribute6;
1354   g_mthd_action_array.attribute7                :=  p_attribute7;
1355   g_mthd_action_array.attribute8                :=  p_attribute8;
1356   g_mthd_action_array.attribute9                :=  p_attribute9;
1357   g_mthd_action_array.attribute10               :=  p_attribute10;
1358   g_mthd_action_array.attribute11               :=  p_attribute11;
1359   g_mthd_action_array.attribute12               :=  p_attribute12;
1360   g_mthd_action_array.attribute13               :=  p_attribute13;
1361   g_mthd_action_array.attribute14               :=  p_attribute14;
1362   g_mthd_action_array.attribute15               :=  p_attribute15;
1363   g_mthd_action_array.attribute16               :=  p_attribute16;
1364   g_mthd_action_array.attribute17               :=  p_attribute17;
1365   g_mthd_action_array.attribute18               :=  p_attribute18;
1366   g_mthd_action_array.attribute19               :=  p_attribute19;
1367   g_mthd_action_array.attribute20               :=  p_attribute20;
1368   g_mthd_action_array.status                    :=  'PROCESSING';
1369   g_mthd_action_array.start_time                :=  SYSDATE;
1370   --
1371   -- Call the overridden procedure to start the processing
1372   --
1373   process;
1374   --
1375   -- In case a child thread end with a warning or error, the
1376   -- master thread should end with a similiar note
1377   --
1378   IF (g_errbuf IS NOT NULL) THEN
1379     errbuf  := g_errbuf;
1380     retcode := NVL(g_retcode,0);
1381   END IF;
1382   --
1383   dbg('Exiting HRI Multithreading Utility');
1384   --
1385 EXCEPTION
1386   WHEN invalid_program OR
1387        other_thread_in_error OR
1388        error_launching_thread OR
1389        invalid_sql
1390   THEN
1391     --
1392     -- These are standard errors so don't raise an exception
1393     --
1394     errbuf  := NVL(g_errbuf,'HRI_MTHD_THREAD_IN_ERR');
1395     retcode := 2;
1396     --
1397     -- The master process should not complete before the child process
1398     -- have finished
1399     --
1400     watch_child_processes(l_slave_errored);
1401     --
1402     l_message := nvl(fnd_message.get,sqlerrm);
1403     --
1404     output(l_message);
1405     --
1406     -- Bug 4105868: Collection Diagnostics
1407     --
1408     hri_bpl_conc_log.log_process_info
1409             (p_msg_type      => 'ERROR'
1410             ,p_package_name  => p_program
1411             ,p_msg_group     => 'MLTTHRDNG'
1412             ,p_msg_sub_group => 'PROCESS'
1413             ,p_sql_err_code  => SQLCODE
1414             ,p_note          => l_message
1415             );
1416     --
1417     hri_bpl_conc_log.log_process_end
1418             (p_status         => FALSE
1419             ,p_period_from    => hr_general.start_of_time
1420             ,p_period_to      => hr_general.end_of_time
1421             ,p_package_name   => p_program
1422             );
1423     --
1424   WHEN others THEN
1425     --
1426     -- Unknown exception encountered, so raise an error
1427     --
1428     errbuf  := NVL(g_errbuf,SQLERRM);
1429     retcode := 2;
1430     --
1431     -- The master process should not complete before the child process
1432     -- have finished
1433     --
1434     watch_child_processes(l_slave_errored);
1435     --
1436     l_message := nvl(fnd_message.get,sqlerrm);
1437     --
1438     output(l_message);
1439     --
1440     -- Bug 4105868: Collection Diagnostics
1441     --
1442     hri_bpl_conc_log.log_process_info
1443             (p_msg_type      => 'ERROR'
1444             ,p_package_name  => p_program
1445             ,p_msg_group     => 'MLTTHRDNG'
1446             ,p_msg_sub_group => 'PROCESS'
1447             ,p_sql_err_code  => SQLCODE
1448             ,p_note          => l_message
1449             );
1450     --
1451     hri_bpl_conc_log.log_process_end
1452             (p_status         => FALSE
1453             ,p_period_from    => hr_general.start_of_time
1454             ,p_period_to      => hr_general.end_of_time
1455             ,p_package_name   => p_program
1456             );
1457     --
1458 END process;
1459 --
1460 -- ----------------------------------------------------------------------------
1461 -- 7.1.2 Get Next Range
1462 -- Procedure   : GET_NEXT_RANGE
1463 -- Description : This process acts as an interface between the child process
1464 --               and the range table. The process performs the following task
1465 --               1) Provide the process with the next Unprocessed object range.
1466 --               2) Maintain the status of object ranges that are currently being
1467 --                  processed by the processes.
1468 --               3) Update the status of the object range that has been processed
1469 --                  by the child process.
1470 --               4) In case a child process encounters an error, it returns the
1471 --                  error code of 1 to the requesting process. The child process
1472 --                  should then stop processing and error out.
1473 -- ----------------------------------------------------------------------------
1474 --
1475 PROCEDURE get_next_range(p_mthd_action_id          IN            NUMBER
1476                         ,p_mthd_range_id           IN OUT NOCOPY NUMBER
1477                         ,p_mthd_range_lvl             OUT NOCOPY NUMBER
1478                         ,p_mthd_range_lvl_order       OUT NOCOPY NUMBER
1479                         ,p_start_object_id            OUT NOCOPY NUMBER
1480                         ,p_end_object_id              OUT NOCOPY NUMBER
1481                         ,p_mode                    IN            VARCHAR2 default 'N')
1482 IS
1483   --
1484   l_error_status                       NUMBER;
1485   --
1486   CURSOR  c_error IS
1487   SELECT  -1
1488   FROM    hri_adm_mthd_ranges
1489   WHERE   status         = 'ERROR'
1490   AND     mthd_action_id = p_mthd_action_id
1491   AND     rownum = 1;
1492   --
1493 BEGIN
1494   --
1495   -- If the p_mthd_range_id will store the previous range id, which has been processed
1496   -- by the child thread, so mark that range as PROCESSED
1497   --
1498   IF p_mthd_range_id is not null THEN
1499     --
1500     UPDATE hri_adm_mthd_ranges
1501     SET    status            = 'PROCESSED'
1502     WHERE  status            = 'PROCESSING'
1503     AND    mthd_action_id    = p_mthd_action_id
1504     AND    mthd_range_id     = p_mthd_range_id;
1505     --
1506     p_mthd_range_id := null;
1507     --
1508   END IF;
1509   --
1510   -- Get the next available range for processing
1511   --
1512   UPDATE hri_adm_mthd_ranges
1513   SET status = 'PROCESSING',
1514       request_id = fnd_global.conc_request_id
1515   WHERE status = 'UNPROCESSED'
1516   AND mthd_action_id = p_mthd_action_id
1517   AND mthd_range_id =
1518     (SELECT MIN(sub.mthd_range_id)
1519      FROM hri_adm_mthd_ranges sub
1520      WHERE sub.status = 'UNPROCESSED'
1521      AND sub.mthd_action_id = p_mthd_action_id)
1522   RETURNING
1523    mthd_range_id
1524   ,mthd_range_lvl
1525   ,mthd_range_lvl_order
1526   ,start_object_id
1527   ,end_object_id
1528   INTO
1529    p_mthd_range_id
1530   ,p_mthd_range_lvl
1531   ,p_mthd_range_lvl_order
1532   ,p_start_object_id
1533   ,p_end_object_id;
1534   --
1535   -- Commit the transactions here, this will ensure when a error is
1536   -- encountered by some other thread, the processesing done by
1537   -- the current thread are commited before an error is raised.
1538   --
1539   COMMIT;
1540   --
1541   --
1542   -- If no range is available for processing, then it could be that
1543   -- a child thread encountered an error. In which, case an
1544   -- exception should be raised and the processing should stop there.
1545   --
1546   IF p_mthd_range_id is null THEN
1547     --
1548     OPEN  c_error;
1549     FETCH c_error INTO l_error_status;
1550     CLOSE c_error;
1551     --
1552     IF l_error_status = -1 THEN
1553       --
1554        p_mthd_range_id := -1;
1555        p_start_object_id := -1;
1556        p_end_object_id   := -1;
1557        --
1558        -- raise an exception
1559        --
1560        dbg('error encountered in get_next_range, other_process_failure');
1561        raise_application_error(- 20999,'other_process_failure');
1562       --
1563     END IF;
1564     --
1565   END IF;
1566   --
1567 END get_next_range;
1568 --
1569 -- ----------------------------------------------------------------------------
1570 -- 7.1.3 Set Range in Error
1571 -- Procedure   : SET_ERROR_STATUS
1572 -- Description : This procedure will be invoked by the child thread that
1573 --               encountered an error. It updates the marks the action and
1574 --               unprocessed ranges as Error
1575 -- ----------------------------------------------------------------------------
1576 --
1577 PROCEDURE set_range_error(p_mthd_action_id NUMBER)
1578 IS
1579   --
1580   --
1581 BEGIN
1582   dbg('Inside set_range_error');
1583   --
1584   -- Mark the object range as Error
1585   --
1586   UPDATE hri_adm_mthd_ranges
1587   SET    status    =  'ERROR',
1588          err_thread_request_id = fnd_global.conc_request_id
1589   WHERE  mthd_action_id = p_mthd_action_id
1590   --
1591   -- The records which have not been processed
1592   --
1593   AND    (status = 'UNPROCESSED' OR
1594   --
1595   -- The records which which was being processed by the thread, when the error
1596   -- was raised
1597   --
1598           (status = 'PROCESSING'
1599            AND request_id = fnd_global.conc_request_id));
1600   --
1601   dbg(sql%rowcount||' range records marked as Error');
1602   --
1603   -- Change the status action record to ERROR
1604   --
1605   UPDATE hri_adm_mthd_actions
1606   SET    status    =  'ERROR'
1607   WHERE  mthd_action_id = p_mthd_action_id;
1608   --
1609   commit;
1610   --
1611   dbg('exiting set_range_error');
1612   --
1613 END set_range_error;
1614 --
1615 -- ----------------------------------------------------------------------------
1616 -- 7.1.5 Process Range (Entry Point for Child thread processes)
1617 -- Procedure   : PROCESS_RANGE
1618 -- Description : This procedure is called by the concurrent manager. It control
1619 --               the child thread processing. It invokes the overloaded
1620 --               process_range procedure
1621 -- ----------------------------------------------------------------------------
1622 --
1623 PROCEDURE process_range(
1624                         errbuf                        OUT NOCOPY VARCHAR2
1625                         ,retcode                      OUT NOCOPY NUMBER
1626                         ,p_master_request_id      IN             NUMBER
1627                         ,p_program                IN             VARCHAR2
1628                         ,p_mthd_action_id         IN             NUMBER
1629                         ,p_worker_id              IN             NUMBER)
1630 IS
1631   --
1632   l_dyn_sql varchar2(4000);
1633   --
1634 BEGIN
1635   --
1636   g_mthd_action_array := get_mthd_action_array(p_mthd_action_id);
1637   --
1638   process_range(p_program         => p_program
1639                 ,p_mthd_action_id => p_mthd_action_id
1640                 ,p_errbuf         => errbuf
1641                 ,p_retcode        => retcode);
1642   --
1643 EXCEPTION
1644   WHEN invalid_program OR other_thread_in_error THEN
1645     --
1646     -- These are standard errors so don't raise an exception
1647     --
1648     errbuf  := NVL(g_errbuf,SQLERRM);
1649     retcode := NVL(g_retcode,2);
1650     output(fnd_message.get);
1651     --
1652     -- Bug 4105868: Collection Diagnostics
1653     --
1654     hri_bpl_conc_log.log_process_end
1655             (p_status         => FALSE
1656             ,p_period_from    => hr_general.start_of_time
1657             ,p_period_to      => hr_general.end_of_time
1658             );
1659     --
1660   WHEN others THEN
1661     --
1662     -- Unknown exception encountered, so raise an error
1663     --
1664     dbg('An error encountered while processing');
1665     errbuf  := SQLERRM;
1666     retcode := 2;
1667     output(fnd_message.get);
1668     output(SQLERRM);
1669     --
1670     -- Collection Diagnostics
1671     --
1672     hri_bpl_conc_log.log_process_end
1673             (p_status         => FALSE
1674             ,p_period_from    => hr_general.start_of_time
1675             ,p_period_to      => hr_general.end_of_time
1676             );
1677     --
1678 END process_range;
1679 --
1680 -- ----------------------------------------------------------------------------
1681 -- 7.1.6 Get multithread action array
1682 -- Procedure   : GET_MTHD_ACTION_ARRAY
1683 -- This function can be called by the collection programs to populate
1684 -- the multithreading action arrays
1685 -- ----------------------------------------------------------------------------
1686 --
1687 FUNCTION get_mthd_action_array(p_mthd_action_id  IN NUMBER)
1688 RETURN hri_adm_mthd_actions%rowtype
1689 IS
1690   --
1691   CURSOR c_adm_mthd_action IS
1692   SELECT *
1693   FROM   HRI_ADM_MTHD_ACTIONS
1694   WHERE  mthd_action_id = p_mthd_action_id;
1695   --
1696 BEGIN
1697   --
1698   OPEN   c_adm_mthd_action;
1699   FETCH  c_adm_mthd_action into g_mthd_action_array;
1700   CLOSE  c_adm_mthd_action;
1701   --
1702   -- Populate the method action id global variable.
1703   --
1704   g_mthd_action_id := g_mthd_action_array.mthd_action_id;
1705   --
1706   RETURN g_mthd_action_array;
1707   --
1708 END get_mthd_action_array;
1709 --
1710 -- Fix for bug 4043240
1711 -- ------------------------------------------------------------------------------
1712 -- FUNCTION get_current_mthd_action_id fetches the mthd_action_id for the invoking
1713 -- process. If it does not exist, a new mthd_action_id is created and returned
1714 -- ------------------------------------------------------------------------------
1715 --
1716 FUNCTION get_mthd_action_id(p_program            IN    VARCHAR2,
1717                             p_start_time         IN    DATE)
1718 RETURN NUMBER
1719 IS
1720   --
1721   l_process_name    VARCHAR2(30);
1722   --
1723 BEGIN
1724   --
1725   dbg('In get_mthd_action_id');
1726   --
1727   -- Return the mthd action id of the current process if it is already defined
1728   --
1729   IF g_mthd_action_id IS NOT NULL THEN
1730     --
1731     RETURN g_mthd_action_id;
1732     --
1733   END IF;
1734   --
1735   -- The mthd action id of the current process is not defined.
1736   -- A new record will be created in hri_adm_mthd_actions
1737   -- corresponding to this process
1738   --
1739   -- store the program name and start time of the process
1740   --
1741   g_mthd_action_array.status                 := 'PROCESSING';
1742   g_mthd_action_array.program                :=  p_program;
1743   g_mthd_action_array.start_time             :=  p_start_time;
1744   --
1745   -- Insert the record in the multi threading table
1746   --
1747   gen_multi_thread_action;
1748   --
1749   dbg('Now g_mthd_action_id='||g_mthd_action_id);
1750   --
1751   --
1752   RETURN g_mthd_action_id;
1753   --
1754 END get_mthd_action_id;
1755 --
1756 -- Updates the multi-threading parameters
1757 PROCEDURE update_parameters(p_mthd_action_id     IN NUMBER,
1758                             p_full_refresh       IN VARCHAR2,
1759                             p_global_start_date  IN DATE) IS
1760 
1761 BEGIN
1762 
1763   -- If a full refresh then set the flag and update the
1764   -- collect from date
1765   IF (p_full_refresh = 'Y') THEN
1766 
1767     UPDATE hri_adm_mthd_actions
1768     SET full_refresh_flag = 'Y'
1769        ,collect_from_date = p_global_start_date
1770     WHERE mthd_action_id = p_mthd_action_id;
1771 
1772   -- Otherwise just set the full refresh flag
1773   ELSE
1774 
1775     UPDATE hri_adm_mthd_actions
1776     SET full_refresh_flag = 'N'
1777     WHERE mthd_action_id = p_mthd_action_id;
1778 
1779   END IF;
1780 
1781   -- Commit
1782   commit;
1783 
1784 END update_parameters;
1785 --
1786 -- ------------------------------------------------------------------------------
1787 -- Returns worker id
1788 -- ------------------------------------------------------------------------------
1789 FUNCTION get_worker_id RETURN NUMBER IS
1790 
1791   l_worker_id   NUMBER;
1792   l_argument4   VARCHAR2(240);
1793 
1794   CURSOR worker_csr IS
1795   SELECT
1796    req.argument4
1797   FROM
1798    fnd_concurrent_requests req
1799   ,fnd_concurrent_programs prg
1800   WHERE req.concurrent_program_id = prg.concurrent_program_id
1801   AND req.program_application_id = prg.application_id
1802   AND prg.concurrent_program_name = 'HRI_MTHD_CHILD_PROCESS'
1803   AND req.request_id = fnd_global.conc_request_id;
1804 
1805 BEGIN
1806 
1807   OPEN worker_csr;
1808   FETCH worker_csr INTO l_argument4;
1809   CLOSE worker_csr;
1810 
1811   l_worker_id := to_number(l_argument4);
1812 
1813   RETURN NVL(l_worker_id, 1);
1814 
1815 EXCEPTION WHEN OTHERS THEN
1816 
1817   RETURN 1;
1818 
1819 END get_worker_id;
1820 --
1821 END HRI_OPL_MULTI_THREAD;