DBA Data[Home] [Help]

PACKAGE BODY: APPS.WF_ENGINE_BULK

Source


1 package body WF_ENGINE_BULK as
2 /* $Header: wfengblkb.pls 120.5 2006/02/10 01:13:02 anachatt noship $ */
3 
4 
5 --
6 -- Exception
7 --
8 no_savepoint exception;
9 bad_format   exception;
10 
11 pragma EXCEPTION_INIT(no_savepoint, -1086);
12 pragma EXCEPTION_INIT(bad_format, -6502);
13 
14 resource_busy exception;
15 pragma exception_init(resource_busy, -00054);
16 
17 -- private variable
18 schema varchar2(30);
19 g_execCount number:=0;
20 
21 
22 --
23 --  ConsolidateKeys (PRIVATE)
24 --Condense the sparse itemkey list into a dense one
25 --   IN OUT
26 -- itemkeys
27 -- userkeys
28 -- ownerroles
29 
30 procedure ConsolidateKeys(itemkeys in out NOCOPY WF_ENGINE_BULK.Itemkeytabtype,
31                user_keys  in out NOCOPY WF_ENGINE_BULK.UserKeyTabType ,
32                owner_roles in out NOCOPY WF_ENGINE_BULK.OwnerRoleTabType )
33 is
34 
35 begin
36 
37  if itemkeys is not null and itemkeys.count>0 then
38    for arrIndex in itemkeys.first..itemkeys.last loop
39         if not itemkeys.exists(arrIndex)  then
40           --fill in the gap with the last list element
41           itemkeys(arrIndex):=itemkeys(itemkeys.last);
42 
43           --consolidate user_keys if it is not null
44           if user_keys is not null and user_keys.count>0 then
45             user_keys(arrIndex):=user_keys(user_keys.last);
46            user_keys.delete(user_keys.last);
47           end if;
48           --consolidate owner_roles if it is not null
49           if owner_roles is not null and owner_roles.count > 0 then
50            owner_roles(arrIndex):=owner_roles(owner_roles.last);
51            owner_roles.delete(owner_roles.last);
52           end if;
53             itemkeys.delete(itemkeys.last);
54         end if;
55    end loop;
56  end if;
57 
58 exception
59  when others then
60         raise;
61 end ConsolidateKeys;
62 
63 
64 --
65 -- Current_Schema (PRIVATE)
66 --   Return the current schema
67 --
68 function Current_Schema
69 return varchar2
70 is
71 begin
72   if (wf_engine_bulk.schema is null) then
73     select sys_context('USERENV','CURRENT_SCHEMA')
74       into wf_engine_bulk.schema
75       from sys.dual;
76   end if;
77   return wf_engine_bulk.schema;
78 exception
79   when OTHERS then
80     Wf_Core.Context('Wf_Engine_Bulk', 'Current_Schema');
81     raise;
82 end Current_Schema;
83 
84 --
85 -- BulkCreateItems (PRIVATE)
86 --   Create rows in bulk in the WF_ITEMS table with the given item type
87 --   , item key list
88 --   and the root process name.
89 -- IN
90 --   itemtype - item type
91 --   wflow    - root process name for this item.
92 --   actdate  - active date of item
93 --   user
94 --
95 procedure BulkCreateItems(
96   itemtype in varchar2,
97   wflow    in varchar2,
98   actdate  in date,
99   user_keys in out NOCOPY wf_engine_bulk.userkeytabtype,
100   owner_roles in out NOCOPY wf_engine_bulk.ownerroletabtype,
101   parent_itemtype in varchar2,
102   parent_itemkey in varchar2,
103   parent_context in varchar2)
104 is
105 
106   rootversion number;
107   itemkeys  wf_engine_bulk.itemkeytabtype;
108 
109 begin
110 
111   rootversion := Wf_Activity.Version(itemtype, wflow, actdate);
112   --initialize the table of itemkeys from the list of successful items.
113   if g_SuccessItems.COUNT=0 then
114     return;
115   else
116   itemkeys:=g_SuccessItems;
117   end if;
118 
119 
120   begin
121   --insert depending on whether user_keys and owner_roles are null or not
122   if  user_keys.count > 0 and owner_roles.count > 0 then
123   forall arrInd in itemkeys.FIRST..itemkeys.LAST  SAVE EXCEPTIONS
124     -- NORMAL: Insert new item and attributes directly in the db
125     insert into WF_ITEMS (
126       ITEM_TYPE,
127       ITEM_KEY,
128       ROOT_ACTIVITY,
129       ROOT_ACTIVITY_VERSION,
130       OWNER_ROLE,
131       PARENT_ITEM_TYPE,
132       PARENT_ITEM_KEY,
133       PARENT_CONTEXT,
134       BEGIN_DATE,
135       END_DATE,
136       USER_KEY
137     ) values (
138       itemtype,
139       itemkeys(arrInd),
140       wflow,
141       rootversion,
142       owner_roles(arrInd),
143       parent_itemtype,
144       parent_itemkey,
145       parent_context,
146       actdate,
147       to_date(NULL),
148       user_keys(arrInd)
149     );
150   elsif user_keys.count>0 and owner_roles.count=0 then
151   forall arrInd in itemkeys.FIRST..itemkeys.LAST  SAVE EXCEPTIONS
152      insert into WF_ITEMS (
153       ITEM_TYPE,
154       ITEM_KEY,
155       ROOT_ACTIVITY,
156       ROOT_ACTIVITY_VERSION,
157       PARENT_ITEM_TYPE,
158       PARENT_ITEM_KEY,
159       PARENT_CONTEXT,
160       BEGIN_DATE,
161       END_DATE,
162       USER_KEY
163     ) values (
164       itemtype,
165       itemkeys(arrInd),
166       wflow,
167       rootversion,
168       parent_itemtype,
169       parent_itemkey,
170       parent_context,
171       actdate,
172       to_date(NULL),
173       user_keys(arrInd)
174     );
175   elsif owner_roles.count>0 and user_keys.count=0 then
176   forall arrInd in itemkeys.FIRST..itemkeys.LAST  SAVE EXCEPTIONS
177      insert into WF_ITEMS (
178       ITEM_TYPE,
179       ITEM_KEY,
180       ROOT_ACTIVITY,
181       ROOT_ACTIVITY_VERSION,
182       OWNER_ROLE,
183       PARENT_ITEM_TYPE,
184       PARENT_ITEM_KEY,
185       PARENT_CONTEXT,
186       BEGIN_DATE,
187       END_DATE
188     ) values (
189       itemtype,
190       itemkeys(arrInd),
191       wflow,
192       rootversion,
193       owner_roles(arrInd),
194       parent_itemtype,
195       parent_itemkey,
196       parent_context,
197       actdate,
198       to_date(NULL)
199     );
200   elsif owner_roles.count=0 and user_keys.count=0 then
201   forall arrInd in itemkeys.FIRST..itemkeys.LAST  SAVE EXCEPTIONS
202      insert into WF_ITEMS (
203       ITEM_TYPE,
204       ITEM_KEY,
205       ROOT_ACTIVITY,
206       ROOT_ACTIVITY_VERSION,
207       PARENT_ITEM_TYPE,
208       PARENT_ITEM_KEY,
209       PARENT_CONTEXT,
210       BEGIN_DATE,
211       END_DATE
212     ) values (
213       itemtype,
214       itemkeys(arrInd),
215       wflow,
216       rootversion,
217       parent_itemtype,
218       parent_itemkey,
219       parent_context,
220       actdate,
221       to_date(NULL)
222     );
223   end if;
224   exception
225      when others then
226 
227        if SQL%BULK_EXCEPTIONS.COUNT>0 then
228         for ExceptionInd in 1..SQL%BULK_EXCEPTIONS.COUNT loop
229 
230            g_failedItems(g_failedItems.COUNT+1):=
231               itemKeys(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
232            itemkeys.DELETE(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
233            if user_keys is not null and user_keys.count>0 then
234             user_keys.DELETE(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
235            end if;
236            if owner_roles is not null and owner_roles.count>0 then
237             owner_roles.DELETE(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
238            end if;
239         end loop;
240         -- condense the PL/SQL tables to eliminate sparse indices
241         ConsolidateKeys(ItemKeys,User_Keys,Owner_Roles);
242        end if;
243    end;
244 
245   if itemkeys.count > 0 AND (not wf_item.Attribute_On_Demand(itemtype, itemkeys(itemkeys.FIRST))) then
246   begin
247   -- Initialize item attributes in bulk
248 
249   --if its on demand, we are not going to do this
250   forall arrInd in itemkeys.FIRST..itemkeys.LAST save exceptions
251 
252     -- NORMAL: store attributes in table
253     insert into WF_ITEM_ATTRIBUTE_VALUES (
254       ITEM_TYPE,
255       ITEM_KEY,
256       NAME,
257       TEXT_VALUE,
258       NUMBER_VALUE,
259       DATE_VALUE
260     ) select
261       itemtype,
262       itemkeys(arrInd),
263       WIA.NAME,
264       WIA.TEXT_DEFAULT,
265       WIA.NUMBER_DEFAULT,
266       WIA.DATE_DEFAULT
267     from WF_ITEM_ATTRIBUTES WIA
268     where WIA.ITEM_TYPE = itemtype;
269 
270     exception
271      when others then
272        if SQL%BULK_EXCEPTIONS.COUNT>0 then
273         for ExceptionInd in 1..SQL%BULK_EXCEPTIONS.COUNT loop
274            g_failedItems(g_failedItems.COUNT+1):=
275                   itemKeys(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
276            itemkeys.DELETE(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
277            user_keys.DELETE(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
278            owner_roles.DELETE(SQL%BULK_EXCEPTIONS(ExceptionInd).ERROR_INDEX);
279         end loop;
280         ConsolidateKeys(ItemKeys,User_Keys,Owner_Roles);
281       end if;
282    end;
283   end if;
284    -- reset the SuccessItems table to reflect the successfully created items.
285    g_SuccessItems:=itemkeys;
286 
287 end BulkCreateItems;
288 
289 
290 --
291 -- BulkAddItemAttr (PRIVATE)
292 --   Add a new unvalidated run-time item attribute for a list of itemkeys.
293 -- IN:
294 --   itemtype - item type
295 
296 --   aname - attribute name
297 --   text_value   - add text value to it if provided.
298 --   number_value - add number value to it if provided.
299 --   date_value   - add date value to it if provided.
300 -- NOTE:
301 --   The new attribute has no type associated.  Get/set usages of the
302 --   attribute must insure type consistency.
303 --
304 procedure BulkAddItemAttr(itemtype in varchar2,
305                       aname in varchar2,
306                       text_values in wf_engine.textTabTyp)
307 is
308   bulkException boolean := FALSE;
309   l_itemkeys WF_ENGINE_BULK.itemkeytabtype;
310   l_ukeys WF_ENGINE_BULK.UserKeyTabType;
311   l_rkeys  WF_ENGINE_BULK.OwnerRoleTabType;
312 begin
313   -- Check Arguments
314   if ((itemtype is null) or
315       (aname is null)) then
316     Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
317     Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
318     Wf_Core.Raise('WFSQL_ARGS');
319 
320   end if;
321   If g_SuccessItems.count=0 then
322     return;
323   end if;
324   l_itemkeys:=g_SuccessItems;
325 
326   --bulk insert the new attribute for all the itemkeys in the list
327   begin
328 
329   forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST SAVE EXCEPTIONS
330     insert into WF_ITEM_ATTRIBUTE_VALUES (
331       ITEM_TYPE,
332       ITEM_KEY,
333       NAME,
334       TEXT_VALUE
335     ) values (
336       itemtype,
337       l_itemkeys(arrInd),
338       aname,
339       BulkAddItemAttr.text_values(arrInd)
340     );
341 
342   exception
343    when others then
344       if SQL%BULK_EXCEPTIONS.COUNT>0 then
345        bulkException:= true;
346        -- load the failedItems table with the list of errored itemkeys,
347        -- and remove them from the itemkeys table
348        for failIndex in 1..SQL%BULK_EXCEPTIONS.COUNT loop
349          g_FailedItems(g_FailedItems.COUNT+1):=
350                 l_itemkeys(SQL%BULK_EXCEPTIONS(failIndex).ERROR_INDEX);
351          l_itemkeys.DELETE(SQL%BULK_EXCEPTIONS(failIndex).ERROR_INDEX);
352        end loop;
353       end if;
354   end;
355   -- load successfully added itemkeys to the SuccessItems Table.
356   if  ( bulkException) then
357    ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
358   end if;
359   g_SuccessItems:=l_itemkeys;
360 exception
361   when others then
362        raise;
363 end BulkAddItemAttr;
364 
365 
366 
367 
368 --
369 -- SetItemAttrText (PUBLIC)
370 --   Set the values of an array of text item attribute.
371 --   Unlike SetItemAttrText(), it stores the values directly.
372 -- IN:
373 --   itemtype - Item type
374 --   itemkey - Item key
375 --   aname - Array of Names
376 --   avalue - Array of New values for attribute
377 --
378 procedure SetItemAttrText (
379   itemtype in varchar2,
380   itemkeys  in Wf_Engine_Bulk.ItemKeyTabType,
381   anames   in Wf_Engine.NameTabTyp,
382   avalues  in Wf_Engine.TextTabTyp)
383 is
384   status      pls_integer;
385   arrayIndex  pls_integer;
386   j           pls_integer;
387   wiavIND     number;
388   success_cnt number;
389   match       boolean;
390   succAttrUpdates Wf_Engine.NameTabTyp;
391   succItemUpdates Wf_Engine_Bulk.ItemKeyTabType;
392 
393 begin
394   -- Check Arguments
395   if (itemtype is null) then
396     Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
397     Wf_Core.Raise('WFSQL_ARGS');
398   end if;
399 
400   if (anames.COUNT = 0 or avalues.COUNT = 0) then
401     -- Do not do anything if index table is empty.
402     return;
403 
404   elsif ((anames.LAST <> avalues.LAST or anames.COUNT <> avalues.COUNT)
405       or (anames.LAST<> itemkeys.LAST or anames.COUNT<> itemkeys.COUNT)) then
406     -- Raise an error if the two index tables do not end at the same index
407     -- or do not have the same number of elements.
408     g_failedItems:=itemkeys;
409     g_failedAttributes:=anames;
410     Wf_Core.Raise('WFENG_BLK_ITM_ATTRARR_MISMATCH');
411   end if;
412 
413   --If itemtype is on demand, we will insert default values if not exists already
414   if wf_item.Attribute_On_Demand(itemtype, itemkeys(itemkeys.FIRST)) then
415     begin
416       forall arrayIndex in itemkeys.FIRST..itemkeys.LAST save exceptions
417       insert into WF_ITEM_ATTRIBUTE_VALUES
418         (ITEM_TYPE, ITEM_KEY, NAME, TEXT_VALUE)
419       select itemtype, itemkeys(arrayIndex), anames(arrayIndex), text_default
420         from WF_ITEM_ATTRIBUTES WIA
421         where
422           WIA.ITEM_TYPE = itemtype
423         and
424           WIA.NAME = anames(arrayIndex)
425         and not exists (select 1 from WF_ITEM_ATTRIBUTE_VALUES WIAV
426           where WIAV.item_type=itemtype
427           and  WIAV.item_key=itemkeys(arrayIndex)
428           and  WIAV.NAME=anames(arrayIndex));
429     exception
430         when others then
431           null; -- ignore failures here
432     end;
433   end if;
434 
435   -- Reset the table of Failed Items and Failed Attributes
436   g_FailedItems.DELETE;
437   g_FailedAttributes.DELETE;
438   -- Set the text value.
439   begin
440    forall arrInd in itemkeys.FIRST..itemkeys.LAST save exceptions
441       update WF_ITEM_ATTRIBUTE_VALUES set
442         TEXT_VALUE = avalues(arrInd)
443       where ITEM_TYPE = itemtype
444       and ITEM_KEY = itemkeys(arrInd)
445       and NAME = anames(arrInd)
446      returning item_key,name bulk collect into succItemUpdates, succAttrUpdates;
447 
448   exception
449       when others then
450         if sql%bulk_exceptions.count>0 then
451          for ErrorIndex in 1..sql%bulk_exceptions.count loop
452           g_failedItems(g_FailedItems.COUNT+1):=
453             itemkeys(sql%bulk_exceptions(ErrorIndex).error_index);
454           g_failedAttributes(g_FailedAttributes.COUNT+1):=
455             anames(sql%bulk_exceptions(ErrorIndex).error_index);
456          end loop;
457         end if;
458    end;
459 
460    if (succItemUpdates.count <> itemkeys.COUNT) then
461    -- determine the failed item/atrributes
462       if succItemUpdates.count=0 then
463           g_failedItems:=itemkeys;
464           g_failedAttributes:=anames;
465       else
466 
467        for i in itemkeys.first..itemkeys.last loop
468 
469          if ((itemkeys.count - i + 1) = succItemUpdates.count) then
470           -- we are done, the rest are all successful
471           exit;
472          end if;
473          match := false;
474          j:=succItemUpdates.first;
475          while j <= succItemUpdates.last loop
476           if anames(i) = succAttrUpdates(j)
477              and itemkeys(i) = succItemUpdates(j) then
478 
479            -- remove the item/attr from the success table
480            succItemUpdates.delete(j);
481            succAttrUpdates.delete(j);
482            match := true;
483            exit;
484           end if;
485 
486           j:=succItemUpdates.next(j);
487 
488          end loop;
489 
490          if not(match) then
491          -- item has failed.Insert into list of failed items
492           g_failedItems(g_FailedItems.COUNT+1):=itemkeys(i);
493           g_failedAttributes(g_FailedAttributes.COUNT+1):=anames(i);
494 
495          end if;
496        end loop;
497       end if; --successUpdates count=0
498    end if; --sucessUpdates doesnot match item count
499 
500 
501   if g_failedItems.COUNT >0 then
502     WF_CORE.TOKEN('ITEMTYPE',itemtype);
503     WF_CORE.TOKEN('TOTAL',to_char(itemkeys.count));
504     WF_CORE.TOKEN('FAILED',to_char(g_FailedItems.COUNT));
505     WF_CORE.Raise('WFENG_BULK_SETATTR');
506   end if;
507 
508 exception
509   when others then
510     Wf_Core.Context('Wf_Engine_Bulk', 'SetItemAttrText', itemtype);
511     raise;
512 end SetItemAttrText;
513 
514 
515 --
516 -- SetItemAttrNumber (PUBLIC)
517 --   Set the value of an array of number item attribute.
518 --   Attribute must be a NUMBER-type attribute.
519 -- IN:
520 --   itemtype - Item type
521 --   itemkey - Item key
522 --   aname - Array of Names
523 --   avalue - Array of new value for attribute
524 --
525 procedure SetItemAttrNumber(
526   itemtype in varchar2,
527   itemkeys  in Wf_Engine_Bulk.ItemKeyTabType,
528   anames    in Wf_Engine.NameTabTyp,
529   avalues   in Wf_Engine.NumTabTyp)
530 is
531   arrayIndex  pls_integer;
532   status      pls_integer;
533   wiavIND     number;
534   success_cnt number;
535   j           pls_integer;
536   match       boolean;
537   succAttrUpdates Wf_Engine.NameTabTyp;
538   succItemUpdates Wf_Engine_Bulk.ItemKeyTabType;
539 
540 begin
541   -- Check Arguments
542   if (itemtype is null) then
543     Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
544     Wf_Core.Raise('WFSQL_ARGS');
545 
546   elsif (anames.COUNT = 0 or avalues.COUNT = 0) then
547     -- Do not do anything if index table is empty.
548     return;
549 
550   elsif ((anames.LAST <> avalues.LAST or anames.COUNT <> avalues.COUNT)
551       or (anames.LAST<> itemkeys.LAST or anames.COUNT<> itemkeys.COUNT)) then
552     -- Raise an error if the two index tables do not end at the same index
553     -- or do not have the same number of elements.
554     g_failedItems:=itemkeys;
555     g_failedAttributes:=anames;
556     Wf_Core.Raise('WFENG_BLK_ITM_ATTRARR_MISMATCH');
557   end if;
558   --If itemtype is on demand, we will insert default values if not exists already
559   if wf_item.Attribute_On_Demand(itemtype, itemkeys(itemkeys.FIRST)) then
560     begin
561       forall arrayIndex in itemkeys.FIRST..itemkeys.LAST save exceptions
562       insert into WF_ITEM_ATTRIBUTE_VALUES
563         (ITEM_TYPE, ITEM_KEY, NAME, NUMBER_VALUE)
564       select itemtype, itemkeys(arrayIndex), anames(arrayIndex), number_default
565         from WF_ITEM_ATTRIBUTES WIA
566         where
567           WIA.ITEM_TYPE = itemtype
568         and
569           WIA.NAME = anames(arrayIndex)
570         and not exists (select 1 from WF_ITEM_ATTRIBUTE_VALUES WIAV
571           where WIAV.item_type=itemtype
572           and  WIAV.item_key=itemkeys(arrayIndex)
573           and  WIAV.NAME=anames(arrayIndex));
574     exception
575         when others then
576           null; -- ignore failures here and let update to handle the logic
577     end;
578   end if;
579 
580   -- Reset the table of Failed Items and Failed Attributes
581   g_FailedItems.DELETE;
582   g_failedAttributes.DELETE;
583 
584   -- Set the number value.
585  begin
586   forall arrInd in itemkeys.FIRST..itemkeys.LAST save exceptions
587       update WF_ITEM_ATTRIBUTE_VALUES set
588       NUMBER_VALUE = avalues(arrInd)
589       where ITEM_TYPE = itemtype
590       and ITEM_KEY = itemkeys(arrInd)
591       and NAME = anames(arrInd)
592      returning item_key,name bulk collect into succItemUpdates, succAttrUpdates;
593 
594  exception
595       when others then
596         if sql%bulk_exceptions.count>0 then
597          for ErrorIndex in 1..sql%bulk_exceptions.count loop
598           g_failedItems(g_FailedItems.COUNT+1):=
599             itemkeys(sql%bulk_exceptions(ErrorIndex).error_index);
600           g_failedAttributes(g_FailedAttributes.COUNT+1):=
601             anames(sql%bulk_exceptions(ErrorIndex).error_index);
602          end loop;
603         end if;
604  end;
605 
606    if (succItemUpdates.count <> itemkeys.COUNT) then
607    -- determine the failed item/atrributes
608       if succItemUpdates.count=0 then
609           g_failedItems:=itemkeys;
610           g_failedAttributes:=anames;
611       else
612 
613        for i in itemkeys.first..itemkeys.last loop
614 
615          if ((itemkeys.count - i + 1) = succItemUpdates.count) then
616           -- we are done, the rest are all successful
617           exit;
618          end if;
619          match := false;
620          j:=succItemUpdates.first;
621          while j <= succItemUpdates.last loop
622           if anames(i) = succAttrUpdates(j)
623              and itemkeys(i) = succItemUpdates(j) then
624 
625            -- remove the item/attr from the success table
626            succItemUpdates.delete(j);
627            succAttrUpdates.delete(j);
628            match := true;
629            exit;
630           end if;
631 
632           j:=succItemUpdates.next(j);
633 
634          end loop;
635 
636          if not(match) then
637          -- item has failed.Insert into list of failed items
638           g_failedItems(g_FailedItems.COUNT+1):=itemkeys(i);
639           g_failedAttributes(g_FailedAttributes.COUNT+1):=anames(i);
640          end if;
641        end loop;
642       end if; --successUpdates count=0
643    end if; --sucessUpdates doesnot match item count
644 
645   if g_failedItems.COUNT >0 then
646 
647     WF_CORE.TOKEN('ITEMTYPE',itemtype);
648     WF_CORE.TOKEN('TOTAL',to_char(itemkeys.COUNT));
649     WF_CORE.TOKEN('FAILED',to_char(g_FailedItems.COUNT));
650     WF_CORE.Raise('WFENG_BULK_SETATTR');
651   end if;
652 
653 exception
654   when others then
655     Wf_Core.Context('Wf_Engine_Bulk', 'SetItemAttrNumber', itemtype);
656     raise;
657 end SetItemAttrNumber;
658 
659 --
660 -- SetItemAttrDate (PUBLIC)
661 --   Set the value of an array of date item attribute.
662 --   Attribute must be a DATE-type attribute.
663 -- IN:
664 --   itemtype - Item type
665 --   itemkey - Item key List
666 --   aname - Array of Name
667 --   avalue - Array of new value for attribute
668 --
669 procedure SetItemAttrDate(
670   itemtype in varchar2,
671   itemkeys  in Wf_Engine_Bulk.ItemKeyTabType,
672   anames    in Wf_Engine.NameTabTyp,
673   avalues   in Wf_Engine.DateTabTyp)
674 is
675   status      pls_integer;
676 
677   wiavIND     number;
678   success_cnt number;
679 
680   j           pls_integer;
681   match       boolean;
682   succAttrUpdates Wf_Engine.NameTabTyp;
683   succItemUpdates Wf_Engine_Bulk.ItemKeyTabType;
684 
685 begin
686 
687   -- Check Arguments
688   if (itemtype is null) then
689     Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
690     Wf_Core.Raise('WFSQL_ARGS');
691 
692   elsif (anames.COUNT = 0 or avalues.COUNT = 0) then
693     -- Do not do anything if index table is empty.
694     return;
695 
696   elsif ((anames.LAST <> avalues.LAST or anames.COUNT <> avalues.COUNT)
697       or (anames.LAST<> itemkeys.LAST or anames.COUNT<> itemkeys.COUNT)) then
698     -- Raise an error if the two index tables do not end at the same index
699     -- or do not have the same number of elements.
700     g_FailedItems:=itemkeys;
701     g_FailedAttributes:=anames;
702     Wf_Core.Raise('WFENG_BLK_ITM_ATTRARR_MISMATCH');
703   end if;
704   -- Reset the table of Failed Items and Failed Attributes
705   g_FailedItems.DELETE;
706   g_FailedAttributes.DELETE;
707 
708  --If itemtype is on demand, we will insert default values if not exists already
709   if wf_item.Attribute_On_Demand(itemtype, itemkeys(itemkeys.FIRST)) then
710     begin
711       forall arrayIndex in itemkeys.FIRST..itemkeys.LAST save exceptions
712       insert into WF_ITEM_ATTRIBUTE_VALUES
713         (ITEM_TYPE, ITEM_KEY, NAME, DATE_VALUE)
714       select itemtype, itemkeys(arrayIndex), anames(arrayIndex), Date_default
715         from WF_ITEM_ATTRIBUTES WIA
716         where
717           WIA.ITEM_TYPE = itemtype
718         and
719           WIA.NAME = anames(arrayIndex)
720         and not exists (select 1 from WF_ITEM_ATTRIBUTE_VALUES WIAV
721           where WIAV.item_type=itemtype
722           and  WIAV.item_key=itemkeys(arrayIndex)
723           and  WIAV.NAME=anames(arrayIndex));
724     exception
725         when others then
726           null; -- ignore failures here
727     end;
728   end if;
729   -- Set the date value.
730   begin
731   forall arrInd in itemkeys.FIRST..itemkeys.LAST save exceptions
732       update WF_ITEM_ATTRIBUTE_VALUES set
733       DATE_VALUE = avalues(arrInd)
734       where ITEM_TYPE = itemtype
735       and ITEM_KEY = itemkeys(arrInd)
736       and NAME = anames(arrInd)
737      returning item_key,name bulk collect into succItemUpdates, succAttrUpdates;
738 
739    exception
740       when others then
741        if sql%bulk_exceptions.count>0 then
742          for ErrorIndex in 1..sql%bulk_exceptions.count loop
743           g_failedItems(g_FailedItems.COUNT+1):=
744             itemkeys(sql%bulk_exceptions(ErrorIndex).error_index);
745           g_failedAttributes(g_FailedAttributes.COUNT+1):=
746             anames(sql%bulk_exceptions(ErrorIndex).error_index);
747          end loop;
748         end if;
749    end;
750 
751    if (succItemUpdates.count <> itemkeys.COUNT) then
752    -- determine the failed item/atrributes
753       if succItemUpdates.count=0 then
754           g_failedItems:=itemkeys;
755           g_failedAttributes:=anames;
756       else
757 
758        for i in itemkeys.first..itemkeys.last loop
759 
760          if ((itemkeys.count - i + 1) = succItemUpdates.count) then
761           -- we are done, the rest are all successful
762           exit;
763          end if;
764          match := false;
765          j:=succItemUpdates.first;
766          while j <= succItemUpdates.last loop
767           if anames(i) = succAttrUpdates(j)
768              and itemkeys(i) = succItemUpdates(j) then
769 
770            -- remove the item/attr from the success table
771            succItemUpdates.delete(j);
772            succAttrUpdates.delete(j);
773            match := true;
774            exit;
775           end if;
776 
777           j:=succItemUpdates.next(j);
778 
779          end loop;
780 
781          if not(match) then
782          -- item has failed.Insert into list of failed items
783           g_failedItems(g_FailedItems.COUNT+1):=itemkeys(i);
784           g_failedAttributes(g_FailedAttributes.COUNT+1):=anames(i);
785          end if;
786        end loop;
787       end if; --successUpdates count=0
788    end if; --sucessUpdates doesnot match item count
789 
790    if g_failedItems.COUNT >0 then
791     WF_CORE.TOKEN('ITEMTYPE',itemtype);
792     WF_CORE.TOKEN('TOTAL',to_char(itemkeys.COUNT));
793     WF_CORE.TOKEN('FAILED',to_char(g_FailedItems.COUNT));
794     WF_CORE.Raise('WFENG_BULK_SETATTR');
795   end if;
796 exception
797    when others then
798     Wf_Core.Context('Wf_Engine_Bulk', 'SetItemAttrDate', itemtype);
799     raise;
800 end SetItemAttrDate;
801 
802 -- BulkCreateProcess (PUBLIC)
803 --   Create a new runtime process for a given list of itemkeys
804 --  (for an application itemtype).
805 -- IN
806 --   itemtype - A valid item type
807 --   itemkeys  - A list of itemkeys  generated from the application
808 --               object's primary key.
809 --   process  - A valid root process for this item type
810 --              (or null to use the item's selector function)
811 --  user_keys - A list of userkeys bearing one to one
812 --              correspondence with the item ley list
813 -- owner_roles - A list of ownerroles bearing one-to-one
814 --               correspondence with the item key list
815 procedure CreateProcess(itemtype in varchar2,
816                         itemkeys  in wf_engine_bulk.itemkeytabtype,
817                         process  in varchar2,
818                         user_keys in wf_engine_bulk.userkeytabtype,
819                         owner_roles in wf_engine_bulk.ownerroletabtype,
820                         parent_itemtype in varchar2,
821                         parent_itemkey in varchar2,
822                         parent_context in varchar2,
823                         masterdetail   in boolean )
824 is
825   root varchar2(30);
826   version number;
827   actdate date;
828   typ varchar2(8);
829   rootid pls_integer;
830   status varchar2(8);
831 
832   foundDuplicate boolean :=FALSE;
833 
834 
835   l_itemkeys  wf_engine_bulk.itemkeytabtype;
836   l_user_keys wf_engine_bulk.userkeytabtype;
837   l_owner_roles wf_engine_bulk.ownerroletabtype;
838   mon_random wf_engine.textTabTyp;
839   acc_random wf_engine.textTabTyp;
840   schemaAttribute varchar2(30);
841   l_rkeys wf_engine_bulk.ownerroleTabType;
842   l_ukeys wf_engine_bulk.userkeyTabType;
843   l_count number;
844 
845 begin
846   -- Argument validation
847   if (itemtype is null)  then
848     Wf_Core.Token('ITEMTYPE', itemtype);
849     Wf_Core.Token('PROCESS', process);
850     Wf_Core.Raise('WFSQL_ARGS');
851   end if;
852   l_itemkeys:=    itemkeys;
853   l_user_keys:=   user_keys;
854   l_owner_roles:= owner_roles;
855 
856   -- check whether the user_keys and owner_roles arrays have been passed
857   -- or not. If not , we would need to intialize them to match the itemkey list
858   -- this needs to be done so that Bulk Inserts later donot fail.
859 
860 
861 
862   --<rwunderl:4198524>
863   if (WF_CACHE.MetaRefreshed) then
864     null;
865   end if;
866   -- Reset the table of failed items and successful items
867   g_FailedItems.DELETE;
868   g_SuccessItems.DELETE;
869 
870   if (process is null) then
871     -- Call the selector function to get the process. The process
872     -- retrieved for the first itemkey in the list, is assumed to
873     -- hold for all the itemkeys in the list.
874 
875     root := Wf_Engine_Util.Get_Root_Process(itemtype, l_itemkeys(l_itemkeys.FIRST));
876     if (root is null) then
877 
878       g_FailedItems:=l_itemkeys;
879 
880       Wf_Core.Token('TYPE', itemtype);
881       Wf_Core.Token('KEY', l_itemkeys.FIRST);
882       Wf_Core.Raise('WFENG_ITEM_ROOT_SELECTOR');
883     end if;
884   else
885     root := process;
886   end if;
887 
888   -- Check that the root argument is a valid process.
889   -- NOTE: The check that the process exists must be done BEFORE
890   -- calling create_item to avoid foreign key problems during the insert.
891   -- The check that the process is runnable can't be done until AFTER
892   -- create_item so the date has been established.
893   actdate := sysdate;
894   typ := Wf_Activity.Type(itemtype, root, actdate);
895   if ((typ is null) or (typ <> wf_engine.eng_process)) then
896 
897     g_FailedItems:=l_itemkeys;
898 
899     Wf_Core.Token('TYPE', itemtype);
900     Wf_Core.Token('NAME', root);
901     Wf_Core.Raise('WFENG_PROCESS_NAME');
902   end if;
903 
904   -- Validate the root argument is runnable
905 
906   begin
907   select INSTANCE_ID
908   into rootid
909   from WF_PROCESS_ACTIVITIES PA, WF_ACTIVITIES A
910   where A.ITEM_TYPE = itemtype
911   and A.NAME = 'ROOT'
912   and actdate >= A.BEGIN_DATE
913   and actdate < NVL(A.END_DATE, actdate+1)
914   and PA.PROCESS_NAME = 'ROOT'
915   and PA.PROCESS_ITEM_TYPE = itemtype
916   and PA.PROCESS_VERSION = A.VERSION
917   and PA.INSTANCE_LABEL = root;
918 
919   exception
920   when no_data_found  then
921     g_failedItems:=l_itemkeys;
922 
923     Wf_Core.Token('TYPE', itemtype);
924     Wf_Core.Token('NAME', root);
925     Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
926   end ;
927   -- Check for the length of the parent_context if it is provided
928      if (parent_context is NOT null and (length(parent_context) > 25)) then
929           g_failedItems:=l_itemkeys;
930           WF_CORE.Token('LABEL', parent_context);
931           WF_CORE.Token('LENGTH', '25');
932           WF_CORE.Raise('WFENG_LABEL_TOO_LARGE');
933      end if;
934   -- Check for duplicate item. Not needed as BulkCreateItems would validate this
935 
936     --  Clear plsql cache first, just in case previous
937     -- item was purged/rolled back, then check for duplicate.
938 /*    Wf_Item.ClearCache;
939     -- loop through all  itemkeys in the list to check for duplicate
940     for arrInd in itemkeys.FIRST..itemkeys.LAST loop
941      if (Wf_Item.Item_Exist(itemtype, itemkeys(arrInd))) then
942          foundDuplicate:=TRUE;
943          g_FailedItems(g_FailedItems.COUNT+1):=itemkeys(arrInd);
944          itemkeys.delete(arrInd);
945          user_keys.delete(arrInd);
946          owner_roles.delete(arrInd);
947      end if;
948     end loop;
949     if foundDuplicate then -- we need to condense the itemkeys and the userkeys,ownerroles etc
950        ConsolidateKeys(ItemKeys,User_Keys,Owner_Roles);
951     end if;*/
952 
953     g_SuccessItems:=l_ItemKeys;
954     -- Bulk Insert rows in items table
955     Wf_Engine_Bulk.BulkCreateItems(itemtype, root, actdate,l_user_keys,
956                       l_owner_roles, parent_itemtype, parent_itemkey,parent_context);
957 
958 
959     -- Build the array of random numbers for monitor and access key attributes
960     schemaAttribute:=Wf_Engine_Bulk.Current_Schema;
961     if g_successItems.count>0 then
962     for arrInd in g_SuccessItems.first..g_SuccessItems.last loop
963        mon_random(arrInd)   := Wf_Core.Random;
964        acc_random(arrInd)   := Wf_Core.Random;
965 
966     end loop;
967     end if;
968     -- Create monitor access key attributes
969     Wf_Engine_Bulk.BulkAddItemAttr(itemtype, wf_engine.wfmon_mon_key,
970         mon_random);
971     Wf_Engine_Bulk.BulkAddItemAttr(itemtype, wf_engine.wfmon_acc_key,
972         acc_random);
973 
974     l_itemkeys:=g_SuccessItems;
975     -- Create a schema attribute across the itemkeys
976    if l_itemkeys.count>0 then
977    begin
978     forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST SAVE EXCEPTIONS
979     insert into WF_ITEM_ATTRIBUTE_VALUES (
980       ITEM_TYPE,
981       ITEM_KEY,
982       NAME,
983       TEXT_VALUE
984     ) values (
985       itemtype,
986       l_itemkeys(arrInd),
987       wf_engine.eng_schema,
988       schemaAttribute
989     );
990 
991   exception
992    when others then
993       if SQL%BULK_EXCEPTIONS.COUNT>0 then
994 
995        -- load the failedItems table with the list of errored itemkeys,
996        -- and remove them from the itemkeys table
997        for failIndex in 1..SQL%BULK_EXCEPTIONS.COUNT loop
998          g_FailedItems(g_FailedItems.COUNT+1):=
999                 l_itemkeys(SQL%BULK_EXCEPTIONS(failIndex).ERROR_INDEX);
1000          l_itemkeys.DELETE(SQL%BULK_EXCEPTIONS(failIndex).ERROR_INDEX);
1001        end loop;
1002        ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
1003       end if;
1004   end;
1005   end if;
1006   g_SuccessItems:=l_itemkeys;
1007 
1008   if (l_itemkeys.count > 0 )
1009   and (parent_itemtype is not null )
1010   and (parent_itemkey is not null) then
1011   --Setting the parent information
1012   if (masterdetail) then
1013     --Increment #WAITFORDETAIL master counter if it exists.
1014     if (WF_ENGINE.AddToItemAttrNumber(parent_itemtype, parent_itemkey,
1015                                       '#WAITFORDETAIL', l_itemkeys.count) is NOT NULL) then
1016       if (parent_context is NOT null) then
1017         --Increment/Create label counter.
1018         if (WF_ENGINE.AddToItemAttrNumber(parent_itemType, parent_itemKey,
1019                                              '#CNT_'||parent_context, l_itemkeys.count)
1020                                               is NULL) then
1021           WF_ENGINE.AddItemAttr(itemType=>parent_itemType,
1022                                 itemKey=>parent_itemKey,
1023                                 aname=>'#CNT_'||parent_context,
1024                                 number_value=>l_itemkeys.count);
1025         end if; --Label Counter exists
1026         begin
1027         forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST SAVE EXCEPTIONS
1028         insert into WF_ITEM_ATTRIBUTE_VALUES (
1029         ITEM_TYPE,
1030         ITEM_KEY,
1031         NAME,
1032         TEXT_VALUE
1033         ) values (
1034         itemtype,
1035         l_itemkeys(arrInd),
1036         '#LBL_'||parent_context,
1037         parent_context
1038         );
1039 
1040         exception
1041         when others then
1042          if SQL%BULK_EXCEPTIONS.COUNT>0 then
1043 
1044          -- load the failedItems table with the list of errored itemkeys,
1045          -- and remove them from the itemkeys table
1046             for failIndex in 1..SQL%BULK_EXCEPTIONS.COUNT loop
1047                 g_FailedItems(g_FailedItems.COUNT+1):=
1048                    l_itemkeys(SQL%BULK_EXCEPTIONS(failIndex).ERROR_INDEX);
1049                 l_itemkeys.DELETE(SQL%BULK_EXCEPTIONS(failIndex).ERROR_INDEX);
1050             end loop;
1051             ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
1052          end if;
1053        end;
1054       else
1055         -- Parent context is null
1056         -- increase all known #CNT counter by the number of itemkeys
1057         l_count:= l_itemkeys.count;
1058         update WF_ITEM_ATTRIBUTE_VALUES
1059         set NUMBER_VALUE = NUMBER_VALUE + l_count
1060         where NAME like '#CNT_%'
1061         and NUMBER_VALUE is not null
1062         and ITEM_TYPE = parent_itemType
1063         and ITEM_KEY = parent_itemKey;
1064       end if; --Parent context is not null
1065     end if; --#WAITFORDETAIL exists
1066   end if; --Caller is signalling that this "should" be a coordinated flow.
1067   end if;
1068 
1069   g_successItems:=l_itemKeys;
1070    --finally raise exception if the FailedItems table is non-empty
1071   if g_FailedItems.COUNT>0 then
1072    WF_CORE.Token('TYPE',itemtype);
1073    WF_CORE.Token('FAILED',to_char(g_FailedItems.count));
1074    WF_CORE.TOKEN('TOTAL', to_char(itemkeys.COUNT));
1075    WF_CORE.RAISE('WFENG_BULK_OPER');
1076   end if;
1077 
1078 exception
1079  when others then
1080   Wf_Core.Context('Wf_Engine_Bulk', 'CreateProcess', itemtype, process);
1081   raise;
1082 end CreateProcess;
1083 
1084 ----------------------------------------------------------------
1085 
1086 --
1087 -- BulkStartProcess (PUBLIC)
1088 --   Begins execution of the process.It identifies the start activities
1089 --   for the run-time process and launches them in bulk for all the item keys
1090 --   in the list, under the given itemtype.
1091 -- IN
1092 --   itemtype - A valid item type
1093 --   itemkeys  - A list of itemkeys generated from the application object's
1094 --               primary key.
1095 --
1096 procedure StartProcess(itemtype in varchar2,
1097                        itemkeys  in wf_engine_bulk.itemkeytabtype)
1098 is
1099 
1100 
1101   -- Select all the start activities in this parent process with
1102   -- no in-transitions.
1103   cursor starter_children (itemtype in varchar2,
1104                            process in varchar2,
1105                            version in number) is
1106     SELECT PROCESS_ITEM_TYPE, PROCESS_NAME, PROCESS_VERSION,
1107            ACTIVITY_ITEM_TYPE, ACTIVITY_NAME, INSTANCE_ID,
1108            INSTANCE_LABEL, PERFORM_ROLE, PERFORM_ROLE_TYPE,
1109            START_END, DEFAULT_RESULT
1110     FROM   WF_PROCESS_ACTIVITIES WPA
1111     WHERE  WPA.PROCESS_ITEM_TYPE = itemtype
1112     AND    WPA.PROCESS_NAME = process
1113     AND    WPA.PROCESS_VERSION = version
1114     AND    WPA.START_END = wf_engine.eng_start
1115     AND NOT EXISTS (
1116       SELECT NULL
1117       FROM WF_ACTIVITY_TRANSITIONS WAT
1118       WHERE WAT.TO_PROCESS_ACTIVITY = WPA.INSTANCE_ID);
1119 
1120   TYPE DateTabType is table of DATE index by binary_integer;
1121   TYPE NumTabType  is table of NUMBER index by binary_integer;
1122   TYPE RawTabType is table of RAW(16) index by binary_integer;
1123   type InstanceArrayTyp is table of pls_integer index by binary_integer;
1124 
1125   childarr InstanceArrayTyp;  -- Place holder for all the instance id
1126                               -- selected from starter_children cursor
1127   i pls_integer := 0;         -- Counter for the for loop
1128   process varchar2(30) := ''; -- root process activity name
1129   version pls_integer;        -- root process activity version
1130   processid pls_integer;
1131   actdate date;
1132   rerun varchar2(8);         -- Activity rerun flag
1133   acttype  varchar2(8);      -- Activity type
1134   cost  number;              -- Activity cost
1135   ftype varchar2(30);        -- Activity function type
1136   defer_mode boolean := FALSE;
1137 
1138   TransitionCount pls_integer := 0;
1139   l_baseLnk       NUMBER;
1140   l_prevLnk       NUMBER;
1141   psaIND          NUMBER;
1142   l_linkCollision BOOLEAN;
1143   status          PLS_INTEGER;
1144 
1145  root varchar2(30);          -- Root process of activity
1146 
1147   rootid  pls_integer;        -- Id of root process
1148   act_fname varchar2(240);
1149   act_ftype varchar2(30);
1150   delay  number; -- dont use pls_integer or numeric overflow can occur.
1151   msg_id  raw(16):=null;
1152   l_result number;
1153 
1154   -- Timeout processing stuff
1155   duedate date;
1156   timeout number;
1157   msg varchar2(30);
1158   msgtype varchar2(8);
1159   expand_role varchar2(8);
1160 
1161   duedateTab DateTabType;
1162   execCountTab numTabType;
1163   msgIdTab  RawTabType;
1164   l_itemkeys wf_engine_bulk.itemkeytabtype;
1165   l_rkeys wf_engine_bulk.ownerroleTabType;
1166   l_ukeys wf_engine_bulk.userkeyTabType;
1167 begin
1168   -- Check if the item exists and also get back the root process name
1169   -- and version. We assume that the process and version are identical
1170   -- across the itemkeys in the list so that they need to be retieved
1171   -- only for the first itemkey in the list.
1172 
1173   l_itemkeys:=itemkeys;
1174 
1175   --reset the tables of successful and failed items
1176   g_failedItems.DELETE;
1177   g_successItems.DELETE;
1178   begin
1179   select WI.ROOT_ACTIVITY, WI.ROOT_ACTIVITY_VERSION
1180   into   process,version
1181   from WF_ITEMS WI
1182   where WI.ITEM_TYPE = itemtype
1183   and WI.ITEM_KEY = l_itemkeys(1);
1184  exception
1185    when no_Data_found then
1186     g_FailedItems:=l_itemkeys;
1187     Wf_Core.Token('TYPE', itemtype);
1188     Wf_Core.Token('KEY', l_itemkeys.FIRST);
1189     Wf_Core.Raise('WFENG_ITEM');
1190 
1191   end;
1192 
1193   -- Get the id of the process root.
1194     processid := Wf_Process_Activity.RootInstanceId(itemtype, l_itemkeys(l_itemkeys.FIRST),
1195                                                   process);
1196     if (processid is null) then
1197 
1198      g_FailedItems:=itemkeys;
1199 
1200      Wf_Core.Token('TYPE', itemtype);
1201      Wf_Core.Token('NAME', process);
1202      Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
1203     end if;
1204 
1205 
1206      --increment the execution time counter value
1207     g_execCount:=g_execCount+1;
1208 
1209     -- bulk insert into WF_ITEM_ACTIVITY_STATUSES
1210      begin
1211      forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST save exceptions
1212       insert
1213         into WF_ITEM_ACTIVITY_STATUSES (
1214         ITEM_TYPE,
1215         ITEM_KEY,
1216         PROCESS_ACTIVITY,
1217         ACTIVITY_STATUS,
1218         ACTIVITY_RESULT_CODE,
1219         ASSIGNED_USER,
1220         NOTIFICATION_ID,
1221         BEGIN_DATE,
1222         END_DATE,
1223         DUE_DATE,
1224         EXECUTION_TIME,
1225         OUTBOUND_QUEUE_ID
1226       ) values (
1227         itemtype,
1228         l_itemkeys(arrInd),
1229         processid,
1230         wf_engine.eng_active,
1231         wf_engine.eng_null,
1232         null,
1233         null,
1234         SYSDATE,
1235         null,
1236         null,
1237         g_execCount,
1238        null
1239       );
1240    exception
1241     when others then
1242      if sql%bulk_exceptions.count > 0 then
1243       for arrIndex in 1.. sql%bulk_exceptions.count loop
1244 
1245         g_failedItems(g_failedItems.COUNT+1) := l_itemkeys(sql%bulk_exceptions
1246                                              (arrIndex).ERROR_INDEX);
1247         l_itemkeys.DELETE(sql%bulk_exceptions(arrIndex).ERROR_INDEX);
1248       end loop;
1249      end if;
1250   end;
1251 
1252   if g_failedItems.COUNT>0 then
1253    ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
1254   end if;
1255 
1256 
1257   -- Retrieve the starting activities from cache.
1258   WF_CACHE.GetProcessStartActivities(itemType=>itemtype,
1259                                      name=>process,
1260                                      version=>version,
1261                                      status=>status,
1262                                      psaIND=>psaIND);
1263 
1264   if (status <> WF_CACHE.task_SUCCESS) then
1265     -- Starting activities are not in cache, so we will store them using a for
1266     -- loop to get all the next transition activities.
1267     -- Then we will access the list from cache to avoid maximum open cursor
1268     -- problem.  First we need to retain the base index to be used later.
1269     l_baseLnk := psaIND;
1270     l_linkCollision := FALSE;
1271     for child in starter_children(itemtype, process, version) loop
1272       if (TransitionCount > 0) then --Second and succeeding iterations
1273         --We will locally store the record index from the last loop iteration.
1274         l_prevLnk := psaIND;
1275         --We will now generate an index for the start activity from the
1276         --itemType, name, version, and the current INSTANCE_ID
1277         psaIND := WF_CACHE.HashKey(itemType||':'||process||':'||version||
1278                       ':'||WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID);
1279 
1280         --Check to make sure a record is not already here.
1281         if (WF_CACHE.ProcessStartActivities.EXISTS(psaIND)) then
1282           l_linkCollision := TRUE;  --There should be no record here, so this
1283                                     --is a hash collision.  We will continue
1284                                     --populating this linked list, but after
1285                                     --we use it, we will clear the pl/sql table
1286         end if;
1287 
1288         --Now the PL/SQL table index has moved to the next link, so we will
1289         --populate the prev_lnk with our locally stored index.  This feature,
1290         --not yet used, allows us to traverse backwards through the link list
1291         --if needed.  Since it is not yet used, it is commented out.
1292         --WF_CACHE.ProcessStartActivities(psaIND).PREV_LNK := l_prevLnk;
1293 
1294         --l_prevLnk represents the index of the previous record, and we need
1295         --to update its NEXT_LNK field with the current index.
1296         WF_CACHE.ProcessStartActivities(l_prevLnk).NEXT_LNK := psaIND;
1297       --else
1298       --  WF_CACHE.ProcessStartActivities(psaIND).PREV_LNK := -1;
1299 
1300       end if;
1301 
1302       WF_CACHE.ProcessStartActivities(psaIND).PROCESS_ITEM_TYPE :=
1303                                                   child.PROCESS_ITEM_TYPE;
1304 
1305       WF_CACHE.ProcessStartActivities(psaIND).PROCESS_NAME :=
1306                                                   child.PROCESS_NAME;
1307 
1308       WF_CACHE.ProcessStartActivities(psaIND).PROCESS_VERSION :=
1309                                                       child.PROCESS_VERSION;
1310 
1311       WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID := child.INSTANCE_ID;
1312 
1313       --While we are here, we can populate the ProcessActivities cache hoping
1314       --that a later request of any of these process activities will save us
1315       --another trip to the DB.
1316       WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_ITEM_TYPE :=
1317                                                     child.PROCESS_ITEM_TYPE;
1318       WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_NAME :=
1319                                                     child.PROCESS_NAME;
1320       WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_VERSION :=
1321                                                     child.PROCESS_VERSION;
1322       WF_CACHE.ProcessActivities(child.INSTANCE_ID).ACTIVITY_ITEM_TYPE :=
1323                                                     child.ACTIVITY_ITEM_TYPE;
1324       WF_CACHE.ProcessActivities(child.INSTANCE_ID).ACTIVITY_NAME :=
1325                                                     child.ACTIVITY_NAME;
1326       WF_CACHE.ProcessActivities(child.INSTANCE_ID).INSTANCE_ID :=
1327                                                     child.INSTANCE_ID;
1328       WF_CACHE.ProcessActivities(child.INSTANCE_ID).INSTANCE_LABEL :=
1329                                                     child.INSTANCE_LABEL;
1330       WF_CACHE.ProcessActivities(child.INSTANCE_ID).PERFORM_ROLE :=
1331                                                     child.PERFORM_ROLE;
1332       WF_CACHE.ProcessActivities(child.INSTANCE_ID).PERFORM_ROLE_TYPE :=
1333                                                     child.PERFORM_ROLE_TYPE;
1334       WF_CACHE.ProcessActivities(child.INSTANCE_ID).START_END :=
1335                                                     child.START_END;
1336       WF_CACHE.ProcessActivities(child.INSTANCE_ID).DEFAULT_RESULT :=
1337                                                     child.DEFAULT_RESULT;
1338 
1339       TransitionCount := TransitionCount+1;
1340     end loop;
1341     WF_CACHE.ProcessStartActivities(psaIND).NEXT_LNK := -1;
1342     psaIND := l_baseLnk; --Reset the index back to the beginning.
1343     status := WF_CACHE.task_SUCCESS;  --We now have the records successfully
1344                                       --in cache.
1345 
1346   end if;
1347 
1348   -- Load a local InstanceArrayTyp, we do this because of the recursion that
1349   -- occurs.  Since the ProcessStartActivities Cache is global, any
1350   -- hashCollision would clear the cache and could cause problems as we
1351   -- process activities in recursive calls.
1352   while (psaIND <> -1) loop
1353     childarr(i) := WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID;
1354     i := i+1;
1355     psaIND := WF_CACHE.ProcessStartActivities(psaIND).NEXT_LNK;
1356   end loop;
1357   childarr(i) := '';
1358 
1359   if (l_linkCollision) then
1360     --When populating the linked list, we discovered that a hash collision
1361     --caused us to overwrite a link belonging to another list.  This would
1362     --cause the other list to be incorrect.  We will clear the table so the
1363     --lists will be rebuilt after this transaction.
1364     WF_CACHE.ProcessStartActivities.DELETE;
1365 
1366   end if;
1367 
1368 
1369   i:=0;
1370 
1371   -- for each child activity, do a bulk defer of all the itemkeys in the list
1372 
1373   while (childarr(i) is not null) loop
1374 
1375     begin
1376 
1377 
1378      -- calculate the activity function name and type
1379       act_fname:= Wf_Activity.activity_function(itemtype,l_itemkeys(l_itemkeys.FIRST),childarr(i));
1380       act_ftype:= Wf_Activity.activity_function_type(itemtype,l_itemkeys(l_itemkeys.FIRST),childarr(i));
1381 
1382 
1383       --depending on the activity function, enqueue on the proper queue.
1384 
1385          if l_itemkeys.COUNT <=0 then
1386            WF_CORE.Token('TYPE',itemtype);
1387            WF_CORE.Token('FAILED',to_char(g_FailedItems.count));
1388            WF_CORE.TOKEN('TOTAL',to_char(itemkeys.COUNT));
1389            WF_CORE.RAISE('WFENG_BULK_OPER');
1390            exit;
1391          end if;
1392 
1393          --reset the duedate, msgid tables
1394 
1395          dueDateTab.delete;
1396 
1397          msgIdTab.delete;
1398          -- increment the execution time counter
1399          g_execCount:=g_execCount+1;
1400 
1401          for arrInd in l_itemkeys.FIRST..l_itemkeys.LAST loop
1402          begin
1403            -- 1. Look first for a '#TIMEOUT' NUMBER attribute
1404           timeout := Wf_Engine.GetActivityAttrNumber(itemtype, l_itemkeys(arrInd),childarr(i),
1405                      wf_engine.eng_timeout_attr,
1406                      ignore_notfound=>TRUE);
1407 
1408           if (nvl(timeout, 0) <> 0) then
1409              -- Figure duedate as offset from begin time.
1410              -- NOTE: Default timeout is in units of minutes, not days like
1411              -- all other 'date as number' values, thus the 1440 fudge factor.
1412              duedate:= SYSDATE + (timeout / 1440);
1413           else
1414              -- 2. Look for a '#TIMEOUT' DATE attribute
1415              duedate := Wf_Engine.GetActivityAttrDate(itemtype, l_itemkeys(arrInd),
1416                          childarr(i), wf_engine.eng_timeout_attr,
1417                          ignore_notfound=>TRUE);
1418           end if;
1419          exception
1420           when others then
1421             if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
1422               -- No #TIMEOUT attr means no timeout
1423               wf_core.clear;
1424               duedate:= null;
1425             end if;
1426          end;
1427 
1428         -- depending on the activity function type enqueue on the appropriate queue
1429 
1430          begin
1431 
1432            delay:=0;
1433 
1434            if act_ftype = 'PL/SQL'  then
1435 
1436             wf_queue.enqueue_event
1437             (queuename=>wf_queue.DeferredQueue,
1438              itemtype=> itemtype,
1439              itemkey=>l_itemkeys(arrInd),
1440              actid=>childarr(i),
1441              delay=>delay,
1442              message_handle=>msg_id);
1443             -- even when internal, keep message for cross reference.
1444             -- msg_id :=null;
1445 
1446 
1447 
1448            elsif act_ftype = 'EXTERNAL' then
1449            -- this is a callout so write to OUTBOUND queue
1450            -- do not set the correlation here for compatibility reason
1451 
1452 
1453             wf_queue.enqueue_event
1454             (queuename=>wf_queue.OutboundQueue,
1455              itemtype=>itemtype,
1456              itemkey=>l_itemkeys(arrInd),
1457              actid=>childarr(i),
1458              funcname=>act_fname,
1459              paramlist=>wf_queue.get_param_list
1460                        (itemtype,l_itemkeys(arrInd),childarr(i)),
1461              message_handle=>msg_id);
1462 
1463            else
1464            -- this is a callout so write to OUTBOUND queue for other type
1465 
1466             wf_queue.enqueue_event
1467             (queuename=>wf_queue.OutboundQueue,
1468              itemtype=>itemtype,
1469              itemkey=>l_itemkeys(arrInd),
1470              actid=>childarr(i),
1471              correlation=>act_ftype,
1472              funcname=>act_fname,
1473              paramlist=>wf_queue.get_param_list
1474                         (itemtype,l_itemkeys(arrInd),childarr(i)),
1475              message_handle=>msg_id);
1476 
1477            end if;
1478            duedateTab(duedateTab.count+1):=duedate;
1479            msgIdTab(msgidtab.count+1):=msg_id;
1480          exception
1481             when others then
1482 
1483              g_faileditems(g_faileditems.count+1):=l_itemkeys(arrInd);
1484              l_itemkeys.DELETE(arrInd);
1485          end;
1486          end loop;
1487          if g_failedItems.COUNT>0 then
1488             for arrInd in l_itemkeys.first..l_itemkeys.last loop
1489                if not l_itemkeys.exists(arrInd)  then
1490 
1491                --fill in the gap with the last list element
1492                     l_itemkeys(arrInd):=l_itemkeys(l_itemkeys.last);
1493 
1494                     -- accordingly re-arrange the duedate,and msgid arrays
1495                     -- so as not to lose one-to-one correspondence
1496                     duedateTab(arrInd):=duedateTab(duedateTab.last);
1497                     msgIdTab(arrInd):=msgIdTab(msgIdTab.last);
1498                     l_itemkeys.delete(l_itemkeys.last);
1499                     msgIdTab.delete(msgIdTab.last);
1500                     duedateTab.delete(duedateTab.last);
1501                end if;
1502              end loop;
1503          end if;
1504 
1505 
1506         -- finally bulk insert the activity, with status as 'DEFERRED'
1507         -- for all the itemkeys.
1508       if l_itemkeys.count>0 then
1509       begin
1510       forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST save exceptions
1511         insert
1512         into WF_ITEM_ACTIVITY_STATUSES (
1513         ITEM_TYPE,
1514         ITEM_KEY,
1515         PROCESS_ACTIVITY,
1516         ACTIVITY_STATUS,
1517         ACTIVITY_RESULT_CODE,
1518         ASSIGNED_USER,
1519         NOTIFICATION_ID,
1520         BEGIN_DATE,
1521         END_DATE,
1522         DUE_DATE,
1523         EXECUTION_TIME,
1524         OUTBOUND_QUEUE_ID
1525       ) values (
1526         itemtype,
1527         l_itemkeys(arrInd),
1528         childarr(i),
1529         'DEFERRED',
1530         null,
1531         null,
1532         null,
1533         SYSDATE,
1534         null,
1535         duedateTab(arrInd),
1536         g_execCount,
1537         msgIdTab(arrInd)
1538       );
1539       exception
1540       when others then
1541            if sql%bulk_exceptions.count>0 then
1542              for failindex in 1.. sql%bulk_exceptions.count loop
1543 
1544               g_failedItems(g_faileditems.count+1):=
1545                    l_itemkeys( sql%bulk_exceptions(failindex).error_index);
1546               l_itemkeys.DELETE(sql%bulk_exceptions(failindex).error_index);
1547              end loop;
1548              ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
1549            end if;
1550       end;
1551       end if;
1552    end;
1553    i:= i+1;
1554   end loop;--child activities
1555 
1556 
1557    -- Report an error if no start activities can be found.
1558    if (i = 0) then
1559 
1560      g_FailedItems:=itemkeys;
1561      Wf_Core.Token('PROCESS', process);
1562      Wf_Core.Raise('WFENG_NO_START');
1563   end if;
1564   g_successItems:=l_ItemKeys;
1565   -- Report Error if Failure has occured for some item keys
1566   if g_failedItems.COUNT>0 then
1567    WF_CORE.Token('TYPE',itemtype);
1568    WF_CORE.Token('FAILED',to_char(g_FailedItems.count));
1569    WF_CORE.TOKEN('TOTAL',to_char(g_successItems.COUNT));
1570    WF_CORE.RAISE('WFENG_BULK_OPER');
1571   end if;
1572 exception
1573     when others then
1574      Wf_Core.Context('Wf_Engine_Bulk','StartProcess',
1575         itemtype);
1576        raise;
1577 end StartProcess;
1578 
1579 -- FastForward (PUBLIC)
1580 --
1581 --This API starts a specific activity for a list of items. This activity
1582 --must be marked as start, but does not need to be an activity without
1583 --any in transition.This API would fast forward the launch process by
1584 --bulk-creating the items, bulk initializing item attributes and bulk
1585 --starting a specified startactivity within the process across all the
1586 --itemkeys.The activity must be a direct child of the root process specified.
1587 
1588 -- IN
1589 --   itemtype      - A valid item type
1590 --   itemkeys      - A list of itemkeys generated from the application object's
1591 --                   primary key.
1592 --   process       - The process to be started
1593 --   activity      - The label of the specific activity within the process to be started.
1594 --   activityStatus - The status of the activity.This should be restricted to 'NOTIFIED'
1595 --                     and 'DEFERRED' only.
1596 
1597 procedure FastForward(itemtype in varchar2,
1598                       itemkeys  in wf_engine_bulk.itemkeytabtype,
1599                       process in varchar2,
1600                       activity in varchar2,
1601                       activityStatus in varchar2,
1602                       parent_itemtype in varchar2,
1603                       parent_itemkey in varchar2,
1604                       parent_context in varchar2,
1605                       masterdetail   in boolean)
1606 is
1607 
1608 
1609 
1610   TYPE DateTabType is table of DATE index by binary_integer;
1611   TYPE NumTabType  is table of NUMBER index by binary_integer;
1612   TYPE RawTabType is table of RAW(16) index by binary_integer;
1613 
1614   i pls_integer := 0;         -- Counter for the for loop
1615   version pls_integer;        -- root process activity version
1616   processid pls_integer;
1617   actdate date;
1618   rerun varchar2(8);         -- Activity rerun flag
1619   acttype  varchar2(8);      -- Activity type
1620   cost  number;              -- Activity cost
1621   ftype varchar2(30);        -- Activity function type
1622   defer_mode boolean := FALSE;
1623   activity_date date;
1624 
1625 
1626  root varchar2(30);          -- Root process of activity
1627   rootid  pls_integer;        -- Id of root process
1628   actid   pls_integer;        --Id of the activity to be fast-forwarded
1629   act_fname varchar2(240);
1630   act_ftype varchar2(30);
1631   delay  number; -- dont use pls_integer or numeric overflow can occur.
1632   msg_id  raw(16):=null;
1633   l_result number;
1634 
1635   -- Timeout processing stuff
1636   duedate date;
1637   timeout number;
1638   msg varchar2(30);
1639   msgtype varchar2(8);
1640   expand_role varchar2(8);
1641 
1642   duedateTab DateTabType;
1643   execCountTab numTabType;
1644   msgIdTab  RawTabType;
1645   l_itemkeys wf_engine_bulk.itemKeyTabType;
1646   l_ukeys wf_engine_bulk.userkeyTabType;
1647   l_rkeys wf_engine_bulk.ownerroleTabType;
1648 
1649 begin
1650 
1651   -- Validate that the activityStatus is not other than 'DEFERRED' or 'NOTIFIED'
1652      if activityStatus is not null then
1653         if activityStatus <> 'DEFERRED' and activityStatus <> 'NOTIFIED' then
1654            --raise error
1655            g_failedItems:=itemkeys;
1656            WF_CORE.RAISE('WFENG_INVALID_ACT_STATUS');
1657         end if;
1658      end if;
1659 
1660   -- initialize the itemkeys list
1661      l_itemkeys:=itemkeys;
1662   -- Call CreateProcess to Create the Process across all itemkeys
1663   begin
1664      WF_ENGINE_BULK.CreateProcess(itemtype,l_itemkeys,process,l_ukeys,l_rkeys,
1665      parent_itemtype,parent_itemkey,parent_context,masterdetail);
1666   exception
1667    when others then
1668 
1669       if g_failedItems.COUNT>0 then
1670 
1671          wf_core.clear;
1672          l_itemkeys:=g_SuccessItems;
1673       end if;
1674   end;
1675   if l_itemkeys.count<=0 then
1676    WF_CORE.Token('TYPE',itemtype);
1677    WF_CORE.Token('FAILED',to_char(itemkeys.count));
1678    WF_CORE.TOKEN('TOTAL', to_char(itemkeys.count));
1679    WF_CORE.RAISE('WFENG_BULK_OPER');
1680   end if;
1681   --clear the successful items table
1682      g_SuccessItems.DELETE;
1683 
1684   -- Check if the item exists and also get back the root
1685   -- version. We assume that the process and version are identical
1686   -- across the itemkeys in the list so that they need to be retrieved
1687   -- only for the first itemkey in the list.
1688    begin
1689      select WI.ROOT_ACTIVITY_VERSION
1690      into   version
1691      from WF_ITEMS WI
1692      where WI.ITEM_TYPE = itemtype
1693      and WI.ITEM_KEY = l_itemkeys(1)
1694      and WI.ROOT_ACTIVITY=process;
1695    exception
1696     when no_Data_found then
1697 
1698      g_FailedItems:=l_itemkeys;
1699 
1700      Wf_Core.Token('TYPE', itemtype);
1701      Wf_Core.Token('KEY', l_itemkeys.FIRST);
1702      Wf_Core.Raise('WFENG_ITEM');
1703     end;
1704 
1705 
1706     -- Get the id of the process root.
1707     processid := Wf_Process_Activity.RootInstanceId(itemtype, l_itemkeys(l_itemkeys.FIRST),
1708                                                   process);
1709 
1710     if (processid is null) then
1711 
1712      g_FailedItems:=itemkeys;
1713 
1714      Wf_Core.Token('TYPE', itemtype);
1715      Wf_Core.Token('NAME', process);
1716      Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
1717     end if;
1718 
1719 
1720 
1721     -- determine the instance_id of the activity
1722     -- raise error if the activity is not found.
1723    begin
1724 
1725     SELECT WPA.INSTANCE_ID
1726     into actid
1727     FROM   WF_PROCESS_ACTIVITIES WPA
1728     WHERE  WPA.PROCESS_ITEM_TYPE = itemtype
1729     AND    WPA.PROCESS_NAME = process
1730     AND    WPA.PROCESS_VERSION = version
1731     AND    WPA.START_END = wf_engine.eng_start
1732     AND    WPA.INSTANCE_LABEL=activity;
1733     Exception
1734      When no_data_found then
1735 
1736        g_FailedItems:=itemkeys;
1737 
1738        WF_CORE.Token('ACTIVITY',activity);
1739        WF_CORE.Token('ITEMTYPE',itemtype);
1740        WF_CORE.Token('PROCESS',process);
1741        WF_CORE.Raise('ACTIVITY_NOT_FOUND');
1742     End;
1743 
1744    -- insert the process into the item activity statuses table
1745 
1746    -- increment the execution time counter by 1.
1747      g_execCount:=g_execCount+1;
1748     -- bulk insert into WF_ITEM_ACTIVITY_STATUSES
1749      begin
1750      forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST save exceptions
1751       insert
1752         into WF_ITEM_ACTIVITY_STATUSES (
1753         ITEM_TYPE,
1754         ITEM_KEY,
1755         PROCESS_ACTIVITY,
1756         ACTIVITY_STATUS,
1757         ACTIVITY_RESULT_CODE,
1758         ASSIGNED_USER,
1759         NOTIFICATION_ID,
1760         BEGIN_DATE,
1761         END_DATE,
1762         DUE_DATE,
1763         EXECUTION_TIME,
1764         OUTBOUND_QUEUE_ID
1765       ) values (
1766         itemtype,
1767         l_itemkeys(arrInd),
1768         processid,
1769         wf_engine.eng_active,
1770         wf_engine.eng_null,
1771         null,
1772         null,
1773         SYSDATE,
1774         null,
1775         null,
1776         g_execCount,
1777        null
1778       );
1779      exception
1780      when others then
1781       if sql%bulk_exceptions.count > 0 then
1782        for arrIndex in 1.. sql%bulk_exceptions.count loop
1783 
1784         g_failedItems(g_failedItems.COUNT+1) :=
1785               l_itemkeys(sql%bulk_exceptions(arrIndex).ERROR_INDEX);
1786         l_itemkeys.DELETE(sql%bulk_exceptions(arrIndex).ERROR_INDEX);
1787        end loop;
1788       end if;
1789      end;
1790      -- in case failed itemkeys have been removed
1791      -- condense the sparsely populated itemkey list
1792      if g_failedItems.COUNT>0 then
1793       ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
1794      end if;
1795 
1796      -- defer and enqueue the given activity across all itemkeys
1797      begin
1798 
1799        -- calculate the activity function name and type
1800        act_fname:= Wf_Activity.activity_function(itemtype,l_itemkeys(l_itemkeys.first),actid);
1801        act_ftype:= Wf_Activity.activity_function_type(itemtype,l_itemkeys(l_itemkeys.first),actid);
1802 
1803 
1804 
1805        if l_itemkeys.COUNT <=0 then
1806 
1807                  g_FailedItems:=itemkeys;
1808 
1809                  WF_CORE.Token('TYPE',itemtype);
1810                  WF_CORE.Token('FAILED', to_char(g_FailedItems.count));
1811                  WF_CORE.TOKEN('TOTAL', to_char(itemkeys.count));
1812                  WF_CORE.RAISE('WFENG_BULK_OPER');
1813                  return;
1814        end if;
1815 
1816        --reset the duedate, msgid tables
1817 
1818        dueDateTab.delete;
1819 
1820        msgIdTab.delete;
1821        -- increment the execution time counter
1822        g_execCount:=g_execCount +1;
1823 
1824        for arrInd in l_itemkeys.FIRST..l_itemkeys.LAST loop
1825        begin
1826          -- 1. Look first for a '#TIMEOUT' NUMBER attribute
1827           timeout := Wf_Engine.GetActivityAttrNumber(itemtype,
1828                       l_itemkeys(arrInd),actid,
1829                      wf_engine.eng_timeout_attr,
1830                        ignore_notfound=>TRUE);
1831 
1832           if (nvl(timeout, 0) <> 0) then
1833              -- Figure duedate as offset from begin time.
1834              -- NOTE: Default timeout is in units of minutes, not days like
1835              -- all other 'date as number' values, thus the 1440 fudge factor.
1836              duedate:= SYSDATE + (timeout / 1440);
1837           else
1838              -- 2. Look for a '#TIMEOUT' DATE attribute
1839              duedate := Wf_Engine.GetActivityAttrDate
1840                         (itemtype, l_itemkeys(arrInd),actid,
1841                         wf_engine.eng_timeout_attr,
1842                         ignore_notfound=>TRUE);
1843           end if;
1844        exception
1845           when others then
1846             if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
1847               -- No #TIMEOUT attr means no timeout
1848               wf_core.clear;
1849               duedate:= null;
1850             end if;
1851        end;
1852 
1853        -- we enqueue only if the activityStatus is 'DEFERRED' or null
1854 
1855        delay:=0;
1856        msg_id :=null;
1857        begin
1858        if activityStatus is null or activityStatus='DEFERRED' then
1859            if act_ftype = 'PL/SQL' then
1860             wf_queue.enqueue_event
1861             (queuename=>wf_queue.DeferredQueue,
1862              itemtype=> itemtype,
1863              itemkey=>l_itemkeys(arrInd),
1864              actid=>actid,
1865              delay=>delay,
1866              message_handle=>msg_id);
1867             -- even when internal, keep message for cross reference.
1868             -- msg_id :=null;
1869 
1870 
1871 
1872            elsif act_ftype = 'EXTERNAL' then
1873            -- this is a callout so write to OUTBOUND queue
1874            -- do not set the correlation here for compatibility reason
1875 
1876 
1877             wf_queue.enqueue_event
1878             (queuename=>wf_queue.OutboundQueue,
1879              itemtype=>itemtype,
1880              itemkey=>l_itemkeys(arrInd),
1881              actid=>actid,
1882              funcname=>act_fname,
1883              paramlist=>wf_queue.get_param_list
1884                         (itemtype,l_itemkeys(arrInd),actid),
1885              message_handle=>msg_id);
1886 
1887            else
1888            -- this is a callout so write to OUTBOUND queue for other type
1889 
1890             wf_queue.enqueue_event
1891             (queuename=>wf_queue.OutboundQueue,
1892              itemtype=>itemtype,
1893              itemkey=>l_itemkeys(arrInd),
1894              actid=>actid,
1895              correlation=>act_ftype,
1896              funcname=>act_fname,
1897              paramlist=>wf_queue.get_param_list
1898                         (itemtype,l_itemkeys(arrInd),actid),
1899              message_handle=>msg_id);
1900 
1901            end if;
1902         end if;
1903            duedateTab(duedateTab.count+1):=duedate;
1904 
1905            msgIdTab(msgidtab.count+1):=msg_id;
1906        exception
1907               when others then
1908 
1909                  g_faileditems(g_faileditems.count+1):=l_itemkeys(arrInd);
1910                  l_itemkeys.DELETE(arrInd);
1911        end;
1912 
1913        end loop;
1914 
1915        if g_failedItems.COUNT>0 then
1916              for arrInd in l_itemkeys.first..l_itemkeys.last loop
1917                if not l_itemkeys.exists(arrInd)  then
1918 
1919                --fill in the gap with the last list element
1920                     l_itemkeys(arrInd):=l_itemkeys(l_itemkeys.last);
1921 
1922                     -- accordingly re-arrange the duedate,and msgid arrays
1923                     -- so as not to lose one-to-one correspondence
1924                     duedateTab(arrInd):=duedateTab(duedateTab.last);
1925                     msgIdTab(arrInd):=msgIdTab(msgIdTab.last);
1926                     l_itemkeys.delete(l_itemkeys.last);
1927                     msgIdTab.delete(msgIdTab.last);
1928                     duedateTab.delete(duedateTab.last);
1929                end if;
1930              end loop;
1931        end if;
1932 
1933 
1934        -- finally bulk insert the activity, with status as 'DEFERRED'
1935        -- for all the itemkeys.
1936       begin
1937       forall arrInd in l_itemkeys.FIRST..l_itemkeys.LAST save exceptions
1938         insert
1939         into WF_ITEM_ACTIVITY_STATUSES (
1940         ITEM_TYPE,
1941         ITEM_KEY,
1942         PROCESS_ACTIVITY,
1943         ACTIVITY_STATUS,
1944         ACTIVITY_RESULT_CODE,
1945         ASSIGNED_USER,
1946         NOTIFICATION_ID,
1947         BEGIN_DATE,
1948         END_DATE,
1949         DUE_DATE,
1950         EXECUTION_TIME,
1951         OUTBOUND_QUEUE_ID
1952       ) values (
1953         itemtype,
1954         l_itemkeys(arrInd),
1955 	actid,
1956         nvl(activityStatus,'DEFERRED'),
1957         null,
1958         null,
1959         null,
1960         SYSDATE,
1961         null,
1962         duedateTab(arrInd),
1963         g_execCount,
1964         msgIdTab(arrInd)
1965       );
1966       exception
1967       when others then
1968            if sql%bulk_exceptions.count>0 then
1969              for failindex in 1.. sql%bulk_exceptions.count loop
1970               g_failedItems(g_faileditems.count+1):=
1971               l_itemkeys( sql%bulk_exceptions(failindex).error_index);
1972               l_itemkeys.DELETE(sql%bulk_exceptions(failindex).error_index);
1973              end loop;
1974              ConsolidateKeys(l_itemkeys,l_ukeys,l_rkeys);
1975            end if;
1976       end;
1977    end;
1978    g_SuccessItems:=l_itemkeys;
1979 
1980   -- Report Error if Failure has occured for some item key
1981   if g_failedItems.COUNT>0 then
1982    WF_CORE.Token('TYPE',itemtype);
1983    WF_CORE.Token('FAILED',to_char(g_FailedItems.count));
1984    WF_CORE.TOKEN('TOTAL', to_char(itemkeys.COUNT));
1985    WF_CORE.RAISE('WFENG_BULK_OPER');
1986   end if;
1987 exception
1988   when others then
1989    Wf_Core.Context('Wf_Engine_Bulk','FastForward',
1990     Itemtype,process,activity);
1991     raise;
1992 
1993 
1994 end FastForward;
1995 
1996 
1997 END WF_ENGINE_BULK;