DBA Data[Home] [Help]

PACKAGE BODY: APPS.WF_PURGE

Source


1 package body WF_PURGE as
2 /* $Header: wfprgb.pls 120.19.12020000.4 2013/02/01 17:46:09 alsosa ship $ */
3 
4 --Private Variables
5 l_docommit boolean;
6 l_runtimeonly boolean;
7 
8 -- procedure Move_To_History
9 --   Move wf_item_activity_status rows for particular itemtype/key from
10 --   main table to history table.
11 -- IN:
12 --   itemtype - Item type to move, or null for all itemtypes
13 --   itemkey - Item key to move, or null for all itemkeys
14 --
15 procedure Move_To_History(
16   itemtype in varchar2,
17   itemkey in varchar2)
18 is
19 begin
20   Null;
21 end Move_To_History;
22 
23 --
24 -- procedure Item_Activity_Statuses
25 --   Delete from wf_item_activity_statuses and wf_item_activity_statuses_h
26 --   where end_date before argument.
27 -- IN:
28 --   itemtype - Item type to delete, or null for all itemtypes
29 --   itemkey - Item key to delete, or null for all itemkeys
30 --   enddate - Date to obsolete to
31 --
32 procedure Item_Activity_Statuses(
33   itemtype in varchar2,
34   itemkey in varchar2,
35   enddate in date)
36 is
37 begin
38    Null;
39 end Item_Activity_Statuses;
40 
41 --
42 -- procedure Items
43 --   Delete items with end_time before argument.
44 -- IN:
45 --   itemtype - Item type to delete, or null for all itemtypes
46 --   itemkey - Item key to delete, or null for all itemkeys
47 --   enddate - Date to obsolete to
48 --   docommit- Do not commit if set to false
49 --
50 procedure Items(
51   itemtype in varchar2,
52   itemkey in varchar2,
53   enddate in date,
54   docommit in boolean,
55   force in boolean,
56   purgesigs in pls_integer)
57 is
58   dummy     varchar2(240);
59   keep  number;                      -- Bug 1895332
60   TYPE nidListTAB IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
61   l_nidListTAB nidListTAB;
62   l_tempListTAB nidListTAB;
63   l_keycount number;
64   l_signature boolean := TRUE;
65   xenddate date;
66   l_ecx_sql  varchar2(2000);
67 
68  -- CTILLEY bug 2755885 include rowid in where clause to resolve
69  -- infinite loop
70 
71  -- CTILLEY bug 3228475 - remove like item type and item key to
72  -- performance violation.  Added item5purge to query records
73  -- where itemtype is passed but no itemkey.  No longer supporting
74  -- partial values for either item type or item key.
75 
76    cursor item2purge is
77    SELECT WI.ITEM_TYPE, WI.ITEM_KEY
78    FROM (SELECT PERSISTENCE_DAYS, NAME
79       FROM WF_ITEM_TYPES
80       WHERE PERSISTENCE_TYPE = Wf_Purge.persistence_type and NAME=itemtype) WIT, WF_ITEMS WI
81    WHERE WI.ITEM_TYPE = WIT.NAME
82      AND WI.END_DATE <= enddate-nvl(WIT.PERSISTENCE_DAYS,0)
83      AND WI.END_DATE >  xenddate
84      AND NOT EXISTS
85      (SELECT NULL
86       FROM WF_ITEMS WI2
87       WHERE WI2.END_DATE IS NULL
88       START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
89         AND WI2.ITEM_KEY = WI.ITEM_KEY
90       CONNECT BY PRIOR WI2.ITEM_TYPE = WI2.PARENT_ITEM_TYPE
91         AND PRIOR  WI2.ITEM_KEY = WI2.PARENT_ITEM_KEY )
92      AND NOT EXISTS
93      (SELECT NULL
94       FROM WF_ITEMS WI2
95       WHERE WI2.END_DATE IS NULL
96       START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
97         AND WI2.ITEM_KEY = WI.ITEM_KEY
98       CONNECT BY PRIOR WI2.PARENT_ITEM_TYPE = WI2.ITEM_TYPE
99         AND PRIOR WI2.PARENT_ITEM_KEY = WI2.ITEM_KEY)
100      ORDER BY WI.END_DATE;
101 
102 
103    -- JWSMITH bug 2070056 - add new cursor for performance
104    cursor item3purge is
105      select WI.ITEM_TYPE, WI.ITEM_KEY
106      from   WF_ITEMS WI
107      where  WI.ITEM_TYPE = itemtype
108       and   WI.ITEM_KEY = itemkey
109       and   WI.end_date <= enddate
110        and exists
111           (select null
112            from WF_ITEM_TYPES WIT
113            where WI.END_DATE+nvl(WIT.PERSISTENCE_DAYS,0)<=enddate
114            and WI.ITEM_TYPE = WIT.NAME
115            and WIT.PERSISTENCE_TYPE = Wf_Purge.persistence_type)
116        and not exists
117           (select null
118            from WF_ITEMS WI2
119            WHERE WI2.END_DATE IS NULL
120            START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
121            AND WI2.ITEM_KEY = WI.ITEM_KEY
122            CONNECT BY PRIOR WI2.ITEM_TYPE = WI2.PARENT_ITEM_TYPE
123            AND PRIOR WI2.ITEM_KEY = WI2.PARENT_ITEM_KEY
124            UNION ALL
125            select null
126            from WF_ITEMS WI2
127            WHERE WI2.END_DATE IS NULL
128            START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
129            AND WI2.ITEM_KEY = WI.ITEM_KEY
130            CONNECT BY PRIOR WI2.PARENT_ITEM_TYPE = WI2.ITEM_TYPE
131            AND PRIOR WI2.PARENT_ITEM_KEY = WI2.ITEM_KEY);
132 
133     -- CTILLEY bug 2642057 - added new cursor for use when FORCE
134     -- arg is TRUE.
135     cursor item4purge is
136       select WI.ITEM_TYPE, WI.ITEM_KEY
137       from  WF_ITEMS WI
138       where WI.ITEM_KEY = itemkey
139       and   WI.ITEM_TYPE = itemtype
140       and   WI.end_date <= enddate;
141 
142     -- CTILLEY bug 3228475 - Added new cursor to resolve performance violation
143     -- of using like condition.  We will no longer support passing partial values
144     -- for item type or item key.
145 
146    cursor item5purge is
147      select /*+ first_rows index(WI,WF_ITEMS_N3) */
148             WI.ITEM_TYPE, WI.ITEM_KEY
149      from   WF_ITEMS WI
150      where  WI.END_DATE <= enddate
151      and    WI.END_DATE > xenddate
152       and exists
153          (select null
154           from WF_ITEM_TYPES WIT
155           where WI.END_DATE+nvl(WIT.PERSISTENCE_DAYS,0)<=enddate
156           and WI.ITEM_TYPE = WIT.NAME
157           and WIT.PERSISTENCE_TYPE = Wf_Purge.persistence_type)
158       and not exists
159          (select null
160           from WF_ITEMS WI2
161           WHERE WI2.END_DATE IS NULL
162           START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
163           AND WI2.ITEM_KEY = WI.ITEM_KEY
164           CONNECT BY PRIOR WI2.ITEM_TYPE = WI2.PARENT_ITEM_TYPE
165           AND PRIOR WI2.ITEM_KEY = WI2.PARENT_ITEM_KEY
166           UNION ALL
167           select null
168           from WF_ITEMS WI2
169           WHERE WI2.END_DATE IS NULL
170           START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
171           AND WI2.ITEM_KEY = WI.ITEM_KEY
172           CONNECT BY PRIOR WI2.PARENT_ITEM_TYPE = WI2.ITEM_TYPE
173           AND PRIOR WI2.PARENT_ITEM_KEY = WI2.ITEM_KEY);
174 
175   no_lock exception;
176   pragma EXCEPTION_INIT(no_lock, -54);
177 
178   -- ora-2292 = integrity constraint (%s.%s) violated - child record found
179   in_other_status_table exception;
180   pragma EXCEPTION_INIT(in_other_status_table, -02292);
181 
182   -- CTILLEY 2755885
183   -- ora-1436 CONNECT BY loop in user data - parent_item_type = item_type
184   connect_by_loop exception;
185   pragma EXCEPTION_INIT(connect_by_loop,-1436);
186 
187   -- <rwunderl:3751558>
188   invalid_component exception;
189   pragma EXCEPTION_INIT(invalid_component, -302);
190 
191   invalid_identifier exception;
192   pragma EXCEPTION_INIT(invalid_identifier, -201);
193   -- </rwunderl:3751558>
194 begin
195 
196   xenddate := to_date(1, 'J');
197   -- Disallow future date in enddate
198   -- Avoid something being purged before its defined persistence period.
199   if (enddate > sysdate) then
200     Wf_Core.Token('SYSDATE', to_char(sysdate));
201     Wf_Core.Raise('WF_FUTURE_END_DATE');
202   end if;
203 
204   if ((itemkey is not null and (instr(itemkey,'%')>0 or itemtype is null))
205       or (instr(itemtype,'%')>0)) then
206         wf_core.raise('WFSQL_ARGS');
207   end if;
208 
209   -- Build ECX Purge sql
210   --<rwunderl:3751558>
211   l_ecx_sql := 'begin ECX_PURGE.Purge_Items(:1, :2, :3, ';
212   if (l_docommit) then
213     l_ecx_sql := l_ecx_sql||'TRUE';
214   else
215     l_ecx_sql := l_ecx_sql||'FALSE';
216   end if;
217 
218   if (nvl(l_runtimeonly, TRUE)) then
219     l_ecx_sql := l_ecx_sql||', TRUE';
220   else
221     l_ecx_sql := l_ecx_sql||', FALSE';
222   end if;
223 
224   l_ecx_sql := l_ecx_sql||'); end;';
225   --</rwunderl:3751558>
226 
227   -- for all items that are valid to purge
228   -- outer loop for purging items.
229 
230   <<outer_purge>>
231   loop
232 
233     if (force) then   -- CTILLEY bug 2642057
234       open item4purge;
235     -- JWSMITH bug 2070056
236     -- CTILLEY bug 3228475 - no longer supporting partial values for itemtype
237     -- and itemkey.
238     elsif (itemtype is not null and itemkey is null) then
239         open item2purge;
240     elsif (itemtype is not null) then
241         open item3purge;
242     else
243         open item5purge;
244     end if;
245 
246     <<item_purge_loop>>
247     loop
248 
249        -- CTILLEY bug 2642057
250        if (force) then
251           -- Bug 4163328
252           -- Use bulk operations to improve performance
253           FETCH item4purge BULK COLLECT INTO l_itemtypeTAB,
254                                              l_itemkeyTAB LIMIT wf_purge.commit_frequency;
255       	  exit outer_purge when l_itemkeyTAB.COUNT = 0;
256 
257        -- JWSMITH bug 2070056
258        elsif (itemtype is not null and itemkey is null) then
259           FETCH item2purge BULK COLLECT INTO l_itemtypeTAB,
260                                              l_itemkeyTAB LIMIT wf_purge.commit_frequency;
261           exit outer_purge when l_itemkeyTAB.COUNT = 0;
262 
263        -- CTILLEY bug 3228475
264        elsif (itemtype is not null) then
265           FETCH item3purge BULK COLLECT INTO l_itemtypeTAB,
266                                              l_itemkeyTAB LIMIT wf_purge.commit_frequency;
267           exit outer_purge when l_itemkeyTAB.COUNT = 0;
268 
269        else
270           FETCH item5purge BULK COLLECT INTO l_itemtypeTAB,
271                                              l_itemkeyTAB LIMIT wf_purge.commit_frequency;
272           exit outer_purge when l_itemkeyTAB.COUNT = 0;
273        end if;
274 
275     l_keycount := l_itemkeyTAB.COUNT;
276 
277     -- set row lock on item
278     <<outer_lock_loop>>
279     FOR j IN l_itemkeyTAB.FIRST..l_itemkeyTAB.LAST loop
280 
281       if (j > l_itemkeyTAB.LAST) then
282         exit;
283       end if;
284 
285       -- If we cannot lock current item, try to lock the last item on the index table.
286       -- If we cannot acquire lock on the last item, delete last item and get the
287       -- next last item on the index table until we can acquire a lock.
288       if ( not (wf_item.acquire_lock(l_itemtypeTAB(j), l_itemkeyTAB(j), FALSE))) then
289         <<lock_loop>>
290 	    loop
291 	      if (l_itemkeyTAB.COUNT < j ) then
292 	        exit outer_lock_loop;
293 
294           elsif (wf_item.acquire_lock(l_itemtypeTAB(l_itemtypeTAB.LAST),
295             l_itemkeyTAB(l_itemkeyTAB.LAST), FALSE)) then
296 
297             -- Once we acquired a lock on a last item, copy the last time to
298             -- the current index so that we have a dense index table.
299             l_itemkeyTAB(j) := l_itemkeyTAB(l_itemkeyTAB.LAST);
300             l_itemtypeTAB(j) := l_itemtypeTAB(l_itemtypeTAB.LAST);
301             l_itemkeyTAB.DELETE(l_itemkeyTAB.LAST);
302             l_itemtypeTAB.DELETE(l_itemtypeTAB.LAST);
303             exit lock_loop;
304           else
305             l_itemkeyTAB.DELETE(l_itemkeyTAB.LAST);
306             l_itemtypeTAB.DELETE(l_itemtypeTAB.LAST);
307           end if;
308         end loop lock_loop;
309       end if;
310     end loop outer_lock_loop;
311 
312     if (l_itemkeyTAB.COUNT > 0) then
313 
314       FOR j IN l_itemkeyTAB.FIRST..l_itemkeyTAB.LAST loop
315         begin
316           select   WN.NOTIFICATION_ID
317 	  BULK COLLECT into l_tempListTAB
318 	    from   WF_ITEM_ACTIVITY_STATUSES WIAS,
319 	           WF_NOTIFICATIONS WN
320 	    where  WIAS.ITEM_TYPE = l_itemtypeTAB(j)
321 	    and    WIAS.ITEM_KEY  = l_itemkeyTAB(j)
322 	    and    WIAS.NOTIFICATION_ID = WN.GROUP_ID
323             and    ((purgesigs = 1)
324 		    or not exists
325 	            (select null
326                      from   WF_DIG_SIGS WDS
327                      where  SIG_OBJ_TYPE = 'WF_NTF'
328                      and    SIG_OBJ_ID = WN.NOTIFICATION_ID))
329 	   union all
330 	   select WN.NOTIFICATION_ID
331 	    from   WF_ITEM_ACTIVITY_STATUSES_H WIASH,
332 	           WF_NOTIFICATIONS WN
333 	    where  WIASH.ITEM_TYPE = l_itemtypeTAB(j)
334 	    and    WIASH.ITEM_KEY  = l_itemkeyTAB(j)
335 	    and    WIASH.NOTIFICATION_ID = WN.GROUP_ID
336             and    ((purgesigs = 1)
337 		    or not exists
338 	            (select null
339                      from   WF_DIG_SIGS WDS
340                      where  SIG_OBJ_TYPE = 'WF_NTF'
341                      and    SIG_OBJ_ID = WN.NOTIFICATION_ID));
342 
343 	  if (l_tempListTAB.COUNT > 0) then
344 	    FOR i IN l_tempListTAB.FIRST..l_tempListTAB.LAST loop
345                 l_nidListTAB(l_nidListTAB.COUNT + 1) := l_tempListTAB(i);
346 	    end loop;
347 	  end if;
348 	end;
349       end loop;
350 
351       if (l_nidListTAB.COUNT > 0) then
352         begin
353           FORALL i IN l_nidListTAB.FIRST..l_nidListTAB.LAST
354             -- now delete each notification
355             delete from WF_NOTIFICATION_ATTRIBUTES WNA
356             where WNA.NOTIFICATION_ID = l_nidListTAB(i);
357 
358           --<rwunderl:3751558>
359           -- Delete the signatures related to this notification
360           -- Bug 4163328
361           -- Check if Wf_Digital_Security_Private.Purge_Signature_By_Obj_I is
362           -- accepting varray, if not don't run it again.
363 	  if purgesigs=1 then
364           FOR i in l_nidListTAB.FIRST..l_nidListTAB.LAST loop
365 	    if (l_signature) then
366               begin
367                 execute IMMEDIATE 'begin Wf_Digital_Security_Private.Purge_Signature_By_Obj_Id(''WF_NTF'', :1); end;'
368                   using to_char(l_nidListTAB(i));
369 
370               exception
371                 when invalid_identifier then
372                   l_signature := FALSE;
373 
374                 when invalid_component then
375                   l_signature := FALSE;
376               end;
377               --</rwunderl:3751558>
378             end if;
379           end loop;
380 	  end if;
381 
382             FORALL i in l_nidListTAB.FIRST..l_nidListTAB.LAST
383             -- Delete notification comments
384             DELETE FROM wf_comments wc
385             WHERE wc.notification_id = l_nidListTAB(i);
386 
387             begin
388               FORALL i in l_nidListTAB.FIRST..l_nidListTAB.LAST
389               delete from WF_NOTIFICATIONS WN
390               where WN.NOTIFICATION_ID = l_nidListTAB(i);
391             exception when in_other_status_table then
392               -- happens when nid is also in history table
393               -- we will delete this later in the loop
394               null;
395             end;
396 
397          -- clear l_nidListTAB index table for next bulk processing
398          FOR i IN l_nidListTAB.FIRST..l_nidListTAB.LAST loop
399            l_nidListTAB.DELETE(i);
400          end loop;
401         exception
402 	  when others then
403         rollback;
404         end;
405       end if;  --if (l_nidListTAB.COUNT > 0)
406 
407        begin
408          --delete all status history.
409          FORALL j IN l_itemkeyTAB.FIRST..l_itemkeyTAB.LAST
410          delete from WF_ITEM_ACTIVITY_STATUSES_H
411            where ITEM_TYPE = l_itemtypeTAB(j)
412            and   ITEM_KEY  = l_itemkeyTAB(j);
413 
414          --delete all statuses.
415          FORALL j IN l_itemkeyTAB.FIRST..l_itemkeyTAB.LAST
416          delete from WF_ITEM_ACTIVITY_STATUSES
417            where ITEM_TYPE = l_itemtypeTAB(j)
418            and   ITEM_KEY  = l_itemkeyTAB(j);
419 
420          --delete item attributes
421          FORALL j IN l_itemkeyTAB.FIRST..l_itemkeyTAB.LAST
422          delete from  WF_ITEM_ATTRIBUTE_VALUES
423            where ITEM_TYPE = l_itemtypeTAB(j)
424            and   ITEM_KEY  = l_itemkeyTAB(j);
425 
426 -- 3966635 Workflwo Provisioning Project
427 -- Following added so as not to loose the changes required.
428 --          --delete Provision Requests, if any
429 --          delete from WF_PROV_REQUESTS
430 --          where CONTEXT like c_item_type||':'||c_item_key||':'||'%'
431 --            and source = 'WORKFLOW';
432 --
433          --finally delete the item itself.
434          FORALL j IN l_itemkeyTAB.FIRST..l_itemkeyTAB.LAST
435          delete from  WF_ITEMS
436            where ITEM_TYPE = l_itemtypeTAB(j)
437            and   ITEM_KEY  = l_itemkeyTAB(j);
438 
439       exception
440         when others then
441         rollback;
442       end;
443 
444       --<rwunderl:3751558>
445       begin
446         execute immediate l_ecx_sql using  itemtype, itemkey, enddate;
447       exception
448         when invalid_identifier then
449           null;
450 
451         when invalid_component then
452           null;
453        end;
454       --</rwunderl:3751558>
455     end if;
456 
457     if (l_keycount >= wf_purge.commit_frequency) then
458       exit item_purge_loop;
459     elsif (l_keycount < wf_purge.commit_frequency) then
460       exit outer_purge;
461     end if;
462 
463     end loop item_purge_loop; -- item2purge or item3purge loop
464 
465       if (item2purge%ISOPEN) then
466         close item2purge;
467       end if;
468 
469       if (item3purge%ISOPEN) then
470         close item3purge;
471       end if;
472 
473       if (item4purge%ISOPEN) then
474         close item4purge;
475       end if;
476 
477       -- CTILLEY bug 3228475
478       if (item5purge%ISOPEN) then
479         close item5purge;
480       end if;
481 
482       if ( docommit ) then
483         commit;
484         fnd_concurrent.set_preferred_rbs;
485       end if;
486 
487   end loop outer_purge;
488 
489   if ( docommit ) then
490     commit;
491     fnd_concurrent.set_preferred_rbs;
492   end if;
493 
494   if (item2purge%ISOPEN) then
495     close item2purge;
496   end if;
497 
498   if (item3purge%ISOPEN) then
499     close item3purge;
500   end if;
501 
502   if (item4purge%ISOPEN) then
503        close item4purge;
504   end if;
505 
506   -- CTILLEY bug 3228475
507   if (item5purge%ISOPEN) then
508          close item5purge;
509   end if;
510 
511   -- Clear engine runtime cache for convenience
512   Wf_Item.ClearCache;
513 
514 exception
515   when others then
516     if (item2purge%ISOPEN) then
517       close item2purge;
518     end if;
519 
520     if (item3purge%ISOPEN) then
521       close item3purge;
522     end if;
523 
524     if (item4purge%ISOPEN) then
525        close item4purge;
526     end if;
527 
528     -- CTILLEY bug 3228475
529     if (item5purge%ISOPEN) then
530            close item5purge;
531     end if;
532 
533 
534     Wf_Core.Context('Wf_Purge', 'Items', itemtype, itemkey, to_char(enddate));
535     raise;
536 end Items;
537 
538 --
539 -- procedure Activities
540 --   Delete old activity versions with end_time before argument,
541 --   and that are not referenced by an existing item.
542 -- IN:
543 --   itemtype - Item type to delete, or null for all itemtypes
544 --   name - Activity to delete, or null for all activities
545 --   enddate - Date to obsolete to
546 -- NOTE:
547 --   It is recommended to purge Items before purging Activities to avoid
548 --   obsolete item references preventing obsolete activities from being
549 --   deleted.
550 --
551 procedure Activities(
552   itemtype in varchar2,
553   name in varchar2,
554   enddate in date)
555 is
556   type typetab is table of varchar2(8) index by binary_integer;
557   type nametab is table of varchar2(30) index by binary_integer;
558 
559   -- Select all activities of this itemtype
560 
561 
562   cursor actcurs1 is
563     select distinct WA.ITEM_TYPE, WA.NAME
564     from WF_ACTIVITIES WA
565     where WA.ITEM_TYPE = itemtype;
566 
567   cursor actcurs2 is
568     select distinct WA.ITEM_TYPE, WA.NAME
569     from WF_ACTIVITIES WA
570     where WA.ITEM_TYPE = itemtype
571     and WA.NAME = name;
572 
573   cursor actcurs3 is
574     select distinct WA.ITEM_TYPE, WA.NAME
575     from WF_ACTIVITIES WA;
576 
577 
578   /* JWSMITH BUG 2208127 - took out decode statment as this was   */
579   /* preventing stats from being used and causing performance */
580   /* issues.  Instead have two cursors and if logic to decide     */
581   /* between them.  See bug for more details.                     */
582   cursor parentcurs1(acttype in varchar2, actname in varchar2) is
583     select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME
584     from WF_PROCESS_ACTIVITIES WPA
585     where WPA.ACTIVITY_ITEM_TYPE = acttype
586     and WPA.ACTIVITY_NAME = actname
587     union
588     select WA.ITEM_TYPE PROCESS_ITEM_TYPE, WA.NAME PROCESS_NAME
589     from WF_ACTIVITIES WA
590     where WA.ERROR_ITEM_TYPE = acttype
591     and WA.ERROR_PROCESS IS NOT NULL;
592 
593   -- Select processes using an activity in any version,
594   -- or referencing this activity as an error process
595   cursor parentcurs2(acttype in varchar2, actname in varchar2) is
596     select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME
597     from WF_PROCESS_ACTIVITIES WPA
598     where WPA.ACTIVITY_ITEM_TYPE = acttype
599     and WPA.ACTIVITY_NAME = actname
600     union
601     select WA.ITEM_TYPE PROCESS_ITEM_TYPE, WA.NAME PROCESS_NAME
602     from WF_ACTIVITIES WA
603     where WA.ERROR_ITEM_TYPE = acttype
604     and WA.ERROR_PROCESS = actname;
605 
606 parent parentcurs1%rowtype;
607 parent1 parentcurs2%rowtype;
608 
609   -- Select all versions of an activity before given enddate
610   cursor vercurs(acttype in varchar2, actname in varchar2) is
611     select WA.BEGIN_DATE, WA.END_DATE, WA.VERSION
612     from WF_ACTIVITIES WA
613     where WA.ITEM_TYPE = acttype
614     and WA.NAME = actname
615     and exists
616       (select null
617       from WF_ITEM_TYPES WIT
618       where WA.END_DATE+nvl(WIT.PERSISTENCE_DAYS,0)<=enddate
619       and WA.ITEM_TYPE = WIT.NAME
620       and WIT.PERSISTENCE_TYPE = Wf_Purge.persistence_type);
621 
622   --Use itemcurs1 if the rootname is 'ROOT'
623   cursor itemcurs1(begdate in date, enddate in date,
624                    roottype in varchar2, rootname in varchar2) is
625     select WI.ITEM_TYPE, WI.ITEM_KEY, WI.ROOT_ACTIVITY, WI.BEGIN_DATE
626     from WF_ITEMS WI
627     where WI.BEGIN_DATE between begdate and  nvl(enddate, WI.BEGIN_DATE)
628     and WI.ITEM_TYPE     = roottype;
629 
630   --Use itemcurs2 for other rootnames
631   cursor itemcurs2(begdate in date, enddate in date,
632                   roottype in varchar2, rootname in varchar2) is
633     select WI.ITEM_TYPE, WI.ITEM_KEY, WI.ROOT_ACTIVITY, WI.BEGIN_DATE
634     from WF_ITEMS WI
635     where WI.BEGIN_DATE between begdate and  nvl(enddate, WI.BEGIN_DATE)
636     and WI.ITEM_TYPE     = roottype
637     and WI.ROOT_ACTIVITY = rootname ;
638 
639    item itemcurs1%rowtype;
640    item1 itemcurs2%rowtype;
641 
642   -- List of processes/activities yet to be checked
643   checkcur  pls_integer;
644   checkmax  pls_integer;
645   checktype typetab;
646   checkname nametab;
647 
648   -- List of processes already checked
649   foundmax  pls_integer;
650   foundtype typetab;
651   foundname nametab;
652 
653   -- List of possible root processes found
654   rootmax  pls_integer;
655   roottype typetab;
656   rootname nametab;
657 
658   found boolean;
659 
660   rootid pls_integer;
661   refflag boolean;
662 
663   numvers pls_integer;
664 
665   c_item_type varchar2(8);
666   c_name varchar2(30);
667 
668 begin
669 
670 
671   -- Disallow future date in enddate
672   -- Avoid something being purged before its defined persistence period.
673   if (enddate > sysdate) then
674     Wf_Core.Token('SYSDATE', to_char(sysdate));
675     Wf_Core.Raise('WF_FUTURE_END_DATE');
676   end if;
677 
678    -- Bug 3228475 Disallow partial values for itemtype and activity name
679    if ((itemtype is not null and (instr(name,'%')>0 or itemtype is null))
680        or (instr(itemtype,'%')>0)) then
681       Wf_Core.Raise('WFSQL_ARGS');
682    end if;
683 <<outer_actloop>>
684 loop
685   if (itemtype is not null and name is null) then
686         open actcurs1;
687   elsif (itemtype is not null) then
688         open actcurs2;
689   else
690         open actcurs3;
691   end if;
692 
693    <<actloop>>
694    loop
695 
696     if (itemtype is not null and name is null) then
697        fetch actcurs1 into  c_item_type, c_name;
698          if (actcurs1%notfound) then
699              exit outer_actloop;
700          end if;
701     elsif (itemtype is not null) then
702           fetch actcurs2 into  c_item_type, c_name;
703   	  if (actcurs2%notfound) then
704              exit outer_actloop;
705           end if;
706     else
707           fetch actcurs3 into  c_item_type, c_name;
708   	  if (actcurs3%notfound) then
709              exit outer_actloop;
710           end if;
711     end if;
712 
713 
714     -- Build table of all possible root processes referencing this activity.
715     -- Start with only this activity on the check list,
716     -- and the found and root lists empty.
717     checkcur := 1;
718     checkmax := 1;
719     checktype(1) := c_item_type;
720     checkname(1) := c_name;
721     foundmax := 0;
722     rootmax := 0;
723 
724     -- While processes left to check...
725     <<checkloop>>
726     while (checkcur <= checkmax) loop
727       -- JWSMITH BUG 2208127, Determine which cursor to use.
728       if (checkname(checkcur) = 'ROOT') then
729          -- If this node is already a root, add it to the list immediately.
730          rootmax := rootmax + 1;
731          roottype(rootmax) := checktype(checkcur);
732          rootname(rootmax) := checkname(checkcur);
733 
734          open parentcurs1(checktype(checkcur),checkname(checkcur));
735       else
736          open parentcurs2(checktype(checkcur),checkname(checkcur));
737       end if;
738 
739       -- Get all processes referencing this activity
740       <<parentloop>>
741 
742       -- JWSMITH BUG 2208127, Determine which cursor to use.
743       loop
744 
745         if (checkname(checkcur) = 'ROOT') then
746            fetch parentcurs1 into parent;
747            EXIT WHEN parentcurs1%NOTFOUND;
748         else
749            fetch parentcurs2 into parent1;
750            EXIT WHEN parentcurs2%NOTFOUND;
751            parent := parent1;
752         end if;
753         if (parent.process_name = 'ROOT') then
754            -- If we have reached a root node, add this to this list
755            -- of root processes to check.
756            rootmax := rootmax + 1;
757            roottype(rootmax) := checktype(checkcur);
758            rootname(rootmax) := checkname(checkcur);
759         else
760            -- Otherwise, check if we have already found this activity
761            -- (Found means it has either already been checked or is
762            -- currently in the check queue.)
763            found := FALSE;
764            <<foundloop>>
765            for i in 1 .. foundmax loop
766               if ((foundtype(i) = parent.process_item_type) and
767                  (foundname(i) = parent.process_name)) then
768                   found := TRUE;
769                   exit foundloop;
770               end if;
771            end loop foundloop;
772            -- This is the first time this process has been found.
773            -- Add it to the found list and the list of further
774            -- processes to check.
775            if (not found) then
776               -- Add to list of processes found
777               foundmax := foundmax + 1;
778               foundtype(foundmax) := parent.process_item_type;
779               foundname(foundmax) := parent.process_name;
780 
781               -- Add parent process to list to check
782               checkmax := checkmax + 1;
783               checktype(checkmax) := parent.process_item_type;
784               checkname(checkmax) := parent.process_name;
785            end if;
786         end if;  -- end not root child
787       end loop parentloop;
788 
789       if (parentcurs1%ISOPEN) then
790          CLOSE parentcurs1;
791       elsif (parentcurs2%ISOPEN) then
792          CLOSE parentcurs2;
793       end if;
794 
795       checkcur := checkcur + 1;
796     end loop checkloop;
797 
798     -- All versions of this activity ...
799     <<verloop>>
800     for ver in vercurs(c_item_type, c_name) loop
801       refflag := FALSE;
802       -- All root processes in reference table ...
803       <<rootloop>>
804       for i in 1 .. rootmax loop
805         -- All items in this version using this root process
806         if (rootname(i) = 'ROOT') then
807          open itemcurs1(ver.begin_date, ver.end_date,roottype(i),rootname(i));
808         else
809          open itemcurs2(ver.begin_date, ver.end_date,roottype(i),rootname(i));
810         end if;
811 
812         /**
813          ** Use the itemcur1/itemcur2 depending on rootname
814          ** ROOT or not
815          for item in itemcurs(ver.begin_date, ver.end_date,
816             roottype(i), rootname(i)) loop
817          **/
818          <<itemloop>>
819 
820          loop
821 
822          if (rootname(i)= 'ROOT') then
823            fetch itemcurs1 into item;
824            EXIT WHEN itemcurs1%NOTFOUND;
825          else
826            fetch itemcurs2 into item1;
827            EXIT WHEN itemcurs2%NOTFOUND;
828            item := item1;
829          end if;
830 
831         begin
832           -- Search tree for a reference
833           rootid := Wf_Process_Activity.RootInstanceId(item.item_type,
834                         item.item_key, item.root_activity);
835           if (Wf_Process_Activity.IsChild(rootid, c_item_type,
836                   c_name, item.begin_date)) then
837             refflag := TRUE;
838             exit rootloop;
839           end if;
840           exception
841             when others then
842               -- If any errors occur while searching the item, then the
843               -- item process has been somehow corrupted.
844               -- To be on the safe side, assume it might have a reference
845               -- and do not purge this act/version.
846               refflag := TRUE;
847               exit rootloop;
848           end;
849         end loop itemloop;
850         if (itemcurs1%ISOPEN) then
851           CLOSE itemcurs1;
852         elsif (itemcurs2%ISOPEN) then
853           CLOSE itemcurs2;
854         end if;
855       end loop rootloop;
856 
857       if (itemcurs1%ISOPEN) then
858         CLOSE itemcurs1;
859       elsif (itemcurs2%ISOPEN) then
860         CLOSE itemcurs2;
861       end if;
862 
863       if (not refflag) then
864         -- Purge the activity if no reference found
865 
866         -- Delete any process activities and
867         -- their attribute values and transitions
868         delete from WF_ACTIVITY_ATTR_VALUES WAAV
869         where WAAV.PROCESS_ACTIVITY_ID in
870           (select WPA.INSTANCE_ID
871           from WF_PROCESS_ACTIVITIES WPA
872           where WPA.PROCESS_NAME = c_name
873           and WPA.PROCESS_ITEM_TYPE = c_item_type
874           and WPA.PROCESS_VERSION = ver.version);
875 
876         delete from WF_ACTIVITY_TRANSITIONS WAT
877         where WAT.TO_PROCESS_ACTIVITY in
878           (select WPA.INSTANCE_ID
879           from WF_PROCESS_ACTIVITIES WPA
880           where WPA.PROCESS_NAME = c_name
881           and WPA.PROCESS_ITEM_TYPE = c_item_type
882           and WPA.PROCESS_VERSION = ver.version);
883 
884         delete from WF_ACTIVITY_TRANSITIONS WAT
885         where WAT.FROM_PROCESS_ACTIVITY in
886           (select WPA.INSTANCE_ID
887           from WF_PROCESS_ACTIVITIES WPA
888           where WPA.PROCESS_NAME = c_name
889           and WPA.PROCESS_ITEM_TYPE = c_item_type
890           and WPA.PROCESS_VERSION = ver.version);
891 
892         delete from WF_PROCESS_ACTIVITIES WPA
893         where WPA.PROCESS_NAME = c_name
894         and WPA.PROCESS_ITEM_TYPE = c_item_type
895         and WPA.PROCESS_VERSION = ver.version;
896 
897         -- Delete activity attributes and _tl
898         delete from WF_ACTIVITY_ATTRIBUTES_TL WAAT
899         where WAAT.ACTIVITY_NAME = c_name
900         and WAAT.ACTIVITY_ITEM_TYPE = c_item_type
901         and WAAT.ACTIVITY_VERSION = ver.version;
902 
903         delete from WF_ACTIVITY_ATTRIBUTES WAA
904         where WAA.ACTIVITY_NAME = c_name
905         and WAA.ACTIVITY_ITEM_TYPE = c_item_type
906         and WAA.ACTIVITY_VERSION = ver.version;
907 
908         -- Delete from activities table and _tl table.
909         delete from WF_ACTIVITIES_TL WAT
910         where WAT.NAME = c_name
911         and WAT.ITEM_TYPE = c_item_type
912         and WAT.VERSION = ver.version;
913 
914         delete from WF_ACTIVITIES WA
915         where WA.NAME = c_name
916         and WA.ITEM_TYPE = c_item_type
917         and WA.VERSION = ver.version;
918       end if;
919 
920     end loop verloop;
921 
922     -- If no versions of activity are left, clear any references to
923     -- this activity in ROOT folders
924     select count(1)
925     into numvers
926     from WF_ACTIVITIES WA
927     where WA.NAME = c_name
928     and WA.ITEM_TYPE = c_item_type;
929 
930     if (numvers = 0) then
931       delete from WF_PROCESS_ACTIVITIES WPA
932       where WPA.PROCESS_ITEM_TYPE = c_item_type
933       and WPA.PROCESS_NAME = 'ROOT'
934       and WPA.ACTIVITY_ITEM_TYPE = c_item_type
935       and WPA.ACTIVITY_NAME = c_name;
936     end if;
937 
938   end loop actloop;
939 
940 
941   if (actcurs1%ISOPEN) then
942       close actcurs1;
943   end if;
944 
945   if (actcurs2%ISOPEN) then
946       close actcurs2;
947   end if;
948 
949   if (actcurs3%ISOPEN) then
950       close actcurs3;
951   end if;
952 
953   end loop outer_actloop;
954 
955   if (actcurs1%ISOPEN) then
956       close actcurs1;
957   end if;
958 
959   if (actcurs2%ISOPEN) then
960       close actcurs2;
961   end if;
962 
963   if (actcurs3%ISOPEN) then
964       close actcurs3;
965   end if;
966 
967   -- Clear engine runtime cache for convenience
968   Wf_Activity.ClearCache;
969 
970 exception
971   when others then
972   if (actcurs1%ISOPEN) then
973       close actcurs1;
974   end if;
975 
976   if (actcurs2%ISOPEN) then
977       close actcurs2;
978   end if;
979 
980   if (actcurs3%ISOPEN) then
981       close actcurs3;
982   end if;
983     Wf_Core.Context('Wf_Purge', 'Activities', itemtype, to_char(enddate));
984     raise;
985 end Activities;
986 
987 -- procedure entity_changes
988 --   Purges data from table WF_ENTITY_CHANGES as per the AGE parameter passed
989 --   to concurrent program FNDWFPRG. Introduced as per bug 9394309
990 -- IN: enddate - anything before this date is to be removed
991 --
992 procedure entity_changes(p_enddate date)
993 is
994 begin
995   delete
996   from WF_ENTITY_CHANGES
997   where CHANGE_DATE <= p_enddate;
998 exception
999   when no_data_found then
1000     null;
1001   when others then
1002     wf_core.context('WF_PURGE', 'entity_changes', p_enddate);
1003     raise;
1004 end;
1005 
1006 --
1007 -- procedure Notifications
1008 --   Delete old notifications with end_time before argument,
1009 --   and that are not referenced by an existing item.
1010 -- IN:
1011 --   itemtype - Item type to delete, or null for all itemtypes
1012 --   enddate - Date to obsolete to
1013 --   docommit- Do not commit if set to false
1014 -- NOTE:
1015 --   It is recommended to purge Items before purging Notifications to avoid
1016 --   obsolete item references preventing obsolete notifications from being
1017 --   deleted.
1018 --
1019 procedure Notifications(
1020   itemtype in varchar2,
1021   enddate in date,
1022   docommit in boolean,
1023   purgesigs in pls_integer)
1024 is
1025   TYPE nidListTAB IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
1026   l_nidListTAB nidListTAB;
1027 
1028   -- Cursor to delete all enddated notifications. If the design info for the item
1029   -- is available check for the persistence, if not just delete
1030   cursor c1 is
1031     select WN.NOTIFICATION_ID
1032     from WF_NOTIFICATIONS WN
1033     where WN.MESSAGE_TYPE = itemtype
1034     and not exists
1035       (select NULL
1036       from WF_ITEM_ACTIVITY_STATUSES WIAS
1037       where WIAS.NOTIFICATION_ID = WN.GROUP_ID)
1038     and not exists
1039       (select NULL
1040       from WF_ITEM_ACTIVITY_STATUSES_H WIAS
1041       where WIAS.NOTIFICATION_ID = WN.GROUP_ID)
1042     and (
1043      exists(
1044       select null
1045       from WF_ITEM_TYPES WIT
1046       where WN.END_DATE+nvl(WIT.PERSISTENCE_DAYS,0) <= enddate
1047       and WN.MESSAGE_TYPE = WIT.NAME
1048       and WIT.PERSISTENCE_TYPE = Wf_Purge.persistence_type)
1049      or not exists(
1050       select null
1051       from   WF_ITEM_TYPES WIT
1052       where  WN.MESSAGE_TYPE = WIT.NAME))
1053     and(
1054      (purgesigs = 1)
1055       or not exists
1056       (select null
1057       from   WF_DIG_SIGS WDS
1058       where  SIG_OBJ_TYPE = 'WF_NTF'
1059       and    SIG_OBJ_ID = WN.NOTIFICATION_ID));
1060 
1061    cursor c2 is
1062     select WN.NOTIFICATION_ID
1063     from WF_NOTIFICATIONS WN
1064     where not exists
1065       (select NULL
1066       from WF_ITEM_ACTIVITY_STATUSES WIAS
1067       where WIAS.NOTIFICATION_ID = WN.GROUP_ID)
1068     and not exists
1069       (select NULL
1070       from WF_ITEM_ACTIVITY_STATUSES_H WIAS
1071       where WIAS.NOTIFICATION_ID = WN.GROUP_ID)
1072     and (
1073      exists(
1074       select null
1075       from WF_ITEM_TYPES WIT
1076       where WN.END_DATE+nvl(WIT.PERSISTENCE_DAYS,0) <= enddate
1077       and WN.MESSAGE_TYPE = WIT.NAME
1078       and WIT.PERSISTENCE_TYPE = Wf_Purge.persistence_type)
1079      or not exists(
1080       select null
1081       from   WF_ITEM_TYPES WIT
1082       where  WN.MESSAGE_TYPE = WIT.NAME))
1083     and(
1084       (purgesigs = 1)
1085       or not exists
1086       (select null
1087       from   WF_DIG_SIGS WDS
1088       where  SIG_OBJ_TYPE = 'WF_NTF'
1089       and    SIG_OBJ_ID = WN.NOTIFICATION_ID));
1090 
1091   -- <rwunderl:3751558>
1092   invalid_component exception;
1093   pragma EXCEPTION_INIT(invalid_component, -302);
1094 
1095   invalid_identifier exception;
1096   pragma EXCEPTION_INIT(invalid_identifier, -201);
1097   -- </rwunderl:3751558>
1098 begin
1099 
1100   -- Disallow future date in enddate
1101   -- Avoid something being purged before its defined persistence period.
1102   if (enddate > sysdate) then
1103     Wf_Core.Token('SYSDATE', to_char(sysdate));
1104     Wf_Core.Raise('WF_FUTURE_END_DATE');
1105   end if;
1106 
1107   -- Bug 3228475 Disallow partial values being passed for itemtype
1108   if (instr(itemtype,'%')>0) then
1109       Wf_Core.Raise('WFSQL_ARGS');
1110   end if;
1111 
1112   -- End date all the orphan notifications. This end dates all the notifications
1113   -- generated before the enddate parameter value.
1114   loop
1115     UPDATE wf_notifications wn
1116     SET end_date = nvl(begin_date, to_date('2002/08/01','YYYY/MM/DD')) + 1
1117     WHERE NOT EXISTS
1118       (SELECT NULL
1119        FROM  wf_item_activity_statuses wias
1120        WHERE  wias.notification_id = wn.group_id)
1121     AND NOT EXISTS
1122       (SELECT NULL
1123        FROM  wf_item_activity_statuses_h wiash
1124        WHERE  wiash.notification_id = wn.group_id)
1125     AND wn.end_date is null
1126     AND wn.begin_date <= enddate
1127     AND rownum < Wf_Purge.Commit_Frequency;
1128 
1129     --Bug 14392753: moved the exit point to here before the potential commit
1130     --happens as it will result in SQL%NOTFOUND set to TRUE.
1131     exit when (sql%NOTFOUND);
1132 
1133     --Bug 6759163 : if statement added to avoid commit when
1134     --'docommit' parameter is false
1135     if (docommit) then
1136       commit;
1137     end if;
1138   end loop;
1139 
1140   <<outer_ntf_loop>>
1141   loop
1142 
1143     if (itemtype is not null) then
1144       open c1;
1145     else
1146       open c2;
1147     end if;
1148 
1149     <<inner_ntf_loop>>
1150     loop
1151 
1152      if (itemtype is not null) then
1153      -- Bug 4163328
1154      -- Use bulk operations to improve performance
1155         FETCH c1 BULK COLLECT INTO l_nidListTAB LIMIT wf_purge.commit_frequency;
1156           exit outer_ntf_loop when l_nidListTAB.COUNT = 0;
1157       else
1158         FETCH c2 BULK COLLECT INTO l_nidListTAB LIMIT wf_purge.commit_frequency;
1159           exit outer_ntf_loop when l_nidListTAB.COUNT = 0;
1160        end if;
1161 
1162      -- Delete notification attributes
1163      FORALL j in l_nidListTAB.FIRST..l_nidListTAB.LAST
1164        delete from WF_NOTIFICATION_ATTRIBUTES WNA
1165        where WNA.NOTIFICATION_ID = l_nidListTAB(j);
1166 
1167      --<rwunderl:3751558>
1168      -- Delete the signatures related to this notification
1169      if (purgesigs = 1) then
1170      FOR j in l_nidListTAB.FIRST..l_nidListTAB.LAST loop
1171        begin
1172            execute IMMEDIATE 'begin Wf_Digital_Security_Private.Purge_Signature_By_Obj_Id(''WF_NTF'', :1); end;'
1173                    using to_char(l_nidListTAB(j));
1174        exception
1175          when invalid_identifier then
1176            null;
1177 
1178          when invalid_component then
1179            null;
1180        end;
1181      --</rwunderl:3751558>
1182      end loop;
1183      end if;
1184 
1185      -- Delete Notification comments
1186      FORALL j in l_nidListTAB.FIRST..l_nidListTAB.LAST
1187        DELETE FROM wf_comments wc
1188        WHERE wc.notification_id = l_nidListTAB(j);
1189 
1190      -- Delete notifications
1191      FORALL j in l_nidListTAB.FIRST..l_nidListTAB.LAST
1192        delete from WF_NOTIFICATIONS WN
1193        where WN.NOTIFICATION_ID = l_nidListTAB(j);
1194 
1195      if (l_nidListTAB.COUNT >= wf_purge.commit_frequency) then
1196        exit inner_ntf_loop;
1197      elsif (l_nidListTAB.COUNT < wf_purge.commit_frequency) then
1198        exit outer_ntf_loop;
1199      end if;
1200 
1201     end loop inner_ntf_loop;
1202 
1203     if (c1%ISOPEN) then
1204        close c1;
1205     elsif (c2%ISOPEN) then
1206        close c2;
1207     end if;
1208 
1209     -- Commit Decision
1210     if (docommit) then
1211       commit;
1212       Fnd_Concurrent.Set_Preferred_RBS;
1213     end if;
1214 
1215   end loop outer_ntf_loop;
1216 
1217   if (c1%ISOPEN) then
1218      close c1;
1219   elsif (c2%ISOPEN) then
1220      close c2;
1221   end if;
1222 
1223   -- Commit Decision
1224   if (docommit) then
1225     commit;
1226     Fnd_Concurrent.Set_Preferred_RBS;
1227   end if;
1228 
1229   -- Purge AdHoc Users/Roles/User_Roles
1230   Wf_Purge.AdHocDirectory(enddate);
1231 
1232 exception
1233   when others then
1234     if (c1%ISOPEN) then
1235        close c1;
1236     elsif (c2%ISOPEN) then
1237        close c2;
1238     end if;
1239 
1240     Wf_Core.Context('Wf_Purge', 'Notifications', itemtype, to_char(enddate));
1241     raise;
1242 end Notifications;
1243 
1244 --
1245 -- procedure Item_Notifications
1246 --   Delete notifications sent by a particular item with end_time
1247 --   before argument.
1248 -- IN:
1249 --   itemtype - Item type to delete, or null for all itemtypes
1250 --   itemkey - Item key to delete, or null for all itemkeys
1251 --   enddate - Date to obsolete to
1252 --   docommit- Do not commit if set to false
1253 --
1254 procedure Item_Notifications(
1255   itemtype in varchar2,
1256   itemkey in varchar2,
1257   enddate in date,
1258   docommit in boolean)
1259 is
1260 begin
1261    Null;
1262 end Item_Notifications;
1263 
1264 --
1265 -- Total
1266 --   Delete all obsolete runtime data with end_time before argument.
1267 -- IN:
1268 --   itemtype - Item type to delete, or null for all itemtypes
1269 --   itemkey - Item key to delete, or null for all itemkeys
1270 --   enddate - Date to obsolete to
1271 --   docommit- Commit or no commit after each purge of entitiy
1272 --   runtimeonly - If true only runtime /transaction data purged
1273 --             if false both runtime and design data purged.
1274 --
1275 procedure Total(
1276   itemtype in varchar2,
1277   itemkey in varchar2,
1278   enddate in date,
1279   docommit in boolean,
1280   runtimeonly  in boolean,
1281   purgesigs in pls_integer,
1282   purgeCacheData in boolean
1283 )
1284 is
1285 
1286 begin
1287 
1288   -- Disallow future date in enddate
1289   -- Avoid something being purged before its defined persistence period.
1290   if (enddate > sysdate) then
1291     Wf_Core.Token('SYSDATE', to_char(sysdate));
1292     Wf_Core.Raise('WF_FUTURE_END_DATE');
1293   end if;
1294 
1295   -- Bug 3228475  Disallow partial values from being passed
1296   if ((itemkey is not null and (instr(itemkey,'%')>0 or itemtype is null))
1297        or (instr(itemtype,'%')>0)) then
1298       Wf_Core.Raise('WFSQL_ARGS');
1299   end if;
1300 
1301  -- Bug 1636510
1302  -- Before attempting to purge items we will abort any error process
1303  -- if the activity that launched it is now complete.
1304   wf_purge.abortErrorProcess(itemtype, itemkey);
1305 
1306   l_docommit := docommit;
1307   l_runtimeonly := runtimeonly;
1308   wf_purge.items(itemtype=>itemtype, itemkey=>itemkey, enddate=>enddate, docommit=>docommit,purgesigs=>purgesigs);
1309 
1310   if (docommit) then
1311     commit;
1312     Fnd_Concurrent.Set_Preferred_RBS;
1313   end if;
1314 
1315   --Purge design data only if runtime is
1316   --set to false
1317   if (not nvl(runtimeonly,TRUE)) then
1318     --also purge design data from activities
1319     wf_purge.activities(itemtype=>itemtype, enddate=>enddate);
1320     if (docommit) then
1321       commit;
1322       Fnd_Concurrent.Set_Preferred_RBS;
1323     end if;
1324 
1325     --Directory Information
1326     Wf_Purge.Directory(end_date=>enddate, autocommit=>docommit);
1327     if (docommit) then
1328       commit;
1329       Fnd_Concurrent.Set_Preferred_RBS;
1330     end if;
1331   end if;
1332 
1333   -- Clear engine runtime cache for convenience
1334   Wf_Item.ClearCache;
1335 
1336   -- Purge orphan notifications only if runtimeonly is false
1337   if (not nvl(runtimeonly, TRUE)) then
1338     --Bug 6759163 : 'docommit' parameter passed to Notifications() as well
1339     Notifications(itemtype=>itemtype, enddate=>enddate,docommit=>docommit,purgesigs=>purgesigs);
1340   end if;
1341   -- As per bug 5576885, purge WF entity tables using WF_ENTITY_MGR
1342   if (purgeCacheData) then
1343     WF_ENTITY_MGR.purge_cache_attributes(enddate);
1344     entity_changes(enddate);
1345   end if;
1346 exception
1347   when others then
1348     Wf_Core.Context('Wf_Purge', 'Total', itemtype, itemkey, to_char(enddate));
1349     raise;
1350 end Total;
1351 
1352 --
1353 -- TotalPERM
1354 --   Delete all obsolete runtime data that is of persistence type 'PERM'
1355 --   and with end_time before argument.
1356 -- IN:
1357 --   itemtype - Item type to delete, or null for all itemtypes
1358 --   itemkey - Item key to delete, or null for all itemkeys
1359 --   enddate - Date to obsolete to
1360 --   docommit- Commit or no commit after each purge of entitiy
1361 --   runtimeonly - Delete runtime data alone if set to true
1362 --             else delete both design and runtime data
1363 --
1364 procedure TotalPERM(
1365   itemtype in varchar2,
1366   itemkey in varchar2,
1367   enddate in date,
1368   docommit in boolean,
1369   runtimeonly in boolean,
1370   purgesigs in pls_integer)
1371 is
1372 begin
1373   wf_purge.persistence_type := 'PERM';
1374 
1375   -- Call Total with new args
1376   Wf_Purge.Total(
1377     itemtype,
1378     itemkey,
1379     enddate,
1380     docommit,
1381     nvl(runtimeonly, TRUE),
1382     purgesigs);
1383 
1384   -- Reset persistence type to the default value
1385   wf_purge.persistence_type := 'TEMP';
1386 
1387 exception
1388   when others then
1389     -- Reset persistence type to the default value
1390     wf_purge.persistence_type := 'TEMP';
1391     Wf_Core.Context('Wf_Purge', 'TotalPERM', itemtype, itemkey,
1392                     to_char(enddate));
1393     raise;
1394 end TotalPERM;
1395 
1396 
1397 --
1398 -- TotalConcurrent
1399 --   Concurrent Program version of Total
1400 -- IN:
1401 --   errbuf - CPM error message
1402 --   retcode - CPM return code (0 = success, 1 = warning, 2 = error)
1403 --   itemtype - Item type to delete, or null for all itemtypes
1404 --   itemkey - Item key to delete, or null for all itemkeys
1405 --   age - Minimum age of data to purge (in days)
1406 --   x_persistence_type - Persistence Type to be purged: 'TEMP' or 'PERM'
1407 --   runtimeonly - If 'Y' then purge runtime data alone
1408 --
1409 procedure TotalConcurrent(
1410   errbuf out NOCOPY varchar2,
1411   retcode out NOCOPY varchar2,
1412   itemtype in varchar2,
1413   itemkey in varchar2,
1414   age in varchar2,
1415   x_persistence_type in varchar2,
1416   runtimeonly  in varchar2,
1417   x_commit_frequency in number,
1418   purgesigs in varchar2,
1419   purgeCacheData in varchar2)
1420 is
1421   enddate date;
1422   errname varchar2(30);
1423   errmsg varchar2(2000);
1424   errstack varchar2(2000);
1425 
1426   l_runtimeonly boolean := FALSE;
1427   l_purgesigs pls_integer;
1428   l_purgeCacheData boolean := FALSE;
1429 begin
1430   -- Convert arguments from varchar2 to real type.
1431   enddate := sysdate - to_number(age);
1432 
1433   wf_purge.persistence_type := x_persistence_type;
1434   wf_purge.commit_frequency := x_commit_frequency;
1435 
1436   --If runtimeonly 'N' then purge both runtime
1437   --and design data
1438   if (upper(runtimeonly) = 'Y') then
1439      l_runtimeonly := TRUE;
1440   end if;
1441 
1442   if (upper(purgesigs) = 'Y') then
1443      l_purgesigs := 1;
1444   end if;
1445 
1446   if upper(purgeCacheData) = 'Y' then
1447     l_purgeCacheData := TRUE;
1448   end if;
1449 
1450    -- Call Total with new args
1451    Wf_Purge.Total(
1452     itemtype,
1453     itemkey,
1454     enddate,
1455     true,
1456     l_runtimeonly,
1457     l_purgesigs,
1458     l_purgeCacheData);
1459 
1460 
1461   -- Return 0 for successful completion.
1462   errbuf := '';
1463   retcode := '0';
1464   wf_purge.persistence_type := 'TEMP';  -- reset to the default value
1465   wf_purge.commit_frequency := 1000; -- reset to the default value
1466 
1467 exception
1468   when others then
1469     -- Retrieve error message into errbuf
1470     wf_core.get_error(errname, errmsg, errstack);
1471     if (errmsg is not null) then
1472       errbuf := errmsg;
1473     else
1474       errbuf := sqlerrm;
1475     end if;
1476 
1477     -- Return 2 for error.
1478     retcode := '2';
1479 
1480     -- Reset persistence type to the default value
1481     wf_purge.persistence_type := 'TEMP';
1482 end TotalConcurrent;
1483 
1484 --
1485 -- Directory
1486 --   Purge all WF_LOCAL_* tables based on expiration date
1487 -- IN:
1488 --   end_date - Date to purge to
1489 --
1490 procedure Directory(
1491   end_date in date default sysdate,
1492   orig_system in varchar2 default null,
1493   autocommit in boolean default false)
1494 is
1495   --
1496   -- All roles that past the end_date and with no outstanding notification
1497   --
1498   -- Need to check ORIGINAL_RECIPIENT as well as the RECIPIENT_ROLE just
1499   -- in case a 'Delegate' happened, we won't purge the orginal owner.
1500   --
1501   --Bug 16054955. This cursor is to purge ad-hoc USERS and related data
1502   cursor user_cursor is
1503     select local.NAME, local.ORIG_SYSTEM, local.ORIG_SYSTEM_ID
1504      from  WF_LOCAL_ROLES PARTITION(WF_LOCAL_ROLES) local
1505      where ORIG_SYSTEM = 'WF_LOCAL_USERS'
1506      and   EXPIRATION_DATE <= end_date
1507      and   local.USER_FLAG='Y'
1508      and not exists
1509            (select NULL
1510               from WF_NOTIFICATIONS wn
1511              where wn.RECIPIENT_ROLE = local.NAME
1512                 or wn.ORIGINAL_RECIPIENT = local.NAME);
1513 
1514   --Bug 16054955. This cursor is to purge ad-hoc ROLES and related data
1515   cursor role_cursor is
1516     select local.NAME, local.ORIG_SYSTEM, local.ORIG_SYSTEM_ID
1517      from  WF_LOCAL_ROLES PARTITION(WF_LOCAL_ROLES) local
1518      where ORIG_SYSTEM = 'WF_LOCAL_ROLES'
1519      and   EXPIRATION_DATE <= end_date
1520      and   local.USER_FLAG='N'
1521      and not exists
1522            (select NULL
1523             from   WF_ROLE_HIERARCHIES
1524             where  SUPER_NAME = local.NAME
1525             or     SUB_NAME   = local.NAME)
1526      and not exists
1527            (select NULL
1528               from WF_NOTIFICATIONS wn
1529              where wn.RECIPIENT_ROLE = local.NAME
1530                 or wn.ORIGINAL_RECIPIENT = local.NAME);
1531 
1532   -- Similar to role_cursor for orig systems different from WF_LOCAL_ROLES
1533   cursor role_orig_system_cursor (p_OrigSystem varchar2,
1534                                   p_endDate date) is
1535     select local.NAME, local.ORIG_SYSTEM, local.ORIG_SYSTEM_ID
1536      from  WF_LOCAL_ROLES local
1537      where ORIG_SYSTEM = p_origSystem
1538      and   (p_endDate is NULL or EXPIRATION_DATE <= p_endDate)
1539      and   local.USER_FLAG='N'
1540      and not exists
1541            (select NULL
1542             from   WF_ROLE_HIERARCHIES
1543             where  SUPER_NAME = local.NAME
1544             or     SUB_NAME   = local.NAME)
1545      and not exists
1546            (select NULL
1547               from WF_NOTIFICATIONS wn
1548              where wn.RECIPIENT_ROLE = local.NAME
1549                 or wn.ORIGINAL_RECIPIENT = local.NAME);
1550 
1551   -- Similar to user_cursor for orig systems different from WF_LOCAL_USERS
1552   cursor user_orig_system_cursor (p_OrigSystem varchar2,
1553                                   p_endDate date) is
1554     select local.NAME, local.ORIG_SYSTEM, local.ORIG_SYSTEM_ID
1555      from  WF_LOCAL_ROLES local
1556      where ORIG_SYSTEM = p_origSystem
1557      and   (p_endDate is NULL or EXPIRATION_DATE <= p_endDate)
1558      and   local.USER_FLAG='Y'
1559      and not exists
1560            (select NULL
1561             from   WF_USER_ROLE_ASSIGNMENTS
1562             where  USER_NAME = local.NAME
1563             and    RELATIONSHIP_ID <> -1)
1564      and not exists
1565            (select NULL
1566               from WF_NOTIFICATIONS wn
1567              where wn.RECIPIENT_ROLE = local.NAME
1568                 or wn.ORIGINAL_RECIPIENT = local.NAME)
1569      and not exists
1570            (select NULL
1571               from WF_ITEMS wi
1572              where wi.OWNER_ROLE = local.NAME);
1573 
1574   -- Bug 8204723. These two cursors are used to remove expired associations regardles
1575   -- of whether the role is expired or not.
1576   cursor expired_rel_cursor is
1577     select wlur.user_name, wlur.user_orig_system, wlur.user_orig_system_id,
1578            wlur.role_name, wlur.role_orig_system, wlur.role_orig_system_id
1579      from  WF_LOCAL_USER_ROLES PARTITION (WF_LOCAL_ROLES) WLUR
1580      where (WLUR.ROLE_ORIG_SYSTEM='WF_LOCAL_ROLES' or WLUR.USER_ORIG_SYSTEM='WF_LOCAL_USERS')
1581      and   WLUR.EFFECTIVE_END_DATE <= directory.end_date;
1582 
1583   cursor expired_rel_orig_system_cursor (p_origSystem varchar2,
1584                                          p_endDate date) is
1585     select wlur.user_name, wlur.user_orig_system, wlur.user_orig_system_id,
1586            wlur.role_name, wlur.role_orig_system, wlur.role_orig_system_id
1587      from  WF_LOCAL_USER_ROLES WLUR, WF_LOCAL_ROLES WLR
1588      where WLR.ORIG_SYSTEM=p_origSystem
1589        and ((WLR.NAME=WLUR.USER_NAME and WLR.ORIG_SYSTEM=WLUR.USER_ORIG_SYSTEM)
1590            or
1591            (WLR.NAME=WLUR.ROLE_NAME and WLR.ORIG_SYSTEM=WLUR.ROLE_ORIG_SYSTEM))
1592        and WLUR.PARTITION_ID = 0
1593        and WLUR.EFFECTIVE_END_DATE <= p_endDate;
1594 
1595   type roleRec is record (NAME           WF_LOCAL_ROLES.NAME%type,
1596                           ORIG_SYSTEM    WF_LOCAL_ROLES.ORIG_SYSTEM%type,
1597                           ORIG_SYSTEM_ID WF_LOCAL_ROLES.ORIG_SYSTEM_ID%type);
1598   type roleTabType is table of roleRec index by binary_integer;
1599   roleTab roleTabType;
1600   type expRoleRec is record (USER_NAME           WF_LOCAL_ROLES.NAME%type,
1601                              USER_ORIG_SYSTEM    WF_LOCAL_ROLES.ORIG_SYSTEM%type,
1602                              USER_ORIG_SYSTEM_ID WF_LOCAL_ROLES.ORIG_SYSTEM_ID%type,
1603                              ROLE_NAME           WF_LOCAL_ROLES.NAME%type,
1604                              ROLE_ORIG_SYSTEM    WF_LOCAL_ROLES.ORIG_SYSTEM%type,
1605                              ROLE_ORIG_SYSTEM_ID WF_LOCAL_ROLES.ORIG_SYSTEM_ID%type);
1606   type expRoleTabType is table of expRoleRec index by binary_integer;
1607   expRoleTab expRoleTabType;
1608   l_commit boolean := false;
1609 
1610 begin
1611   -- Disallow future date in enddate
1612   -- Avoid something being purged before its expiration date.
1613   if (end_date > sysdate) then
1614     Wf_Core.Token('SYSDATE', to_char(sysdate));
1615     Wf_Core.Raise('WF_FUTURE_END_DATE');
1616   end if;
1617   -- autocommit is particular to this API and can be set from sql*plus whereas
1618   -- l_docommit is private to WF_PURGE. We will commit if either is true
1619   if (autocommit or l_docommit) then
1620     l_commit := true;
1621   end if;
1622 
1623   if (orig_system is NOT NULL) then
1624     --We are using the new feature allowing a specific orig_system to be
1625     --removed from the local partition either by end_date, or remove all with
1626     --a null end-date (providing there are no active notifications or items).
1627     --
1628     -- Purge Roles
1629     -- Delete rows in wf_local_user_roles by role_name and wf_local_roles
1630     -- by name as determined
1631     open role_orig_system_cursor(orig_system, end_date);
1632     loop
1633       fetch role_orig_system_cursor bulk collect into roleTab LIMIT wf_purge.commit_frequency;
1634       exit when roleTab.COUNT=0;
1635       for idx in roleTab.FIRST..roleTab.LAST loop
1636         delete from WF_LOCAL_USER_ROLES local
1637          where local.ROLE_NAME = roleTab(idx).name
1638            and local.ROLE_ORIG_SYSTEM = roleTab(idx).orig_system
1639            and local.ROLE_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1640 
1641         delete from WF_USER_ROLE_ASSIGNMENTS local
1642          where local.ROLE_NAME = roleTab(idx).name
1643            and local.ROLE_ORIG_SYSTEM = roleTab(idx).orig_system
1644            and local.ROLE_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1645         --Remove the role
1646         delete from WF_LOCAL_ROLES local
1647         where local.NAME = roleTab(idx).name
1648           and local.ORIG_SYSTEM = roleTab(idx).orig_system
1649           and local.ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1650       end loop;
1651       if l_commit then
1652         commit;
1653       end if;
1654     end loop;
1655     close role_orig_system_cursor;
1656     open user_orig_system_cursor (orig_system, end_date);
1657     loop
1658       fetch user_orig_system_cursor bulk collect into roleTab LIMIT wf_purge.commit_frequency;
1659       exit when roleTab.COUNT=0;
1660       for idx in roleTab.FIRST..roleTab.LAST loop
1661         delete from WF_LOCAL_USER_ROLES local
1662          where local.USER_NAME = roleTab(idx).name
1663            and local.USER_ORIG_SYSTEM = roleTab(idx).orig_system
1664            and local.USER_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1665 
1666         delete from WF_USER_ROLE_ASSIGNMENTS local
1667          where local.USER_NAME = roleTab(idx).name
1668            and local.USER_ORIG_SYSTEM = roleTab(idx).orig_system
1669            and local.USER_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1670         --Remove the role
1671         delete from WF_LOCAL_ROLES local
1672         where local.NAME = roleTab(idx).name
1673           and local.ORIG_SYSTEM = roleTab(idx).orig_system
1674           and local.ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1675       end loop;
1676       if l_commit then
1677         commit;
1678       end if;
1679     end loop;
1680     close user_orig_system_cursor;
1681 
1682     open expired_rel_orig_system_cursor (orig_system, end_date);
1683     loop
1684       fetch expired_rel_orig_system_cursor bulk collect into expRoleTab
1685       limit wf_purge.commit_frequency;
1686       exit when expRoleTab.COUNT=0;
1687       for idx in expRoleTab.FIRST..expRoleTab.LAST loop
1688         delete from WF_LOCAL_USER_ROLES local
1689          where local.USER_NAME = expRoleTab(idx).USER_NAME
1690            and local.USER_ORIG_SYSTEM = expRoleTab(idx).USER_ORIG_SYSTEM
1691            and local.USER_ORIG_SYSTEM_ID = expRoleTab(idx).USER_ORIG_SYSTEM_ID
1692            and local.ROLE_NAME = expRoleTab(idx).ROLE_NAME
1693            and local.ROLE_ORIG_SYSTEM = expRoleTab(idx).ROLE_ORIG_SYSTEM
1694            and local.ROLE_ORIG_SYSTEM_ID = expRoleTab(idx).ROLE_ORIG_SYSTEM_ID;
1695 
1696         delete from WF_USER_ROLE_ASSIGNMENTS local
1697          where local.USER_NAME = expRoleTab(idx).USER_NAME
1698            and local.USER_ORIG_SYSTEM = expRoleTab(idx).USER_ORIG_SYSTEM
1699            and local.USER_ORIG_SYSTEM_ID = expRoleTab(idx).USER_ORIG_SYSTEM_ID
1700            and local.ROLE_NAME = expRoleTab(idx).ROLE_NAME
1701            and local.ROLE_ORIG_SYSTEM = expRoleTab(idx).ROLE_ORIG_SYSTEM
1702            and local.ROLE_ORIG_SYSTEM_ID = expRoleTab(idx).ROLE_ORIG_SYSTEM_ID;
1703       end loop;
1704       if l_commit then
1705         commit;
1706       end if;
1707     end loop;
1708     close expired_rel_orig_system_cursor;
1709   elsif (end_date is not null) then
1710     --
1711     -- Purge Roles
1712     -- Delete rows in wf_local_user_roles by role_name and wf_local_roles
1713     -- by name as determined
1714     open role_cursor;
1715     loop
1716       fetch role_cursor bulk collect into roleTab limit WF_PURGE.COMMIT_FREQUENCY;
1717       exit when roleTab.COUNT = 0;
1718       for idx in roleTab.FIRST..roleTab.LAST loop
1719         delete from WF_LOCAL_USER_ROLES local
1720          where local.ROLE_NAME = roleTab(idx).name
1721            and local.ROLE_ORIG_SYSTEM = roleTab(idx).orig_system
1722            and local.ROLE_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1723 
1724         delete from WF_USER_ROLE_ASSIGNMENTS local
1725          where local.ROLE_NAME = roleTab(idx).name
1726            and local.ROLE_ORIG_SYSTEM = roleTab(idx).orig_system
1727            and local.ROLE_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1728         delete from WF_LOCAL_ROLES PARTITION(WF_LOCAL_ROLES) local
1729          where local.NAME = roleTab(idx).name
1730            and local.ORIG_SYSTEM = roleTab(idx).orig_system
1731            and local.ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1732       end loop;
1733       if l_commit then
1734         commit;
1735       end if;
1736     end loop;
1737     close role_cursor;
1738     open user_cursor;
1739     loop
1740       fetch user_cursor bulk collect into roleTab limit WF_PURGE.COMMIT_FREQUENCY;
1741       exit when roleTab.COUNT=0;
1742       for idx in roleTab.FIRST..roleTab.LAST loop
1743         delete from WF_LOCAL_USER_ROLES local
1744          where local.USER_NAME = roleTab(idx).name
1745            and local.USER_ORIG_SYSTEM = roleTab(idx).orig_system
1746            and local.USER_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1747 
1748         delete from WF_USER_ROLE_ASSIGNMENTS local
1749          where local.USER_NAME = roleTab(idx).name
1750            and local.USER_ORIG_SYSTEM = roleTab(idx).orig_system
1751            and local.USER_ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1752 
1753         delete from WF_LOCAL_ROLES PARTITION(WF_LOCAL_ROLES) local
1754          where local.NAME = roleTab(idx).name
1755            and local.ORIG_SYSTEM = roleTab(idx).orig_system
1756            and local.ORIG_SYSTEM_ID = roleTab(idx).orig_system_id;
1757       end loop;
1758       if l_commit then
1759         commit;
1760       end if;
1761     end loop;
1762     close user_cursor;
1763     open expired_rel_cursor;
1764     loop
1765       fetch expired_rel_cursor bulk collect into expRoleTab limit
1766       WF_PURGE.COMMIT_FREQUENCY;
1767       exit when expRoleTab.COUNT=0;
1768       for idx in expRoleTab.FIRST..expRoleTab.LAST loop
1769         delete from WF_LOCAL_USER_ROLES local
1770         where local.USER_NAME = expRoleTab(idx).USER_NAME
1771           and local.USER_ORIG_SYSTEM = expRoleTab(idx).USER_ORIG_SYSTEM
1772           and local.USER_ORIG_SYSTEM_ID = expRoleTab(idx).USER_ORIG_SYSTEM_ID
1773           and local.ROLE_NAME = expRoleTab(idx).ROLE_NAME
1774           and local.ROLE_ORIG_SYSTEM = expRoleTab(idx).ROLE_ORIG_SYSTEM
1775           and local.ROLE_ORIG_SYSTEM_ID = expRoleTab(idx).ROLE_ORIG_SYSTEM_ID;
1776 
1777         delete from WF_USER_ROLE_ASSIGNMENTS local
1778         where local.USER_NAME = expRoleTab(idx).USER_NAME
1779           and local.USER_ORIG_SYSTEM = expRoleTab(idx).USER_ORIG_SYSTEM
1780           and local.USER_ORIG_SYSTEM_ID = expRoleTab(idx).USER_ORIG_SYSTEM_ID
1781           and local.ROLE_NAME = expRoleTab(idx).ROLE_NAME
1782           and local.ROLE_ORIG_SYSTEM = expRoleTab(idx).ROLE_ORIG_SYSTEM
1783           and local.ROLE_ORIG_SYSTEM_ID = expRoleTab(idx).ROLE_ORIG_SYSTEM_ID;
1784       end loop;
1785       if l_commit then
1786         commit;
1787       end if;
1788     end loop;
1789     close expired_rel_cursor;
1790   end if;
1791 exception
1792   when others then
1793     if (role_cursor%isopen) then
1794       close role_cursor;
1795     end if;
1796     if (user_cursor%isopen) then
1797       close user_cursor;
1798     end if;
1799     if (role_orig_system_cursor%isopen) then
1800       close role_orig_system_cursor;
1801     end if;
1802     if (user_orig_system_cursor%isopen) then
1803       close user_orig_system_cursor;
1804     end if;
1805     if (expired_rel_orig_system_cursor%isopen) then
1806       close expired_rel_orig_system_cursor;
1807     end if;
1808     if (expired_rel_cursor%isopen) then
1809       close expired_rel_cursor;
1810     end if;
1811     wf_core.context('WF_PURGE', 'Directory', end_date);
1812     raise;
1813 end Directory;
1814 
1815 --
1816 -- AdHocDirectory
1817 --   Purge all WF_LOCAL_* tables based on expiration date
1818 -- IN:
1819 --   end_date - Date to purge to
1820 --
1821 procedure AdHocDirectory(
1822   end_date in date)
1823 is
1824 begin
1825   Wf_Purge.Directory(end_date);
1826 exception
1827   when OTHERS then
1828     wf_core.context('WF_PURGE', 'AdHocDirectory', end_date);
1829     raise;
1830 end AdHocDirectory;
1831 
1832  --
1833  -- GetPurgeableCount
1834  --   Returns the count of purgeable items for a specific itemType.
1835  -- IN:
1836  --   p_itemType  in VARCHAR2
1837  --
1838  FUNCTION GetPurgeableCount (p_itemType in varchar2) return number is
1839  l_purgeable number ;
1840  BEGIN
1841     --Get all records for the itemtype
1842      select Count(*) into l_purgeable
1843      from  WF_ITEMS WI
1844      where WI.ITEM_TYPE = p_itemType
1845      and   WI.END_DATE <= sysdate
1846      and  exists
1847          (select null
1848           from WF_ITEM_TYPES WIT
1849           where WI.END_DATE+nvl(WIT.PERSISTENCE_DAYS,0)<=sysdate
1850           and WI.ITEM_TYPE = WIT.NAME
1851           )
1852       and not exists
1853          (select null
1854           from WF_ITEMS WI2
1855           WHERE WI2.END_DATE IS NULL
1856           START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
1857           AND WI2.ITEM_KEY = WI.ITEM_KEY
1858           CONNECT BY PRIOR WI2.ITEM_TYPE = WI2.PARENT_ITEM_TYPE
1859           AND PRIOR WI2.ITEM_KEY = WI2.PARENT_ITEM_KEY
1860           UNION ALL
1861           select null
1862           from WF_ITEMS WI2
1863           WHERE WI2.END_DATE IS NULL
1864           START WITH WI2.ITEM_TYPE = WI.ITEM_TYPE
1865           AND WI2.ITEM_KEY = WI.ITEM_KEY
1866           CONNECT BY PRIOR WI2.PARENT_ITEM_TYPE = WI2.ITEM_TYPE
1867           AND PRIOR WI2.PARENT_ITEM_KEY = WI2.ITEM_KEY);
1868    return l_purgeable;
1869 exception
1870   when OTHERS then
1871     wf_core.context('WF_PURGE', 'GetPurgeableCount', p_itemType);
1872     raise;
1873 end;
1874 
1875 
1876  --
1877  -- AbortErrorProcess
1878  --   Aborts the Error process for an errored activity if the activity is
1879  --   now COMPLETE.
1880  -- IN:
1881  --   itemtype  in VARCHAR2
1882  --   itemkey   in VARCHAR2
1883  --
1884  procedure AbortErrorProcess (itemtype varchar2,
1885                               itemkey varchar2)
1886  is
1887 
1888   c_item_key varchar2(240);
1889 
1890   -- Bug 11728665, added null statement to improve performance of c_errorX cursors.
1891   cursor c_error1 is
1892     select wi.item_key
1893     from wf_item_activity_statuses wias, wf_items wi
1894     where wi.item_type = 'WFERROR'
1895     and parent_item_type = itemtype
1896     and wi.parent_item_type = wias.item_type
1897     and wi.parent_item_key = wias.item_key
1898     and wi.parent_context = wias.process_activity
1899     and wi.end_date is null
1900     and wias.activity_status = 'COMPLETE';
1901 
1902  cursor c_error2 is
1903     select wi.item_key
1904     from wf_item_activity_statuses wias, wf_items wi
1905     where wi.item_type = 'WFERROR'
1906     and parent_item_type = itemtype
1907     and parent_item_key = itemkey
1908     and wi.parent_item_type = wias.item_type
1909     and wi.parent_item_key = wias.item_key
1910     and wi.parent_context = wias.process_activity
1911     and wi.end_date is null
1912     and wias.activity_status = 'COMPLETE';
1913 
1914  cursor c_error3 is
1915     select wi.item_key
1916     from wf_item_activity_statuses wias, wf_items wi
1917     where wi.item_type = 'WFERROR'
1918     and wi.parent_item_type = wias.item_type
1919     and wi.parent_item_key = wias.item_key
1920     and wi.parent_context = wias.process_activity
1921     and wi.end_date is null
1922     and wias.activity_status = 'COMPLETE';
1923 
1924 
1925  begin
1926 
1927    if ((itemkey is not null and (instr(itemkey,'%')>0 or itemtype is null))
1928       or (instr(itemtype,'%')>0)) then
1929           Wf_Core.Raise('WFSQL_ARGS');
1930    end if;
1931 
1932    -- Outer loop
1933    <<outer_abort>>
1934    loop
1935 
1936       if (itemtype is not null and itemkey is null) then
1937          open c_error1;
1938       elsif (itemtype is not null) then
1939          open c_error2;
1940       else
1941          open c_error3;
1942       end if;
1943 
1944      -- Inner loop
1945      <<abort_loop>>
1946      loop
1947 
1948         if (itemtype is not null and itemkey is null) then
1949            fetch c_error1 into  c_item_key;
1950            if (c_error1%notfound) then
1951               exit outer_abort;
1952            end if;
1953         elsif (itemtype is not null) then
1954            fetch c_error2 into c_item_key;
1955            if (c_error2%notfound) then
1956               exit outer_abort;
1957            end if;
1958         else
1959            fetch c_error3 into c_item_key;
1960            if (c_error3%notfound) then
1961               exit outer_abort;
1962            end if;
1963         end if;
1964 
1965         -- Abort the error process since the activity is now COMPLETE
1966         begin
1967 
1968            wf_engine.abortprocess('WFERROR', c_item_key);
1969 
1970         exception when others then
1971            null;
1972         end;
1973 
1974      end loop abort_loop;
1975 
1976       if (c_error1%ISOPEN) then
1977          close c_error1;
1978       end if;
1979 
1980       if (c_error2%ISOPEN) then
1981          close c_error2;
1982       end if;
1983 
1984       if (c_error3%ISOPEN) then
1985          close c_error3;
1986       end if;
1987 
1988    end loop outer_abort;
1989 
1990       if (c_error1%ISOPEN) then
1991          close c_error1;
1992       end if;
1993 
1994       if (c_error2%ISOPEN) then
1995          close c_error2;
1996       end if;
1997 
1998       if (c_error3%ISOPEN) then
1999          close c_error3;
2000       end if;
2001 
2002   EXCEPTION WHEN OTHERS THEN
2003       if (c_error1%ISOPEN) then
2004          close c_error1;
2005       end if;
2006 
2007       if (c_error2%ISOPEN) then
2008          close c_error2;
2009       end if;
2010 
2011       if (c_error3%ISOPEN) then
2012          close c_error3;
2013       end if;
2014 
2015       Wf_Core.Context('Wf_Purge', 'AbortErrorProcess', itemtype, itemkey);
2016       raise;
2017 
2018  end AbortErrorProcess;
2019 
2020 -- 3966635 Workflow Provisioning Project
2021 -- Following procedures are added in order not to loose the changes
2022 -- required.
2023 --  --
2024 --  -- procedure ProvisionRequests
2025 --  --   Delete old provisiong requests with end_time before argument.
2026 --  --   These provisioning requests are not tied explicity to a item.
2027 --  --   Those requests will be removed with the WF_PURGE.Item API.
2028 --  --
2029 --  -- IN:
2030 --  --   context - The context to match for the requests to remove.
2031 --  --   enddate - Date to obsolete to
2032 --  --   docommit- Do not commit if set to false
2033 --  -- NOTE:
2034 --  --
2035 --  procedure ProvisionRequests(
2036 --    context in varchar2,
2037 --    enddate in date,
2038 --    docommit in boolean)
2039 --  is
2040 --    l_context varchar2(320);
2041 --    l_prov_request_id number;
2042 --
2043 --    cursor c1(c_context in varchar2,
2044 --              c_end_date in date)
2045 --    is
2046 --    select prov_request_id
2047 --    from wf_prov_requests
2048 --    where source = 'EXTERNAL'
2049 --      and context like c_context
2050 --      and (end_date is not null and end_date < c_end_date);
2051 --
2052 --  begin
2053 --
2054 --    if (enddate > sysdate) then
2055 --      wf_core.context('WF_PURGE', 'ProvisionRequests');
2056 --      Wf_Core.Token('SYSDATE', to_char(sysdate));
2057 --      Wf_Core.Raise('WF_FUTURE_END_DATE');
2058 --    end if;
2059 --
2060 --    if context is null or length(context) = 0 then
2061 --      l_context := '%';
2062 --    else
2063 --      l_context := context;
2064 --    end if;
2065 --
2066 --    <<outer_prv_loop>>
2067 --    loop
2068 --
2069 --       open c1(l_context, enddate);
2070 --
2071 --       <<inner_prv_loop>>
2072 --       loop
2073 --          fetch c1 into l_prov_request_id;
2074 --          if c1%notfound then
2075 --             exit outer_prv_loop;
2076 --          end if;
2077 --
2078 --          delete from WF_PROV_REQUESTS
2079 --          where prov_request_id = l_prov_request_id;
2080 --
2081 --          exit inner_prv_loop when
2082 --          ( docommit and (c1%rowcount = wf_purge.commit_frequency));
2083 --
2084 --       end loop inner_prv_loop;
2085 --
2086 --       if c1%isopen then
2087 --          close c1;
2088 --       end if;
2089 --
2090 --       if (docommit) then
2091 --         commit;
2092 --         Fnd_Concurrent.Set_Preferred_RBS;
2093 --       end if;
2094 --
2095 --    end loop outer_prv_loop;
2096 --
2097 --
2098 --    if c1%isopen then
2099 --       close c1;
2100 --    end if;
2101 --
2102 --    if (docommit) then
2103 --      commit;
2104 --      Fnd_Concurrent.Set_Preferred_RBS;
2105 --    end if;
2106 --
2107 --  exception
2108 --    when others then
2109 --      if c1%isopen then
2110 --         close c1;
2111 --      end if;
2112 --      wf_core.context('WF_PURGE','ProvisionRequests', context, enddate);
2113 --  end  ProvisionRequests;
2114 --
2115 -- --
2116 -- -- ProvisionRequestsConcurrent
2117 -- --   Concurrent Program version of Total
2118 -- -- IN:
2119 -- --   errbuf - CPM error message
2120 -- --   retcode - CPM return code (0 = success, 1 = warning, 2 = error)
2121 -- --   itemtype - Item type to delete, or null for all itemtypes
2122 -- --   itemkey - Item key to delete, or null for all itemkeys
2123 -- --   age - Minimum age of data to purge (in days)
2124 -- --   x_persistence_type - Persistence Type to be purged: 'TEMP' or 'PERM'
2125 -- --   runtimeonly - If 'Y' then purge runtime data alone
2126 -- --
2127 -- procedure ProvisionRequestsConcurrent(
2128 --   errbuf out NOCOPY varchar2,
2129 --   retcode out NOCOPY varchar2,
2130 --   context in varchar2,
2131 --   age in varchar2 default 0,
2132 --   x_commit_frequency in number default 500
2133 --   )
2134 -- is
2135 --   enddate date;
2136 --   errname varchar2(30);
2137 --   errmsg varchar2(2000);
2138 --   errstack varchar2(2000);
2139 --
2140 --   l_runtimeonly boolean := FALSE;
2141 -- begin
2142 --   -- Convert arguments from varchar2 to real type.
2143 --   if age is not null and age > 0 then
2144 --     enddate := sysdate - to_number(age);
2145 --   else
2146 --     enddate := sysdate;
2147 --   end if;
2148 --
2149 --
2150 --   if x_commit_frequency is not null or x_commit_frequency > 0 then
2151 --     wf_purge.commit_frequency := x_commit_frequency;
2152 --   end if;
2153 --
2154 --   -- Call provisionrequest with new args
2155 --   Wf_Purge.ProvisionRequests(context,
2156 --                              enddate,
2157 --                              true);
2158 --
2159 --
2160 --   -- Return 0 for successful completion.
2161 --   errbuf := '';
2162 --   retcode := '0';
2163 --   wf_purge.commit_frequency := 500; -- reset to the default value
2164 --
2165 -- exception
2166 --   when others then
2167 --     -- Retrieve error message into errbuf
2168 --     wf_core.get_error(errname, errmsg, errstack);
2169 --     if (errmsg is not null) then
2170 --       errbuf := errmsg;
2171 --     else
2172 --       errbuf := sqlerrm;
2173 --     end if;
2174 --
2175 --     -- Return 2 for error.
2176 --     retcode := '2';
2177 
2178 --     -- Reset commit frequency to the default value
2179 --     wf_purge.commit_frequency := 500;
2180 -- end ProvisionRequestsConcurrent;
2181 
2182 
2183 
2184 end WF_PURGE;