DBA Data[Home] [Help]

PACKAGE BODY: APPS.FND_OAM_DSCFG_COMPILER_PKG

Source


1 PACKAGE BODY FND_OAM_DSCFG_COMPILER_PKG as
2 /* $Header: AFOAMDSCCOMPB.pls 120.8 2006/07/10 19:11:28 ilawler noship $ */
3 
4    ----------------------------------------
5    -- Private Body Constants
6    ----------------------------------------
7    PKG_NAME                     CONSTANT VARCHAR2(20) := 'DSCFG_COMPILER_PKG.';
8 
9    -- Defaults used when creating engine entities
10    B_DEFAULT_NUM_BUNDLES                CONSTANT NUMBER         := 1;
11    B_DEFAULT_RUN_MODE                   CONSTANT VARCHAR2(30)   := FND_OAM_DSCRAM_UTILS_PKG.G_MODE_NORMAL;
12 
13    --Pseudo-private state set and returned by public Getters
14    B_DEFAULT_WORKERS_ALLOWED            NUMBER                  := NULL; -- set to MAX(#CPU-1,1) by first call to compile_config_instance
15    B_DEFAULT_BATCH_SIZE                 CONSTANT NUMBER         := 10000;
16    B_DEFAULT_VALID_CHECK_INTERVAL       CONSTANT NUMBER         := 300; --5 minutes
17    -- # of blocks under which we don't parallelize, if NULL engine parallelizes everything with a weight.
18    -- NULL weights are always parallelized to be safe.  50 blocks corresponds to around 30k rows.
19    B_DEFAULT_MIN_PARALLEL_WEIGHT        CONSTANT NUMBER         := 50;
20 
21    -- Constant used for DSCRAM Unit phases for various types of objects in a dependency group
22    B_PHASE_INCREMENT                    CONSTANT NUMBER := 10;
23    B_BASE_PHASE_TRUNCATES               CONSTANT NUMBER := 1000000;
24    B_BASE_PHASE_UNBOUND_PLSQLS          CONSTANT NUMBER := 2000000;
25    B_BASE_PHASE_BOUND_OPERATIONS        CONSTANT NUMBER := 3000000;
26    B_BASE_PRIORITY_DELETES              CONSTANT NUMBER := 100;
27    B_BASE_PRIORITY_BOUND_PLSQLS         CONSTANT NUMBER := 200;
28    B_BASE_PRIORITY_UPDATES              CONSTANT NUMBER := 300;
29 
30    -- Default weight modifiers for various kinds of operations
31    -- TODO: these need some investigation
32    B_TRUNCATE_WEIGHT_MODIFIER           CONSTANT NUMBER := .01;
33    B_DELETE_WEIGHT_MODIFIER             CONSTANT NUMBER := 1.01; --according to explain plan cost, delete is a little more than a one column update
34 
35    -- The maximum length of a generated SQL statement.  This needs to be the max
36    -- varchar2 length - space typically used for adding AD Splitting clauses.
37    B_STMT_MAXLEN                        CONSTANT NUMBER := 3900;
38 
39    ----------------------------------------
40    -- Private Body Variables
41    ----------------------------------------
42 
43    --*****
44    --Types
45    --*****
46 
47    -- List of numbers
48    TYPE b_number_list_type IS TABLE OF NUMBER;
49 
50    -- List of domains
51    TYPE b_domain_list_type IS TABLE OF VARCHAR2(120);
52 
53    -- Map of domains for easy lookup
54    TYPE b_domain_map_type IS TABLE OF BOOLEAN INDEX BY VARCHAR2(120);
55 
56    -- Mapping from primary_domain name to list of object_ids with that primary domain
57    TYPE b_primary_domains_type IS TABLE OF b_number_list_type INDEX BY VARCHAR2(120);
58 
59    -- A dependency group loosely maps to a task in the engine.  The dependency group is a list of primary domains and additional
60    -- domains which overlap due to the definitions of the objects in these domains.
61    TYPE b_dependency_group_def_type IS RECORD
62       (
63        primary_domains          b_domain_list_type      := NULL,
64        additional_domains       b_domain_map_type,
65        task_id                  NUMBER                  := NULL,
66        priority                 NUMBER                  := NULL,
67        weight                   NUMBER                  := NULL
68        );
69 
70    -- Mapping from dependency_group_id to a record containing component primary domains/additional domains
71    TYPE b_dependency_groups_type IS TABLE OF b_dependency_group_def_type INDEX BY BINARY_INTEGER;
72 
73    -- Mapping from domain name to owning dependency_group_id
74    TYPE b_domain_to_group_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(120);
75 
76    -- Represents a DSCFG_PROPERTIES row.
77    TYPE b_property_def_type IS RECORD
78       (
79        property_id              NUMBER          := NULL,
80        property_name            VARCHAR2(120)   := NULL,
81        datatype                 VARCHAR2(30)    := NULL,
82        canonical_value          VARCHAR2(4000)  := NULL
83        );
84    -- Table of properties, indexed by property_id.
85    TYPE b_properties_type IS TABLE OF b_property_def_type INDEX BY BINARY_INTEGER;
86 
87    -- Represents a DSCFG_OBJECTS row.  Records the type most importantly but also any top level, object metadata.
88    TYPE b_object_def_type IS RECORD
89       (
90        object_id                NUMBER                  := NULL,
91        object_type              VARCHAR2(30)            := NULL,
92        target_type              VARCHAR2(30)            := NULL,
93        target_id                NUMBER                  := NULL,
94        new_errors_found_flag    VARCHAR2(3)             := NULL,
95        new_message              VARCHAR2(4000)          := NULL,  --new message to write to the object's message field
96        is_dirty                 BOOLEAN                 := FALSE,  --determines if we need to write the object out
97        primary_domain           VARCHAR2(120)           := NULL,
98        additional_domains       b_domain_list_type      := NULL
99        --properties     b_properties_type
100        );
101    -- master objects map, key=object_id, value = an object_def_type record.  Exists as an indirect
102    -- to allow other structures to be type-agnostic by using object_ids that can be looked up in this
103    -- htable to determine the htable where the object's definition is stored based on the object_type.
104    TYPE b_objects_type IS TABLE OF b_object_def_type INDEX BY BINARY_INTEGER;
105 
106    -- Record and Table types for DML_UPDATE_SEGMENT-typed objects.
107    TYPE b_dml_update_segment_def_type IS RECORD
108       (
109        table_owner      VARCHAR2(30)    := NULL,
110        table_name       VARCHAR2(30)    := NULL,
111        column_name      VARCHAR2(30)    := NULL,
112        new_column_value VARCHAR2(4000)  := NULL,
113        where_clause     VARCHAR2(4000)  := NULL,
114        weight_modifier  NUMBER          := 1
115        );
116    TYPE b_dml_update_segments_type IS TABLE OF b_dml_update_segment_def_type INDEX BY BINARY_INTEGER;
117 
118    -- Record and Table types for DML_DELETE-typed objects.
119    TYPE b_dml_delete_stmt_def_type IS RECORD
120       (
121        table_owner      VARCHAR2(30)    := NULL,
122        table_name       VARCHAR2(30)    := NULL,
123        where_clause     VARCHAR2(4000)  := NULL,
124        weight           NUMBER          := NULL
125        );
126    TYPE b_dml_delete_stmts_type IS TABLE OF b_dml_delete_stmt_def_type INDEX BY BINARY_INTEGER;
127 
128    -- Record and Table types for DML_TRUNCATE-typed objects.
129    TYPE b_dml_truncate_stmt_def_type IS RECORD
130       (
131        table_owner      VARCHAR2(30)    := NULL,
132        table_name       VARCHAR2(30)    := NULL,
133        weight           NUMBER          := NULL
134        );
135    TYPE b_dml_truncate_stmts_type IS TABLE OF b_dml_truncate_stmt_def_type INDEX BY BINARY_INTEGER;
136 
137    -- Record and Table types for PLSQL_TEXT-typed objects.
138    TYPE b_plsql_text_def_type IS RECORD
139       (
140        plsql_text       VARCHAR2(4000)  := NULL,
141        table_owner      VARCHAR2(30)    := NULL, --used for table-specific splittable pl/sqls
142        table_name       VARCHAR2(30)    := NULL,
143        weight           NUMBER          := NULL
144        );
145    TYPE b_plsql_texts_type IS TABLE OF b_plsql_text_def_type INDEX BY BINARY_INTEGER;
146 
147    -- Holds metadata concering the dscram_run entity
148    TYPE b_run_type IS RECORD
149       (
150        object_id                NUMBER          := NULL,
151        run_id                   NUMBER          := NULL,
152        run_mode                 VARCHAR2(30)    := NULL,
153        valid_check_interval     NUMBER          := NULL,
154        num_bundles              NUMBER          := NULL,
155        weight                   NUMBER          := NULL,
156        assigned_physical_weight NUMBER          := 0
157        );
158 
159    -- Holds metadata concering the dscram_bundle entity
160    TYPE b_bundle_def_type IS RECORD
161       (
162        bundle_id                NUMBER          := NULL,
163        target_hostname          VARCHAR2(256)   := NULL,
164        workers_allowed          NUMBER          := NULL,
165        batch_size               NUMBER          := NULL,
166        min_parallel_unit_weight NUMBER          := NULL,
167        weight                   NUMBER          := NULL,
168        assigned_physical_weight NUMBER          := 0,
169        assigned_task_count      NUMBER          := 0
170        );
171    TYPE b_bundles_type IS TABLE OF b_bundle_def_type INDEX BY BINARY_INTEGER;
172 
173    -- Holds metadata related to a particular domain
174    TYPE b_domain_metadata_type IS RECORD
175       (
176        -- TODO: allow domain_metadata to specify a target_hostname to allow user-driven task partitioning.
177        weight                   NUMBER          := NULL,
178        priority                 NUMBER          := NULL,
179        phase                    NUMBER          := NULL,
180        workers_allowed          NUMBER          := NULL,
181        disable_splitting        VARCHAR2(3)     := NULL,
182        error_fatality_level     VARCHAR2(30)    := NULL,
183        batch_size               NUMBER          := NULL
184        );
185    -- Map from object_type(may be null) to domain_metadata record
186    TYPE b_domain_obj_metadata_map_type IS TABLE OF b_domain_metadata_type INDEX BY VARCHAR2(30);
187    -- Map from domain name to (Map of object types-> domain metadata).  Allows us to stripe metadata by particular types of
188    -- objects.
189    TYPE b_domain_metadata_map_type IS TABLE OF b_domain_obj_metadata_map_type INDEX BY VARCHAR2(120);
190 
191    -- Map from a host name to the bundle object_id that represents it
192    TYPE b_host_name_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(256);
193 
194    -- Map from column_name -> object_id
195    TYPE b_column_name_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(30);
196 
197    -- Map from where_clause -> map of column_name->object_id
198    TYPE b_where_clause_column_map_type IS TABLE OF b_column_name_map_type INDEX BY VARCHAR2(4000);
199 
200    -- Map from where_clause -> list of object_ids
201    TYPE b_where_clause_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(4000);
202 
203    -- Stores references to the various objects under a given table owner/name
204    TYPE b_table_objects_def_type IS RECORD
205       (
206        update_map       b_where_clause_column_map_type,
207        delete_map       b_where_clause_map_type,
208        plsqls           b_number_list_type := b_number_list_type()
209        );
210 
211    -- Map from table_name -> table_objects_def_type struct
212    TYPE b_table_name_map_type IS TABLE OF b_table_objects_def_type INDEX BY VARCHAR2(30);
213 
214    -- Map from table_owner -> map from table_name->table_objects struct containing corresponding object_ids
215    TYPE b_table_bound_map_type IS TABLE OF b_table_name_map_type INDEX BY VARCHAR2(30);
216 
217    --*********
218    --State
219    --*********
220 
221    --b_config_instance_id               NUMBER := NULL;
222    -- Package level hash-tables to store parsed data fetched from
223    b_objects                    b_objects_type;
224    b_dml_update_segments        b_dml_update_segments_type;
225    b_dml_delete_stmts           b_dml_delete_stmts_type;
226    b_dml_truncate_stmts         b_dml_truncate_stmts_type;
227    b_plsql_texts                b_plsql_texts_type;
228 
229    b_primary_domains            b_primary_domains_type;
230    b_domain_metadata_map        b_domain_metadata_map_type;
231 
232    b_dependency_groups          b_dependency_groups_type;
233    b_domain_to_group_map        b_domain_to_group_map_type; --transient, used while coalescing domains into dependency groups
234 
235    b_run                        b_run_type;
236    b_bundles                    b_bundles_type;
237 
238    b_host_name_map              b_host_name_map_type;
239    ----------------------------------------
240    -- Public/Private Procedures/Functions
241    ----------------------------------------
242 
243    -- Private, allocates a new property definition type
244    FUNCTION CREATE_PROPERTY_DEF(p_property_id           IN NUMBER,
245                                 p_property_name         IN VARCHAR2,
246                                 p_datatype              IN VARCHAR2,
247                                 p_canonical_value       IN VARCHAR2)
248       RETURN b_property_def_type
249    IS
250       l_property        b_property_def_type;
251    BEGIN
252       l_property.property_id            := p_property_id;
253       l_property.property_name          := p_property_name;
254       l_property.datatype               := p_datatype;
255       l_property.canonical_value        := p_canonical_value;
256 
257       RETURN l_property;
258    END;
259 
260    -- Private, allocates a new object definition type
261    FUNCTION CREATE_OBJECT_DEF(p_object_id       IN NUMBER,
262                               p_object_type     IN VARCHAR2,
263                               p_target_type     IN VARCHAR2 DEFAULT NULL,
264                               p_target_id       IN NUMBER DEFAULT NULL)
265       RETURN b_object_def_type
266    IS
267       l_object  b_object_def_type;
268    BEGIN
269       l_object.object_id        := p_object_id;
270       l_object.object_type      := p_object_type;
271       l_object.target_type      := p_target_type;
272       l_object.target_id        := p_target_id;
273 
274       RETURN l_object;
275    END;
276 
277    -- Private, allocates a new property definition type
278    FUNCTION CREATE_DEPENDENCY_GROUP_DEF(p_primary_domain        IN VARCHAR2)
279       RETURN b_dependency_group_def_type
280    IS
281       l_group   b_dependency_group_def_type;
282    BEGIN
283       l_group.primary_domains           := b_domain_list_type(p_primary_domain);
284 
285       RETURN l_group;
286    END;
287 
288    -- Private, allocates a new property definition type
289    FUNCTION CREATE_BUNDLE_DEF(p_target_hostname         IN VARCHAR2)
290 
291       RETURN b_bundle_def_type
292    IS
293       l_bundle  b_bundle_def_type;
294    BEGIN
295       l_bundle.target_hostname          := p_target_hostname;
296       l_bundle.workers_allowed          := GET_DEFAULT_NUM_WORKERS;
297       l_bundle.batch_size               := GET_DEFAULT_BATCH_SIZE;
298       l_bundle.min_parallel_unit_weight := GET_DFLT_MIN_PARALLEL_WEIGHT;
299 
300       RETURN l_bundle;
301    END;
302 
303    -- Private, allocates a new table_objects_type.
304    FUNCTION CREATE_TABLE_OBJECTS_DEF
305       RETURN b_table_objects_def_type
306    IS
307       l_table_objects   b_table_objects_def_type;
308    BEGIN
309 
310       RETURN l_table_objects;
311    END;
312 
313    -- Private, attach an error message
314    PROCEDURE MARK_OBJECT_AS_ERRORED(px_object           IN OUT NOCOPY b_object_def_type,
315                                     p_message           IN VARCHAR2)
316    IS
317    BEGIN
318       px_object.new_errors_found_flag := FND_API.G_TRUE;
319       IF px_object.new_message IS NULL THEN
320          px_object.new_message := p_message;
321       ELSE
322          px_object.new_message := px_object.new_message||', '||p_message;
323       END IF;
324       px_object.is_dirty := TRUE;
325       RETURN;
326    END;
327 
328    -- Private, attach an error message
329    PROCEDURE MARK_OBJECT_AS_ERRORED(p_object_id         IN NUMBER,
330                                     p_message           IN VARCHAR2)
331    IS
332    BEGIN
333       b_objects(p_object_id).new_errors_found_flag := FND_API.G_TRUE;
334       IF b_objects(p_object_id).new_message IS NULL THEN
335          b_objects(p_object_id).new_message := p_message;
336       ELSE
337          b_objects(p_object_id).new_message := b_objects(p_object_id).new_message||', '||p_message;
338       END IF;
339       b_objects(p_object_id).is_dirty := TRUE;
340       RETURN;
341    END;
342 
343    -- Wrapper on other mark_object_as_errored calls for exceptions.
344    PROCEDURE MARK_OBJECT_AS_ERRORED(p_object_id         IN NUMBER,
345                                     p_ctxt              IN VARCHAR2,
346                                     p_error_code        IN NUMBER,
347                                     p_error_msg         IN VARCHAR2)
348    IS
349       l_msg     VARCHAR2(4000);
350    BEGIN
351       l_msg := 'Exception: (Code('||p_error_code||'), Message("'||p_error_msg||'"))';
352       fnd_oam_debug.log(3, p_ctxt, l_msg);
353       l_msg := '['||p_ctxt||']'||l_msg;
354       MARK_OBJECT_AS_ERRORED(p_object_id,
355                              l_msg);
356    END;
357 
358    -- Private, attach a warning message
359    PROCEDURE MARK_OBJECT_WITH_WARNING(p_object_id       IN NUMBER,
360                                       p_ctxt            IN VARCHAR2,
361                                       p_message         IN VARCHAR2)
362    IS
363       l_prefix  VARCHAR2(30) := 'WARNING: ';
364       l_msg     VARCHAR2(4000);
365    BEGIN
366       -- prepare the message
367       IF length(p_message) + length(l_prefix) < 4000 THEN
368          l_msg := l_prefix||p_message;
369       ELSE
370          l_msg := p_message;
371       END IF;
372       --don't log a level 3 because this is a common case.
373       fnd_oam_debug.log(1, p_ctxt, l_msg);
374 
375       IF b_objects(p_object_id).new_message IS NULL THEN
376          b_objects(p_object_id).new_message := l_msg;
377       ELSE
378          b_objects(p_object_id).new_message := b_objects(p_object_id).new_message||', '||l_msg;
379       END IF;
380       b_objects(p_object_id).is_dirty := TRUE;
381 
382       RETURN;
383    END;
384 
385    -- Private, helper to the CACHE_<OBJECT_TYPE> procedures to handle properties
386    -- which are common by setting them in the object.  Returns a boolean of whether the property was
387    -- handled sucessfully.
388    PROCEDURE HANDLE_INVALID_PROPERTY(p_ctxt             IN VARCHAR2,
389                                      px_object          IN OUT NOCOPY b_object_def_type,
390                                      px_prop            IN OUT NOCOPY b_property_def_type,
391                                      p_error_code       IN NUMBER,
392                                      p_error_msg        IN VARCHAR2)
393    IS
394       l_msg             VARCHAR2(4000);
395    BEGIN
396       l_msg := 'Invalid Property "'||px_prop.property_name||'"('||px_prop.property_id||'), Error: (Code('||p_error_code||'), Message("'||p_error_msg||'"))';
397       fnd_oam_debug.log(3, p_ctxt, l_msg);
398       MARK_OBJECT_AS_ERRORED(px_object,
399                              l_msg);
400    END;
401 
402    -- Private, used to mark an object as errored when an unknown property is seen.  This can be made a warning but
403    -- for now it's an error to keep people from seeding attributes that are ignored by the engine.
404    PROCEDURE HANDLE_UNKNOWN_PROPERTY(p_ctxt             IN VARCHAR2,
405                                      px_object          IN OUT NOCOPY b_object_def_type,
406                                      px_prop            IN OUT NOCOPY b_property_def_type)
407    IS
408       l_msg             VARCHAR2(4000);
409    BEGIN
410       l_msg := 'Unrecognized Property: '||px_prop.property_name||'('||px_prop.property_id||')';
411       fnd_oam_debug.log(3, p_ctxt, l_msg);
412       --unrecognized property
413       MARK_OBJECT_AS_ERRORED(px_object,
414                              l_msg);
415    END;
416 
417    -- Private, helper to the CACHE_<OBJECT_TYPE> procedures to handle properties
418    -- which are common by setting them in the object.  Returns a boolean of whether the property was
419    -- handled sucessfully.
420    FUNCTION HANDLE_COMMON_PROPERTY(px_object            IN OUT NOCOPY b_object_def_type,
421                                    px_prop              IN OUT NOCOPY b_property_def_type)
422       RETURN BOOLEAN
423    IS
424       l_ctxt            VARCHAR2(60) := PKG_NAME||'HANDLE_COMMON_PROPERTY';
425       l_msg             VARCHAR2(4000);
426    BEGIN
427       CASE px_prop.property_name
428          WHEN FND_OAM_DSCFG_API_PKG.G_PROP_PRIMARY_DOMAIN THEN
429             --force all domains to be case insensitive
430             px_object.primary_domain := UPPER(TRIM(px_prop.canonical_value));
431 
432          WHEN FND_OAM_DSCFG_API_PKG.G_PROP_ADDITIONAL_DOMAIN THEN
433             --the additional domain can't be null
434             IF px_prop.canonical_value IS NULL THEN
435                l_msg := 'Additional Domain properties cannot have NULL values.';
436                fnd_oam_debug.log(3, l_ctxt, l_msg);
437                --unrecognized property
438                MARK_OBJECT_AS_ERRORED(px_object,
439                                       l_msg);
440                RETURN FALSE;
441             END IF;
442 
443             --append the domain
444             fnd_oam_debug.log(1, l_ctxt, 'found additional_domain: '||px_prop.canonical_value);
445             IF px_object.additional_domains IS NOT NULL THEN
446                px_object.additional_domains.EXTEND;
447                px_object.additional_domains(px_object.additional_domains.COUNT) := UPPER(TRIM(px_prop.canonical_value));
448             ELSE
449                px_object.additional_domains := b_domain_list_type(UPPER(TRIM(px_prop.canonical_value)));
450             END IF;
451 
452          ELSE
453             HANDLE_UNKNOWN_PROPERTY(l_ctxt,
454                                     px_object,
455                                     px_prop);
456             RETURN FALSE;
457       END CASE;
458 
459       -- sucessfully handled
460       RETURN TRUE;
461    EXCEPTION
462       WHEN OTHERS THEN
463          HANDLE_INVALID_PROPERTY(l_ctxt,
464                                  px_object,
465                                  px_prop,
466                                  SQLCODE,
467                                  SQLERRM);
468          RETURN FALSE;
469    END;
470 
471    -- Private, simple helper to get rid of extra spaces and the 'where' if present.
472    PROCEDURE CLEANUP_WHERE_CLAUSE(px_where_clause       IN OUT NOCOPY VARCHAR2)
473    IS
474       l_str     VARCHAR2(30);
475    BEGIN
476       px_where_clause := TRIM(px_where_clause);
477       IF UPPER(SUBSTR(px_where_clause, 1, 6)) = 'WHERE ' THEN
478          px_where_clause := SUBSTR(px_where_clause, 7);
479       END IF;
480    END;
481 
482    -- Private, Helper to ADD_ENGINE_UNITS_FOR_DOMAIN to validate an object of type DML_UPDATE_SEGMENT.
483    -- Returns TRUE if valid.
484    FUNCTION VALIDATE_DML_UPDATE_SEGMENT(px_object       IN OUT NOCOPY b_object_def_type,
485                                         p_segment       IN b_dml_update_segment_def_type)
486       RETURN BOOLEAN
487    IS
488       l_ctxt            VARCHAR2(60) := PKG_NAME||'VALIDATE_DML_UPDATE_SEGMENT';
489 
490       l_valid                   BOOLEAN := TRUE;
491    BEGIN
492       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
493 
494       --check for our properties, add messages for each not found
495       IF p_segment.table_owner IS NULL THEN
496          MARK_OBJECT_AS_ERRORED(px_object,
497                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER);
498          l_valid := FALSE;
499       END IF;
500 
501       IF p_segment.table_name IS NULL THEN
502          MARK_OBJECT_AS_ERRORED(px_object,
503                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME);
504          l_valid := FALSE;
505       END IF;
506 
507       IF p_segment.column_name IS NULL THEN
508          MARK_OBJECT_AS_ERRORED(px_object,
509                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_COLUMN_NAME);
510          l_valid := FALSE;
511       END IF;
512 
513       IF p_segment.new_column_value IS NULL THEN
514          MARK_OBJECT_AS_ERRORED(px_object,
515                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_NEW_COLUMN_VALUE);
516          l_valid := FALSE;
517       END IF;
518 
519       fnd_oam_debug.log(1, l_ctxt, 'Verdict: '||FND_OAM_DSCFG_UTILS_PKG.BOOLEAN_TO_CANONICAL(l_valid));
520 
521       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
522       RETURN l_valid;
523    EXCEPTION
524       WHEN OTHERS THEN
525          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
526          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
527          RAISE PROGRAM_ERROR;
528    END;
529 
530    -- Private, helper to CACHE_OBJECT to cache an object of type DML_UPDATE_SEGMENT
531    --
532    PROCEDURE CACHE_DML_UPDATE_SEGMENT(px_object         IN OUT NOCOPY b_object_def_type,
533                                       px_object_props   IN OUT NOCOPY b_properties_type)
534    IS
535       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_DML_UPDATE_SEGMENT';
536 
537       l_dml_update_segment      b_dml_update_segment_def_type;
538 
539       k                         NUMBER;
540       l_prop                    b_property_def_type := NULL;
541       l_msg                     VARCHAR2(4000);
542    BEGIN
543       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
544 
545       --loop through the object properties, filling in the details of the dml_update_segment
546       BEGIN
547          k := px_object_props.FIRST;
548          WHILE k IS NOT NULL LOOP
549             l_prop := px_object_props(k);
550 
551             CASE l_prop.property_name
552                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER THEN
553                   l_dml_update_segment.table_owner := l_prop.canonical_value;
554 
555                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME THEN
556                   l_dml_update_segment.table_name := l_prop.canonical_value;
557 
558                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_COLUMN_NAME THEN
559                   l_dml_update_segment.column_name := l_prop.canonical_value;
560 
561                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_NEW_COLUMN_VALUE THEN
562                   l_dml_update_segment.new_column_value := l_prop.canonical_value;
563 
564                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WHERE_CLAUSE THEN
565                   l_dml_update_segment.where_clause := l_prop.canonical_value;
566 
567                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT_MODIFIER THEN
568                   l_dml_update_segment.weight_modifier := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
569 
570                ELSE
571                   --defer to the common handler if none of the object_type-specific property names match.
572                   IF NOT HANDLE_COMMON_PROPERTY(px_object,
573                                                 l_prop) THEN
574                      fnd_oam_debug.log(2, l_ctxt, 'EXIT');
575                      RETURN;
576                   END IF;
577             END CASE;
578 
579             k := px_object_props.NEXT(k);
580          END LOOP;
581       EXCEPTION
582          WHEN OTHERS THEN
583             -- covers character string buffer too small
584             HANDLE_INVALID_PROPERTY(l_ctxt,
585                                     px_object,
586                                     l_prop,
587                                     SQLCODE,
588                                     SQLERRM);
589             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
590             RETURN;
591       END;
592 
593       -- only cache it if it's valid
594       IF VALIDATE_DML_UPDATE_SEGMENT(px_object,
595                                      l_dml_update_segment) THEN
596 
597          --if it's valid, do some cleanup
598          l_dml_update_segment.table_owner := UPPER(TRIM(l_dml_update_segment.table_owner));
599          l_dml_update_segment.table_name := UPPER(TRIM(l_dml_update_segment.table_name));
600          IF l_dml_update_segment.where_clause IS NOT NULL THEN
601             CLEANUP_WHERE_CLAUSE(l_dml_update_segment.where_clause);
602          END IF;
603 
604          --default the primary_domain using the table_owner.table_name if not specified as a property
605          IF px_object.primary_domain IS NULL THEN
606             px_object.primary_domain := l_dml_update_segment.table_owner||'.'||l_dml_update_segment.table_name;
607          END IF;
608 
609          --put it in the dml_update_segments cache
610          b_dml_update_segments(px_object.object_id) := l_dml_update_segment;
611       END IF;
612 
613       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
614    EXCEPTION
615       WHEN PROGRAM_ERROR THEN
616          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
617          RAISE;
618       WHEN OTHERS THEN
619          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
620          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
621          RAISE PROGRAM_ERROR;
622    END;
623 
624    -- Private, Helper to ADD_ENGINE_UNITS_FOR_DOMAIN to validate an object of type DML_DELETE_STMT
625    -- Returns TRUE if valid.
626    FUNCTION VALIDATE_DML_DELETE_STMT(px_object  IN OUT NOCOPY b_object_def_type,
627                                      p_stmt     IN b_dml_delete_stmt_def_type)
628       RETURN BOOLEAN
629    IS
630       l_ctxt            VARCHAR2(60) := PKG_NAME||'VALIDATE_DML_DELETE_STMT';
631 
632       l_valid                   BOOLEAN := TRUE;
633    BEGIN
634       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
635 
636       --check for our properties, add messages for each not found
637       IF p_stmt.table_owner IS NULL THEN
638          MARK_OBJECT_AS_ERRORED(px_object,
639                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER);
640          l_valid := FALSE;
641       END IF;
642 
643       IF p_stmt.table_name IS NULL THEN
644          MARK_OBJECT_AS_ERRORED(px_object,
645                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME);
646          l_valid := FALSE;
647       END IF;
648 
649       fnd_oam_debug.log(1, l_ctxt, 'Verdict: '||FND_OAM_DSCFG_UTILS_PKG.BOOLEAN_TO_CANONICAL(l_valid));
650 
651       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
652       RETURN l_valid;
653    EXCEPTION
654       WHEN OTHERS THEN
655          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
656          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
657          RAISE PROGRAM_ERROR;
658    END;
659 
660    -- Private, helper to CACHE_OBJECT to cache an object of type DML_DELETE_STMT
661    --
662    PROCEDURE CACHE_DML_DELETE_STMT(px_object            IN OUT NOCOPY b_object_def_type,
663                                    px_object_props      IN OUT NOCOPY b_properties_type)
664    IS
665       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_DML_DELETE_STMT';
666 
667       l_dml_delete_stmt         b_dml_delete_stmt_def_type;
668 
669       k                         NUMBER;
670       l_prop                    b_property_def_type := NULL;
671       l_msg                     VARCHAR2(4000);
672    BEGIN
673       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
674 
675       --loop through the object properties, filling in the details of the dml_delete_stmt
676       BEGIN
677          k := px_object_props.FIRST;
678          WHILE k IS NOT NULL LOOP
679             l_prop := px_object_props(k);
680 
681             CASE l_prop.property_name
682                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER THEN
683                   l_dml_delete_stmt.table_owner := l_prop.canonical_value;
684 
685                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME THEN
686                   l_dml_delete_stmt.table_name := l_prop.canonical_value;
687 
688                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WHERE_CLAUSE THEN
689                   l_dml_delete_stmt.where_clause := l_prop.canonical_value;
690 
691                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT THEN
692                   l_dml_delete_stmt.weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
693 
694                ELSE
695                   --defer to the common handler if none of the object_type-specific property names match.
696                   IF NOT HANDLE_COMMON_PROPERTY(px_object,
697                                                 l_prop) THEN
698                      fnd_oam_debug.log(2, l_ctxt, 'EXIT');
699                      RETURN;
700                   END IF;
701             END CASE;
702 
703             k := px_object_props.NEXT(k);
704          END LOOP;
705       EXCEPTION
706          WHEN OTHERS THEN
707             -- covers character string buffer too small
708             HANDLE_INVALID_PROPERTY(l_ctxt,
709                                     px_object,
710                                     l_prop,
711                                     SQLCODE,
712                                     SQLERRM);
713             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
714             RETURN;
715       END;
716 
717       -- only cache it if it's valid
718       IF VALIDATE_DML_DELETE_STMT(px_object,
719                                   l_dml_delete_stmt) THEN
720 
721          --if it's valid, do some cleanup
722          l_dml_delete_stmt.table_owner := UPPER(TRIM(l_dml_delete_stmt.table_owner));
723          l_dml_delete_stmt.table_name := UPPER(TRIM(l_dml_delete_stmt.table_name));
724          IF l_dml_delete_stmt.where_clause IS NOT NULL THEN
725             CLEANUP_WHERE_CLAUSE(l_dml_delete_stmt.where_clause);
726          END IF;
727 
728          --default the primary_domain using the table_owner.table_name if not specified as a property
729          IF px_object.primary_domain IS NULL THEN
730             px_object.primary_domain := l_dml_delete_stmt.table_owner||'.'||l_dml_delete_stmt.table_name;
731          END IF;
732 
733          --put it in the dml_delete_stmts cache
734          b_dml_delete_stmts(px_object.object_id) := l_dml_delete_stmt;
735       END IF;
736 
737       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
738    EXCEPTION
739       WHEN PROGRAM_ERROR THEN
740          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
741          RAISE;
742       WHEN OTHERS THEN
743          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
744          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
745          RAISE PROGRAM_ERROR;
746    END;
747 
748    -- Private, Helper to ADD_ENGINE_UNITS_FOR_DOMAIN to validate an object of type DML_TRUNCATE_STMT
749    -- Returns TRUE if valid.
750    FUNCTION VALIDATE_DML_TRUNCATE_STMT(px_object        IN OUT NOCOPY b_object_def_type,
751                                        p_stmt           IN b_dml_truncate_stmt_def_type)
752       RETURN BOOLEAN
753    IS
754       l_ctxt            VARCHAR2(60) := PKG_NAME||'VALIDATE_DML_TRUNCATE_STMT';
755 
756       l_valid                   BOOLEAN := TRUE;
757    BEGIN
758       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
759 
760       --check for our properties, add messages for each not found
761       IF p_stmt.table_owner IS NULL THEN
762          MARK_OBJECT_AS_ERRORED(px_object,
763                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER);
764          l_valid := FALSE;
765       END IF;
766 
767       IF p_stmt.table_name IS NULL THEN
768          MARK_OBJECT_AS_ERRORED(px_object,
769                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME);
770          l_valid := FALSE;
771       END IF;
772 
773       fnd_oam_debug.log(1, l_ctxt, 'Verdict: '||FND_OAM_DSCFG_UTILS_PKG.BOOLEAN_TO_CANONICAL(l_valid));
774 
775       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
776       RETURN l_valid;
777    EXCEPTION
778       WHEN OTHERS THEN
779          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
780          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
781          RAISE PROGRAM_ERROR;
782    END;
783 
784    -- Private, helper to CACHE_OBJECT to cache an object of type DML_TRUNCATE_STMT
785    --
786    PROCEDURE CACHE_DML_TRUNCATE_STMT(px_object          IN OUT NOCOPY b_object_def_type,
787                                      px_object_props    IN OUT NOCOPY b_properties_type)
788    IS
789       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_DML_TRUNCATE_STMT';
790 
791       l_dml_truncate_stmt       b_dml_truncate_stmt_def_type;
792 
793       k                         NUMBER;
794       l_prop                    b_property_def_type := NULL;
795       l_msg                     VARCHAR2(4000);
796    BEGIN
797       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
798 
799       --loop through the object properties, filling in the details of the dml_truncate_stmt
800       BEGIN
801          k := px_object_props.FIRST;
802          WHILE k IS NOT NULL LOOP
803             l_prop := px_object_props(k);
804 
805             CASE l_prop.property_name
806                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER THEN
807                   l_dml_truncate_stmt.table_owner := l_prop.canonical_value;
808 
809                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME THEN
810                   l_dml_truncate_stmt.table_name := l_prop.canonical_value;
811 
812                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT THEN
813                   l_dml_truncate_stmt.weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
814 
815                ELSE
816                   --defer to the common handler if none of the object_type-specific property names match.
817                   IF NOT HANDLE_COMMON_PROPERTY(px_object,
818                                                 l_prop) THEN
819                      fnd_oam_debug.log(2, l_ctxt, 'EXIT');
820                      RETURN;
821                   END IF;
822             END CASE;
823 
824             k := px_object_props.NEXT(k);
825          END LOOP;
826       EXCEPTION
827          WHEN OTHERS THEN
828             -- covers character string buffer too small
829             HANDLE_INVALID_PROPERTY(l_ctxt,
830                                     px_object,
831                                     l_prop,
832                                     SQLCODE,
833                                     SQLERRM);
834             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
835             RETURN;
836       END;
837 
838       -- only cache it if it's valid
839       IF VALIDATE_DML_TRUNCATE_STMT(px_object,
840                                     l_dml_truncate_stmt) THEN
841 
842          --if it's valid, do some cleanup
843          l_dml_truncate_stmt.table_owner := UPPER(TRIM(l_dml_truncate_stmt.table_owner));
844          l_dml_truncate_stmt.table_name := UPPER(TRIM(l_dml_truncate_stmt.table_name));
845 
846          --default the primary_domain using the table_owner.table_name if not specified as a property
847          IF px_object.primary_domain IS NULL THEN
848             px_object.primary_domain := l_dml_truncate_stmt.table_owner||'.'||l_dml_truncate_stmt.table_name;
849          END IF;
850 
851          --put it in the dml_truncate_stmts cache
852          b_dml_truncate_stmts(px_object.object_id) := l_dml_truncate_stmt;
853       END IF;
854 
855       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
856    EXCEPTION
857       WHEN PROGRAM_ERROR THEN
858          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
859          RAISE;
860       WHEN OTHERS THEN
861          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
862          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
863          RAISE PROGRAM_ERROR;
864    END;
865 
866    -- Private, Helper to ADD_ENGINE_UNITS_FOR_DOMAIN to validate an object of type PLSQL_TEXT
867    -- Returns TRUE if valid.
868    FUNCTION VALIDATE_PLSQL_TEXT(px_object       IN OUT NOCOPY b_object_def_type,
869                                 p_plsql         IN b_plsql_text_def_type)
870       RETURN BOOLEAN
871    IS
872       l_ctxt            VARCHAR2(60) := PKG_NAME||'VALIDATE_PLSQL_TEXT';
873 
874       l_valid                   BOOLEAN := TRUE;
875    BEGIN
876       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
877 
878       --check for our properties, add messages for each not found
879       IF p_plsql.plsql_text IS NULL THEN
880          MARK_OBJECT_AS_ERRORED(px_object,
881                                 'Missing Property: '||FND_OAM_DSCFG_API_PKG.G_PROP_PLSQL_TEXT);
882          l_valid := FALSE;
883       END IF;
884 
885       fnd_oam_debug.log(1, l_ctxt, 'Verdict: '||FND_OAM_DSCFG_UTILS_PKG.BOOLEAN_TO_CANONICAL(l_valid));
886 
887       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
888       RETURN l_valid;
889    EXCEPTION
890       WHEN OTHERS THEN
891          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
892          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
893          RAISE PROGRAM_ERROR;
894    END;
895 
896    -- Private, helper to CACHE_OBJECT to cache an object of type PLSQL_TEXT
897    --
898    PROCEDURE CACHE_PLSQL_TEXT(px_object         IN OUT NOCOPY b_object_def_type,
899                               px_object_props   IN OUT NOCOPY b_properties_type)
900    IS
901       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_PLSQL_TEXT';
902 
903       l_plsql_text      b_plsql_text_def_type;
904 
905       k                         NUMBER;
906       l_prop                    b_property_def_type := NULL;
907       l_msg                     VARCHAR2(4000);
908    BEGIN
909       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
910 
911       --loop through the object properties, filling in the details of the plsql_text
912       BEGIN
913          k := px_object_props.FIRST;
914          WHILE k IS NOT NULL LOOP
915             l_prop := px_object_props(k);
916 
917             CASE l_prop.property_name
918                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_PLSQL_TEXT THEN
919                   l_plsql_text.plsql_text := l_prop.canonical_value;
920 
921                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_OWNER THEN
922                   l_plsql_text.table_owner := l_prop.canonical_value;
923 
924                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TABLE_NAME THEN
925                   l_plsql_text.table_name := l_prop.canonical_value;
926 
927                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT THEN
928                   l_plsql_text.weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
929 
930                ELSE
931                   --defer to the common handler if none of the object_type-specific property names match.
932                   IF NOT HANDLE_COMMON_PROPERTY(px_object,
933                                                 l_prop) THEN
934                      fnd_oam_debug.log(2, l_ctxt, 'EXIT');
935                      RETURN;
936                   END IF;
937             END CASE;
938 
939             k := px_object_props.NEXT(k);
940          END LOOP;
941       EXCEPTION
942          WHEN OTHERS THEN
943             -- covers character string buffer too small
944             HANDLE_INVALID_PROPERTY(l_ctxt,
945                                     px_object,
946                                     l_prop,
947                                     SQLCODE,
948                                     SQLERRM);
949             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
950             RETURN;
951       END;
952 
953       -- only cache it if it's valid
954       IF VALIDATE_PLSQL_TEXT(px_object,
955                              l_plsql_text) THEN
956 
957          -- try to default the primary_domain using the table_owner/name if specified
958          IF px_object.primary_domain IS NULL THEN
959             IF l_plsql_text.table_owner IS NOT NULL AND l_plsql_text.table_name IS NOT NULL THEN
960                px_object.primary_domain := l_plsql_text.table_owner||'.'||l_plsql_text.table_name;
961             END IF;
962          END IF;
963 
964          --put it in the plsql_texts cache
965          b_plsql_texts(px_object.object_id) := l_plsql_text;
966       END IF;
967 
968       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
969    EXCEPTION
970       WHEN PROGRAM_ERROR THEN
971          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
972          RAISE;
973       WHEN OTHERS THEN
974          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
975          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
976          RAISE PROGRAM_ERROR;
977    END;
978 
979    -- Private, helper to CACHE_OBJECT to cache the run metadata information
980    --
981    PROCEDURE CACHE_RUN(px_object        IN OUT NOCOPY b_object_def_type,
982                        px_object_props  IN OUT NOCOPY b_properties_type)
983    IS
984       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_RUN';
985 
986       k                         NUMBER;
987       l_prop                    b_property_def_type := NULL;
988       l_msg                     VARCHAR2(4000);
989    BEGIN
990       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
991 
992       --if we've already initialized the run metadata, this object is invalid, we need only one
993       --run metadata entry per config instance
994       IF b_run.object_id IS NOT NULL THEN
995          l_msg := 'Run Metadata already defined by Object ID: "'||b_run.object_id||'"';
996          fnd_oam_debug.log(3, l_ctxt, l_msg);
997          MARK_OBJECT_AS_ERRORED(px_object,
998                                 l_msg);
999          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1000          RETURN;
1001       END IF;
1002 
1003       --loop through the object properties, filling in the details of the run object
1004       BEGIN
1005          k := px_object_props.FIRST;
1006          WHILE k IS NOT NULL LOOP
1007             l_prop := px_object_props(k);
1008 
1009             CASE l_prop.property_name
1010                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_RUN_ID THEN
1011                   b_run.run_id := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1012 
1013                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_RUN_MODE THEN
1014                   b_run.run_mode := l_prop.canonical_value;
1015 
1016                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_VALID_CHECK_INTERVAL THEN
1017                   b_run.valid_check_interval := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1018 
1019                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_NUM_BUNDLES THEN
1020                   b_run.num_bundles := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1021 
1022                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT THEN
1023                   b_run.weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1024 
1025                ELSE
1026                   --invalid property
1027                   HANDLE_UNKNOWN_PROPERTY(l_ctxt,
1028                                           px_object,
1029                                           l_prop);
1030                   fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1031                   RETURN;
1032             END CASE;
1033 
1034             k := px_object_props.NEXT(k);
1035          END LOOP;
1036       EXCEPTION
1037          WHEN OTHERS THEN
1038             -- covers character string buffer too small
1039             HANDLE_INVALID_PROPERTY(l_ctxt,
1040                                     px_object,
1041                                     l_prop,
1042                                     SQLCODE,
1043                                     SQLERRM);
1044             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1045             RETURN;
1046       END;
1047 
1048       --defer validation until the GENERATE-phase because we need to have read in all objects to
1049       --get definitive answers.
1050 
1051       --set the object_id to mark that we've seen runtime metadata
1052       b_run.object_id := px_object.object_id;
1053 
1054       --success
1055       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1056       RETURN;
1057    EXCEPTION
1058       WHEN OTHERS THEN
1059          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1060          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1061          RAISE PROGRAM_ERROR;
1062    END;
1063 
1064    -- Private, helper to CACHE_OBJECT to cache the metadata for a bundle
1065    --
1066    PROCEDURE CACHE_BUNDLE(px_object             IN OUT NOCOPY b_object_def_type,
1067                           px_object_props       IN OUT NOCOPY b_properties_type)
1068    IS
1069       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_BUNDLE';
1070 
1071       l_bundle                  b_bundle_def_type;
1072 
1073       k                         NUMBER;
1074       l_prop                    b_property_def_type := NULL;
1075       l_msg                     VARCHAR2(4000);
1076    BEGIN
1077       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1078 
1079       --if we've already seen run.num_bundles of bundles, this one is too many
1080       IF b_run.num_bundles IS NOT NULL AND b_run.num_bundles = b_bundles.COUNT THEN
1081          l_msg := 'Already seen expected number of bundles: "'||b_run.num_bundles||'".';
1082          fnd_oam_debug.log(3, l_ctxt, l_msg);
1083          MARK_OBJECT_AS_ERRORED(px_object,
1084                                 l_msg);
1085          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1086          RETURN;
1087       END IF;
1088 
1089       --loop through the object properties, filling in the details of the run object
1090       BEGIN
1091          k := px_object_props.FIRST;
1092          WHILE k IS NOT NULL LOOP
1093             l_prop := px_object_props(k);
1094 
1095             CASE l_prop.property_name
1096                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_BUNDLE_ID THEN
1097                   l_bundle.bundle_id := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1098 
1099                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_TARGET_HOSTNAME THEN
1100                   l_bundle.target_hostname := UPPER(l_prop.canonical_value);
1101 
1102                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WORKERS_ALLOWED THEN
1103                   l_bundle.workers_allowed := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1104 
1105                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_BATCH_SIZE THEN
1106                   l_bundle.batch_size := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1107 
1108                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_MIN_PARALLEL_WEIGHT THEN
1109                   l_bundle.min_parallel_unit_weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1110 
1111                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT THEN
1112                   l_bundle.weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1113 
1114                ELSE
1115                   --invalid property
1116                   HANDLE_UNKNOWN_PROPERTY(l_ctxt,
1117                                           px_object,
1118                                           l_prop);
1119                   fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1120                   RETURN;
1121             END CASE;
1122 
1123             k := px_object_props.NEXT(k);
1124          END LOOP;
1125       EXCEPTION
1126          WHEN OTHERS THEN
1127             -- covers character string buffer too small
1128             HANDLE_INVALID_PROPERTY(l_ctxt,
1129                                     px_object,
1130                                     l_prop,
1131                                     SQLCODE,
1132                                     SQLERRM);
1133             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1134             RETURN;
1135       END;
1136 
1137       --defer validation until the GENERATE-phase because we need to have read in all objects to
1138       --get definitive answers.
1139 
1140       --add the bundle to the bundles list
1141       b_bundles(px_object.object_id) := l_bundle;
1142 
1143       --success
1144       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1145       RETURN;
1146    EXCEPTION
1147       WHEN OTHERS THEN
1148          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1149          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1150          RAISE PROGRAM_ERROR;
1151    END;
1152 
1153 /*
1154    --TODO:
1155    -- Private, helper to CACHE_OBJECT to cache the metadata for a domain
1156    --
1157    PROCEDURE CACHE_DOMAIN_METADATA(px_object            IN OUT NOCOPY b_object_def_type,
1158                                    px_object_props      IN OUT NOCOPY b_properties_type)
1159    IS
1160       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_DOMAIN_METADATA';
1161 
1162       l_domain                          VARCHAR2(120) := NULL;
1163       l_table_owner                     VARCHAR2(30) := NULL;
1164       l_table_name                      VARCHAR2(30) := NULL;
1165       l_object_type                     VARCHAR2(30) := NULL;
1166       l_domain_metadata                 b_domain_metadata_type;
1167       l_domain_obj_metadata_map         b_domain_obj_metadata_map_type;
1168 
1169       k                                 NUMBER;
1170       l_prop                            b_property_def_type := NULL;
1171       l_msg                             VARCHAR2(4000);
1172    BEGIN
1173       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1174 
1175       --loop through the object properties, filling in the details of the run object
1176       BEGIN
1177          k := px_object_props.FIRST;
1178          WHILE k IS NOT NULL LOOP
1179             l_prop := px_object_props(k);
1180 
1181             CASE l_prop.property_name
1182                WHEN FND_OAM_DSCFG_API_PKG.G_PROP_WEIGHT THEN
1183                   l_bundle.weight := FND_OAM_DSCFG_UTILS_PKG.CANONICAL_TO_NUMBER(l_prop.canonical_value);
1184 
1185                ELSE
1186                   --invalid property
1187                   HANDLE_UNKNOWN_PROPERTY(l_ctxt,
1188                                           px_object,
1189                                           l_prop);
1190                   fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1191                   RETURN;
1192             END CASE;
1193 
1194             k := px_object_props.NEXT(k);
1195          END LOOP;
1196       EXCEPTION
1197          WHEN OTHERS THEN
1198             -- covers character string buffer too small
1199             HANDLE_INVALID_PROPERTY(l_ctxt,
1200                                     px_object,
1201                                     l_prop,
1202                                     SQLCODE,
1203                                     SQLERRM);
1204             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1205             RETURN;
1206       END;
1207 
1208       --add the bundle to the bundles list
1209       --b_bundles(px_object.object_id) := l_bundle;
1210 
1211       --success
1212       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1213       RETURN;
1214    EXCEPTION
1215       WHEN OTHERS THEN
1216          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1217          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1218          RAISE;
1219    END;
1220 */
1221    -- Private, used by FETCH_COMPILABLE_OBJECTS to put a new object into the various
1222    -- package caches
1223    PROCEDURE CACHE_OBJECT(px_object             IN OUT NOCOPY b_object_def_type,
1224                           px_object_props       IN OUT NOCOPY b_properties_type)
1225    IS
1226       l_ctxt            VARCHAR2(60) := PKG_NAME||'CACHE_OBJECT';
1227 
1228       l_number_list             b_number_list_type;
1229       l_msg                     VARCHAR2(4000);
1230    BEGIN
1231       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1232 
1233       --prepare the object and cache it in the object_type-specific cache
1234       CASE px_object.object_type
1235          WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_UPDATE_SEGMENT THEN
1236             CACHE_DML_UPDATE_SEGMENT(px_object,
1237                                      px_object_props);
1238          WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_DELETE_STMT THEN
1239             CACHE_DML_DELETE_STMT(px_object,
1240                                   px_object_props);
1241          WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_TRUNCATE_STMT THEN
1242             CACHE_DML_TRUNCATE_STMT(px_object,
1243                                     px_object_props);
1244          WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_PLSQL_TEXT THEN
1245             CACHE_PLSQL_TEXT(px_object,
1246                              px_object_props);
1247          WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_RUN THEN
1248             CACHE_RUN(px_object,
1249                       px_object_props);
1250          WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_BUNDLE THEN
1251             CACHE_BUNDLE(px_object,
1252                          px_object_props);
1253          --WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DOMAIN_METADATA THEN
1254          --   CACHE_DOMAIN_METADATA(px_object,
1255          --                       px_object_props);
1256          ELSE
1257             --unknown object type, add a message, set the errors flag
1258             l_msg := 'Object Type: "'||px_object.object_type||'" not supported';
1259             fnd_oam_debug.log(3, l_ctxt, l_msg);
1260             MARK_OBJECT_AS_ERRORED(px_object,
1261                                    l_msg);
1262             fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1263             RETURN;
1264       END CASE;
1265 
1266       --check that the object came back with a primary domain
1267       IF px_object.primary_domain IS NULL AND
1268          px_object.object_type NOT IN (FND_OAM_DSCFG_API_PKG.G_OTYPE_RUN,
1269                                        FND_OAM_DSCFG_API_PKG.G_OTYPE_BUNDLE) THEN
1270          l_msg := 'Object has no primary domain - this is not allowed.';
1271          fnd_oam_debug.log(3, l_ctxt, l_msg);
1272          MARK_OBJECT_AS_ERRORED(px_object,
1273                                 l_msg);
1274       END IF;
1275 
1276       --store the object in the b_objects cache in any case
1277       b_objects(px_object.object_id) := px_object;
1278 
1279       --so we don't have to traverse the object list right after this, go ahead and add this object to
1280       --the primary domains cache if it hasn't errored out yet.
1281       IF px_object.new_errors_found_flag IS NULL AND px_object.primary_domain IS NOT NULL THEN
1282          fnd_oam_debug.log(1, l_ctxt, 'Caching valid primary_domain('||px_object.primary_domain||'), object_id('||px_object.object_id||')');
1283          IF b_primary_domains.EXISTS(px_object.primary_domain) THEN
1284             --add this object_id
1285             b_primary_domains(px_object.primary_domain).EXTEND;
1286             b_primary_domains(px_object.primary_domain)(b_primary_domains(px_object.primary_domain).COUNT) := px_object.object_id;
1287          ELSE
1288             --new number list
1289             l_number_list := b_number_list_type(px_object.object_id);
1290             b_primary_domains(px_object.primary_domain) := l_number_list;
1291          END IF;
1292       END IF;
1293 
1294       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1295    EXCEPTION
1296       WHEN PROGRAM_ERROR THEN
1297          --any errors in the CACHE_<OBJECT_TYPE> come up to this level as program_errors, catch them and swallow
1298          --so we can work try other objects
1299          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1300          RETURN;
1301       WHEN OTHERS THEN
1302          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1303          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1304          RAISE PROGRAM_ERROR;
1305    END;
1306 
1307    -- Private
1308    -- Helper to compile_config_instance to query out all objects
1309    PROCEDURE FETCH_COMPILABLE_OBJECTS(p_config_instance_id      IN NUMBER)
1310    IS
1311       l_ctxt            VARCHAR2(60) := PKG_NAME||'FETCH_COMPILABLE_OBJECTS';
1312 
1313       -- temp variables for bulk collecting objects
1314       l_object_ids      DBMS_SQL.NUMBER_TABLE;
1315       l_object_types    DBMS_SQL.VARCHAR2_TABLE;
1316       l_target_types    DBMS_SQL.VARCHAR2_TABLE;
1317       l_target_ids      DBMS_SQL.NUMBER_TABLE;
1318 
1319       TYPE long_varchar2_table_type IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
1320 
1321       -- temp variables for bulk collecting properties
1322       l_prop_ids                DBMS_SQL.NUMBER_TABLE;
1323       l_prop_object_ids         DBMS_SQL.NUMBER_TABLE;
1324       l_prop_names              DBMS_SQL.VARCHAR2_TABLE;
1325       l_datatypes               DBMS_SQL.VARCHAR2_TABLE;
1326       l_canonical_values        long_varchar2_table_type;
1327 
1328       -- variables for traversal of the bulk collections
1329       l_curr_object_id          NUMBER;
1330       l_curr_object             b_object_def_type;
1331       l_curr_prop               b_property_def_type;
1332       l_curr_object_props       b_properties_type;
1333       l_curr_object_index       NUMBER;
1334       l_curr_prop_index         NUMBER;
1335 
1336    BEGIN
1337       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1338 
1339       fnd_oam_debug.log(1, l_ctxt, 'Fetching compilable objects...');
1340       -- obtain all compilable objects
1341       SELECT object_id, object_type, target_type, target_id
1342          BULK COLLECT INTO l_object_ids, l_object_types, l_target_types, l_target_ids
1343          FROM fnd_oam_dscfg_objects
1344          WHERE config_instance_id = p_config_instance_id
1345          AND object_type IN (FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_UPDATE_SEGMENT,
1346                              FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_DELETE_STMT,
1347                              FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_TRUNCATE_STMT,
1348                              FND_OAM_DSCFG_API_PKG.G_OTYPE_PLSQL_TEXT,
1349                              FND_OAM_DSCFG_API_PKG.G_OTYPE_RUN,
1350                              FND_OAM_DSCFG_API_PKG.G_OTYPE_BUNDLE,
1351                              FND_OAM_DSCFG_API_PKG.G_OTYPE_DOMAIN_METADATA)
1352          ORDER BY object_id DESC;
1353       fnd_oam_debug.log(1, l_ctxt, '...Done');
1354 
1355       -- since we can't use l_object_ids to only get the properties we want, duplicate the object conditions above
1356       -- for selecting the entire list of properties
1357       fnd_oam_debug.log(1, l_ctxt, 'Fetching all corresponding object properties...');
1358       SELECT property_id, parent_id, property_name, datatype, canonical_value
1359          BULK COLLECT INTO l_prop_ids, l_prop_object_ids, l_prop_names, l_datatypes, l_canonical_values
1360          FROM fnd_oam_dscfg_properties
1361          WHERE parent_type = FND_OAM_DSCFG_API_PKG.G_TYPE_OBJECT
1362          AND parent_id in (SELECT object_id
1363                            FROM fnd_oam_dscfg_objects
1364                            WHERE config_instance_id = p_config_instance_id
1365                            AND object_type IN (FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_UPDATE_SEGMENT,
1366                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_DELETE_STMT,
1367                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_TRUNCATE_STMT,
1368                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_PLSQL_TEXT,
1369                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_RUN,
1370                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_BUNDLE,
1371                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_DOMAIN_METADATA))
1372          ORDER BY parent_id DESC, property_id DESC;
1373       fnd_oam_debug.log(1, l_ctxt, '...Done');
1374 
1375       --now we need to go through and create b_objects entries and b_<object_type> entries.
1376       l_curr_object_index := l_object_ids.FIRST;
1377       l_curr_prop_index := l_prop_ids.FIRST;
1378       WHILE l_curr_object_index IS NOT NULL LOOP
1379          l_curr_object_id := l_object_ids(l_curr_object_index);
1380 
1381          fnd_oam_debug.log(1, l_ctxt, 'Processing object_id: '||l_curr_object_id);
1382          --for each object create a new object def record
1383          l_curr_object := CREATE_OBJECT_DEF(l_curr_object_id,
1384                                             l_object_types(l_curr_object_index),
1385                                             l_target_types(l_curr_object_index),
1386                                             l_target_ids(l_curr_object_index));
1387 
1388          --now we need to snag the properties for this object
1389          l_curr_object_props.DELETE;
1390          WHILE l_curr_prop_index IS NOT NULL LOOP
1391             --since the l_prop_object_ids is ordered, we just need to keep going until the object id isn't ours
1392             IF l_prop_object_ids(l_curr_prop_index) <> l_curr_object_id THEN
1393                EXIT;
1394             END IF;
1395 
1396             fnd_oam_debug.log(1, l_ctxt, 'Processing property_id: '||l_prop_ids(l_curr_prop_index));
1397             --property belongs to this object, make a prop def and add it to the object props with the prop_id as the key
1398             l_curr_prop := CREATE_PROPERTY_DEF(l_prop_ids(l_curr_prop_index),
1399                                                l_prop_names(l_curr_prop_index),
1400                                                l_datatypes(l_curr_prop_index),
1401                                                l_canonical_values(l_curr_prop_index));
1402             l_curr_object_props(l_curr_prop.property_id) := l_curr_prop;
1403 
1404             --move to the next prop index
1405             l_curr_prop_index := l_prop_ids.NEXT(l_curr_prop_index);
1406          END LOOP;
1407 
1408          --now that we have the object and its list of properties, create an object_type-specific
1409          --cache entry
1410          CACHE_OBJECT(l_curr_object,
1411                       l_curr_object_props);
1412 
1413          --move on to the next object
1414          l_curr_object_index := l_object_ids.NEXT(l_curr_object_index);
1415       END LOOP;
1416 
1417       --all objects fetched and cached, return success
1418       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1419       RETURN;
1420    EXCEPTION
1421       WHEN PROGRAM_ERROR THEN
1422          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1423          RAISE;
1424       WHEN OTHERS THEN
1425          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1426          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1427          RAISE PROGRAM_ERROR;
1428    END;
1429 
1430    -- Private
1431    PROCEDURE ADD_ADDITIONAL_DOMAIN_TO_GROUP(p_group_id          IN NUMBER,
1432                                             p_additional_domain IN VARCHAR2)
1433    IS
1434    BEGIN
1435       -- uses index by table to keep out dupes
1436       b_dependency_groups(p_group_id).additional_domains(p_additional_domain) := TRUE;
1437    END;
1438 
1439    -- Private
1440    -- Used to move the contents of one dependency group to another.  This is done by appending the list of
1441    -- primary_domains (because those are only listed once in any given group) and inserting the list of
1442    -- additional domains into the current group.  For each moved domain, we also update the domain_to_group_map.
1443    PROCEDURE REASSIGN_DEPENDENCY_GROUP(p_from_group_id          IN NUMBER,
1444                                        p_to_group_id            IN NUMBER)
1445    IS
1446       l_ctxt            VARCHAR2(60) := PKG_NAME||'REASSIGN_DEPENDENCY_GROUP';
1447 
1448       l_from_group              b_dependency_group_def_type;
1449       --l_from_primary_domains          b_domain_list_type;
1450       --l_from_additional_domains               b_domain_map_type;
1451       l_domain                  VARCHAR2(120);
1452       l_from_index              NUMBER;
1453       l_to_index                NUMBER;
1454    BEGIN
1455       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1456 
1457       fnd_oam_debug.log(1, l_ctxt, 'Reassigning group '||p_from_group_id||' to '||p_to_group_id);
1458 
1459       --get a local handle to the from
1460       l_from_group := b_dependency_groups(p_from_group_id);
1461 
1462       --extend and copy the primary domains
1463       l_to_index := b_dependency_groups(p_to_group_id).primary_domains.COUNT + 1;
1464       b_dependency_groups(p_to_group_id).primary_domains.EXTEND(l_from_group.primary_domains.COUNT);
1465       l_from_index := l_from_group.primary_domains.FIRST;
1466       WHILE l_from_index IS NOT NULL LOOP
1467          l_domain := l_from_group.primary_domains(l_from_index);
1468          fnd_oam_debug.log(1, l_ctxt, 'Moving primary_domain: '||l_domain);
1469          b_domain_to_group_map(l_domain) := p_to_group_id;
1470          b_dependency_groups(p_to_group_id).primary_domains(l_to_index) := l_domain;
1471          l_to_index := l_to_index + 1;
1472          l_from_index := l_from_group.primary_domains.NEXT(l_from_index);
1473       END LOOP;
1474 
1475       --insert the additional domains
1476       l_domain := l_from_group.additional_domains.FIRST;
1477       WHILE l_domain IS NOT NULL LOOP
1478          fnd_oam_debug.log(2, l_ctxt, 'Moving additional_domain: '||l_domain);
1479          b_domain_to_group_map(l_domain) := p_to_group_id;
1480          ADD_ADDITIONAL_DOMAIN_TO_GROUP(p_to_group_id,
1481                                         l_domain);
1482          l_domain := l_from_group.additional_domains.NEXT(l_domain);
1483       END LOOP;
1484 
1485       --remove the from group
1486       fnd_oam_debug.log(1, l_ctxt, 'Removing the from group.');
1487       b_dependency_groups.DELETE(p_from_group_id);
1488 
1489       --success
1490       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1491    EXCEPTION
1492       WHEN OTHERS THEN
1493          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1494          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1495          RAISE PROGRAM_ERROR;
1496    END;
1497 
1498    -- Private
1499    PROCEDURE COMPUTE_DEPENDENCY_GROUPS
1500    IS
1501       l_ctxt            VARCHAR2(60) := PKG_NAME||'COMPUTE_DEPENDENCY_GROUPS';
1502 
1503       l_primary_domain          VARCHAR2(120);
1504       l_additional_domain       VARCHAR2(120);
1505       l_additional_domains      b_domain_list_type;
1506       l_object_id               NUMBER;
1507       l_object_ids              b_number_list_type;
1508 
1509       l_next_group_id   NUMBER := 1;
1510       l_this_group_id   NUMBER;
1511       l_found_group_id  NUMBER;
1512 
1513       k                 NUMBER;
1514       j                 NUMBER;
1515    BEGIN
1516       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1517 
1518       --traverse the map of primary domains, ignore the NULL domain key, shouldn't be allowed.
1519       l_primary_domain := b_primary_domains.FIRST;
1520       WHILE l_primary_domain IS NOT NULL LOOP
1521          --default objects in this primary domain to the next available group_id and store
1522          --the bidirectional mapping between them.  All future conflicting domains will be rolled into this
1523          --one.
1524          fnd_oam_debug.log(1, l_ctxt, 'Processing primary_domain: '||l_primary_domain);
1525          l_this_group_id := l_next_group_id;
1526          fnd_oam_debug.log(1, l_ctxt, 'Given group ID: '||l_this_group_id);
1527          l_next_group_id := l_next_group_id + 1;
1528          b_domain_to_group_map(l_primary_domain) := l_this_group_id;
1529          b_dependency_groups(l_this_group_id) := CREATE_DEPENDENCY_GROUP_DEF(l_primary_domain);
1530 
1531          --get the objects in this primary domain and loop over them to look for additional domains
1532          --that may conflict with existing dependency groups.
1533          l_object_ids := b_primary_domains(l_primary_domain);
1534          k := l_object_ids.FIRST;
1535          WHILE k IS NOT NULL LOOP
1536             l_object_id := l_object_ids(k);
1537             fnd_oam_debug.log(1, l_ctxt, 'Processing domain object_id: '||l_object_id);
1538             -- see if the object_id has any additional domains, if it doesn't then this object produces no conflicts.
1539             IF b_objects(l_object_id).additional_domains IS NOT NULL THEN
1540                fnd_oam_debug.log(1, l_ctxt, 'Has Additional Domains');
1541                --loop over the additional domains
1542                l_additional_domains := b_objects(l_object_id).additional_domains;
1543                j := l_additional_domains.FIRST;
1544                WHILE j IS NOT NULL LOOP
1545                   l_additional_domain := l_additional_domains(j);
1546                   fnd_oam_debug.log(1, l_ctxt, 'Processing additional_domain: '||l_additional_domain);
1547                   --see if the additional domain has already been seen
1548                   IF b_domain_to_group_map.EXISTS(l_additional_domain) THEN
1549                      --see if the additional domain is not already in this group, if it is we don't have to do anything
1550                      l_found_group_id := b_domain_to_group_map(l_additional_domain);
1551                      IF l_found_group_id <> l_this_group_id THEN
1552                         --migrate the contents of the found group to the current,"this" group
1553                         REASSIGN_DEPENDENCY_GROUP(p_from_group_id       => l_found_group_id,
1554                                                   p_to_group_id         => l_this_group_id);
1555                      END IF;
1556                   ELSE
1557                      --hasn't been seen before, just add the domain_to_group mapping
1558                      b_domain_to_group_map(l_additional_domain) := l_this_group_id;
1559                   END IF;
1560 
1561                   --always add this additional domain to the current group in case the re-assign took somebody else's
1562                   --primary.
1563                   ADD_ADDITIONAL_DOMAIN_TO_GROUP(l_this_group_id,
1564                                                  l_additional_domain);
1565 
1566                   j := l_additional_domains.NEXT(j);
1567                END LOOP;
1568             END IF;
1569             k := l_object_ids.NEXT(k);
1570          END LOOP;
1571 
1572          l_primary_domain := b_primary_domains.NEXT(l_primary_domain);
1573       END LOOP;
1574 
1575       -- success
1576       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1577       RETURN;
1578    EXCEPTION
1579       WHEN PROGRAM_ERROR THEN
1580          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1581          RAISE;
1582       WHEN OTHERS THEN
1583          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1584          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1585          RAISE PROGRAM_ERROR;
1586    END;
1587 
1588    -- Private, helper to COMPILE_CONFIG_INSTANCE to dump out the run entity
1589    PROCEDURE GENERATE_ENGINE_RUN(p_config_instance_id           IN NUMBER)
1590    IS
1591       PRAGMA AUTONOMOUS_TRANSACTION;
1592 
1593       l_ctxt            VARCHAR2(60) := PKG_NAME||'GENERATE_ENGINE_RUN';
1594 
1595       l_dbname          VARCHAR2(30);
1596       l_property_id     NUMBER;
1597       l_msg             VARCHAR2(4000);
1598 
1599       l_display_name    VARCHAR2(120);
1600       l_description     VARCHAR2(2000);
1601       l_language        VARCHAR2(12);
1602    BEGIN
1603       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1604 
1605       --see if we need to create a run object if none was cached on fetch
1606       IF b_run.object_id IS NULL THEN
1607          fnd_oam_debug.log(2, l_ctxt, 'Creating new run object');
1608          FND_OAM_DSCFG_API_PKG.ADD_OBJECT(p_object_type => FND_OAM_DSCFG_API_PKG.G_OTYPE_RUN,
1609                                           x_object_id   => b_run.object_id);
1610          fnd_oam_debug.log(2, l_ctxt, 'run object_id: '||b_run.object_id);
1611          --add an entry to the b_objects array so we can cache messages there
1612          b_objects(b_run.object_id) := CREATE_OBJECT_DEF(b_run.object_id,
1613                                                          FND_OAM_DSCFG_API_PKG.G_OTYPE_RUN);
1614       END IF;
1615 
1616       --validate the run mode
1617       IF b_run.run_mode IS NULL THEN
1618          b_run.run_mode := B_DEFAULT_RUN_MODE;
1619          FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id          => b_run.object_id,
1620                                                    p_property_name      => FND_OAM_DSCFG_API_PKG.G_PROP_RUN_MODE,
1621                                                    p_varchar2_value     => b_run.run_mode,
1622                                                    x_property_id        => l_property_id);
1623       ELSE
1624          IF b_run.run_mode NOT IN (FND_OAM_DSCRAM_UTILS_PKG.G_MODE_NORMAL,
1625                                    FND_OAM_DSCRAM_UTILS_PKG.G_MODE_TEST,
1626                                    FND_OAM_DSCRAM_UTILS_PKG.G_MODE_TEST_NO_EXEC,
1627                                    FND_OAM_DSCRAM_UTILS_PKG.G_MODE_DIAGNOSTIC) THEN
1628             l_msg := 'Invalid run mode specified: "'||b_run.run_mode||'".';
1629             fnd_oam_debug.log(3, l_ctxt, l_msg);
1630             MARK_OBJECT_AS_ERRORED(b_run.object_id,
1631                                    l_msg);
1632             RAISE PROGRAM_ERROR;
1633          END IF;
1634       END IF;
1635       fnd_oam_debug.log(1, l_ctxt, 'Run Mode: '||b_run.run_mode);
1636 
1637       --default the valid_check_interval if not present
1638       IF b_run.valid_check_interval IS NULL THEN
1639          b_run.valid_check_interval := GET_DFLT_VALID_CHECK_INTERVAL;
1640          FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id          => b_run.object_id,
1641                                                    p_property_name      => FND_OAM_DSCFG_API_PKG.G_PROP_VALID_CHECK_INTERVAL,
1642                                                    p_number_value       => b_run.valid_check_interval,
1643                                                    x_property_id        => l_property_id);
1644       ELSE
1645          IF b_run.valid_check_interval < 0 THEN
1646             l_msg := 'Invalid valid_check_interval specified: "'||b_run.valid_check_interval||'".';
1647             fnd_oam_debug.log(3, l_ctxt, l_msg);
1648             MARK_OBJECT_AS_ERRORED(b_run.object_id,
1649                                    l_msg);
1650             RAISE PROGRAM_ERROR;
1651          END IF;
1652       END IF;
1653       fnd_oam_debug.log(1, l_ctxt, 'Valid Check Interval: '||b_run.valid_check_interval);
1654 
1655       --default the # of bundles using what we've read into b_bundles or the default
1656       IF b_run.num_bundles IS NULL THEN
1657          IF b_bundles.COUNT > 0 THEN
1658             b_run.num_bundles := b_bundles.COUNT;
1659             --defer the check if this is greater than the # of hosts in the instance until GENERATE_BUNDLES
1660          ELSE
1661             b_run.num_bundles := B_DEFAULT_NUM_BUNDLES;
1662          END IF;
1663          FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id          => b_run.object_id,
1664                                                    p_property_name      => FND_OAM_DSCFG_API_PKG.G_PROP_NUM_BUNDLES,
1665                                                    p_number_value       => b_run.num_bundles,
1666                                                    x_property_id        => l_property_id);
1667       ELSE
1668          IF b_run.num_bundles < 1 THEN
1669             l_msg := 'Invalid num_bundles specified: "'||b_run.num_bundles||'".';
1670             fnd_oam_debug.log(3, l_ctxt, l_msg);
1671             MARK_OBJECT_AS_ERRORED(b_run.object_id,
1672                                    l_msg);
1673             RAISE PROGRAM_ERROR;
1674          END IF;
1675       END IF;
1676       fnd_oam_debug.log(1, l_ctxt, 'Number of Bundles: '||b_run.num_bundles);
1677 
1678       --make sure the num_bundles is >= the # of bundle objects we read in
1679       IF b_bundles.COUNT > b_run.num_bundles THEN
1680          l_msg := 'Number of specified run bundles, '||b_run.num_bundles||', less than the number of bundles seen in configuration, '||b_bundles.COUNT;
1681          fnd_oam_debug.log(3, l_ctxt, l_msg);
1682          MARK_OBJECT_AS_ERRORED(b_run.object_id,
1683                                 l_msg);
1684          RAISE PROGRAM_ERROR;
1685       END IF;
1686 
1687       --now we need to prep the actual engine entity
1688       IF b_run.run_id IS NULL THEN
1689          --get a run_id
1690          SELECT FND_OAM_DSCRAM_RUNS_S.NEXTVAL
1691             INTO b_run.run_id
1692             FROM dual;
1693       ELSE
1694          fnd_oam_debug.log(1, l_ctxt, 'Deleting Existing, Stale run_id: '||b_run.run_id);
1695          --TODO: make this smarter
1696          IF NOT FND_OAM_DSCRAM_UTILS_PKG.DELETE_RUN(b_run.run_id) THEN
1697             l_msg := 'Failed to delete previously compiled run: '||b_run.run_id;
1698             fnd_oam_debug.log(3, l_ctxt, l_msg);
1699          END IF;
1700       END IF;
1701 
1702       --query the current db as the target_dbname
1703       SELECT UPPER(name)
1704          INTO l_dbname
1705          FROM v$database
1706          WHERE rownum < 2;
1707 
1708       --at this point insert a new run, we'll update the weight later
1709       INSERT INTO FND_OAM_DSCRAM_RUNS_B (RUN_ID,
1710                                          RUN_STATUS,
1711                                          RUN_MODE,
1712                                          TARGET_DBNAME,
1713                                          CONFIG_INSTANCE_ID,
1714                                          VALID_CHECK_INTERVAL,
1715                                          CREATED_BY,
1716                                          CREATION_DATE,
1717                                          LAST_UPDATED_BY,
1718                                          LAST_UPDATE_DATE,
1719                                          LAST_UPDATE_LOGIN
1720                                          )
1721          VALUES
1722             (b_run.run_id,
1723              FND_OAM_DSCRAM_UTILS_PKG.G_STATUS_UNPROCESSED,
1724              b_run.run_mode,
1725              l_dbname,
1726              p_config_instance_id,
1727              b_run.valid_check_interval,
1728              FND_GLOBAL.USER_ID,
1729              SYSDATE,
1730              FND_GLOBAL.USER_ID,
1731              SYSDATE,
1732              FND_GLOBAL.USER_ID)
1733          RETURNING run_id INTO b_run.run_id;
1734       fnd_oam_debug.log(1, l_ctxt, 'Created new run_id: '||b_run.run_id);
1735 
1736       --store the run_id
1737       FND_OAM_DSCFG_API_PKG.SET_OR_ADD_OBJECT_PROPERTY(p_object_id      => b_run.object_id,
1738                                                        p_property_name  => FND_OAM_DSCFG_API_PKG.G_PROP_RUN_ID,
1739                                                        p_number_value   => b_run.run_id,
1740                                                        x_property_id    => l_property_id);
1741 
1742       fnd_oam_debug.log(1, l_ctxt, 'Querying config_instance attributes');
1743       -- get the name/description from the config instance
1744       SELECT name, description, language
1745          INTO l_display_name, l_description, l_language
1746          FROM fnd_oam_dscfg_instances
1747          WHERE config_instance_id = p_config_instance_id;
1748 
1749       fnd_oam_debug.log(1, l_ctxt, 'Inserting runs_tl row');
1750       INSERT INTO FND_OAM_DSCRAM_RUNS_TL (RUN_ID,
1751                                           DISPLAY_NAME,
1752                                           DESCRIPTION,
1753                                           LANGUAGE,
1754                                           SOURCE_LANG,
1755                                           CREATED_BY,
1756                                           CREATION_DATE,
1757                                           LAST_UPDATED_BY,
1758                                           LAST_UPDATE_DATE,
1759                                           LAST_UPDATE_LOGIN
1760                                           )
1761          VALUES
1762             (b_run.run_id,
1763              l_display_name||'('||b_run.run_id||')',
1764              l_description,
1765              l_language,
1766              l_language,
1767              --FND_GLOBAL.CURRENT_LANGUAGE,
1768              FND_GLOBAL.USER_ID,
1769              SYSDATE,
1770              FND_GLOBAL.USER_ID,
1771              SYSDATE,
1772              FND_GLOBAL.USER_ID);
1773 
1774       COMMIT;
1775 
1776       --success
1777       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1778    EXCEPTION
1779       WHEN PROGRAM_ERROR THEN
1780          ROLLBACK;
1781          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1782          RAISE;
1783       WHEN OTHERS THEN
1784          ROLLBACK;
1785          --try to mark the run object
1786          IF b_run.object_id IS NOT NULL THEN
1787             MARK_OBJECT_AS_ERRORED(b_run.object_id,
1788                                    l_ctxt,
1789                                    SQLCODE,
1790                                    SQLERRM);
1791          END IF;
1792          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
1793          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
1794          RAISE PROGRAM_ERROR;
1795    END;
1796 
1797    -- Private, helper to COMPILE_CONFIG_INSTANCE to dump out bundle entities
1798    PROCEDURE GENERATE_ENGINE_BUNDLES
1799    IS
1800       PRAGMA AUTONOMOUS_TRANSACTION;
1801 
1802       l_ctxt            VARCHAR2(60) := PKG_NAME||'GENERATE_ENGINE_BUNDLES';
1803 
1804 
1805       l_host_names              DBMS_SQL.VARCHAR2_TABLE;
1806       l_host_name               VARCHAR2(256);
1807       l_bundle_object_id        NUMBER;
1808       l_property_id             NUMBER;
1809 
1810       k                         NUMBER;
1811       j                         NUMBER;
1812       l_msg                     VARCHAR2(4000);
1813       l_found                   BOOLEAN;
1814       l_bundles_to_create       NUMBER;
1815 
1816    BEGIN
1817       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
1818 
1819       --get the list of hosts for this DB
1820       SELECT UPPER(host_name)
1821          BULK COLLECT INTO l_host_names
1822          FROM gv$instance;
1823 
1824       --make sure we don't have more bundles than hosts
1825       IF b_run.num_bundles > l_host_names.COUNT THEN
1826          l_msg := 'More bundles defined in run configuration, '||b_run.num_bundles||', than defined for database: '||l_host_names.COUNT;
1827          fnd_oam_debug.log(3, l_ctxt, l_msg);
1828          MARK_OBJECT_AS_ERRORED(b_run.object_id,
1829                                 l_msg);
1830          RAISE PROGRAM_ERROR;
1831       END IF;
1832 
1833       --make sure each bundle with a hostname has a valid, different hostname.  Also check and default the
1834       --workers_allowed, batch_size and min_parallel_weight attributes
1835       k := b_bundles.FIRST;
1836       WHILE k IS NOT NULL LOOP
1837          l_host_name := b_bundles(k).target_hostname;
1838          fnd_oam_debug.log(1, l_ctxt, 'Validating Bundle with object_id: '||k);
1839 
1840          --if a host is specified, do validation/bookkeeping
1841          IF l_host_name IS NOT NULL THEN
1842             fnd_oam_debug.log('Validating specified host_name: '||l_host_name);
1843             --see if the host name is already used
1844             IF b_host_name_map.EXISTS(l_host_name) THEN
1845                l_msg := 'Target Hostname "'||l_host_name||'" already used by object id: '||b_host_name_map(l_host_name);
1846                fnd_oam_debug.log(3, l_ctxt, l_msg);
1847                MARK_OBJECT_AS_ERRORED(k,
1848                                       l_msg);
1849                RAISE PROGRAM_ERROR;
1850             ELSE
1851                --hostname not seen yet, remove it from l_host_names
1852                l_found := FALSE;
1853                j := l_host_names.FIRST;
1854                WHILE j IS NOT NULL LOOP
1855                   IF l_host_names(j) = l_host_name THEN
1856                      l_host_names.DELETE(j);
1857                      l_found := TRUE;
1858                      EXIT;
1859                   END IF;
1860                   j := l_host_names.NEXT(j);
1861                END LOOP;
1862 
1863                --if we didn't find it, it's invalid
1864                IF NOT l_found THEN
1865                   l_msg := 'Target Hostname "'||l_host_name||'" is not a hostname attached to this instance, check gv$instance.host_name.';
1866                   fnd_oam_debug.log(3, l_ctxt, l_msg);
1867                   MARK_OBJECT_AS_ERRORED(k,
1868                                          l_msg);
1869                   RAISE PROGRAM_ERROR;
1870                END IF;
1871 
1872                --add it to the used host names map
1873                b_host_name_map(l_host_name) := k;
1874             END IF;
1875          END IF;
1876 
1877          --validate the workers_allowed
1878          IF b_bundles(k).workers_allowed IS NULL THEN
1879             b_bundles(k).workers_allowed := GET_DEFAULT_NUM_WORKERS;
1880             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => k,
1881                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_WORKERS_ALLOWED,
1882                                                       p_number_value    => b_bundles(k).workers_allowed,
1883                                                       x_property_id     => l_property_id);
1884          ELSE
1885             IF b_bundles(k).workers_allowed < 1 THEN
1886                l_msg := 'Invalid workers_allowed specified: "'||b_bundles(k).workers_allowed||'".';
1887                fnd_oam_debug.log(3, l_ctxt, l_msg);
1888                MARK_OBJECT_AS_ERRORED(k,
1889                                       l_msg);
1890                RAISE PROGRAM_ERROR;
1891             END IF;
1892          END IF;
1893          fnd_oam_debug.log(1, l_ctxt, 'Workers Allowed: '||b_bundles(k).workers_allowed);
1894 
1895          --validate the batch_size
1896          IF b_bundles(k).batch_size IS NULL THEN
1897             b_bundles(k).batch_size := GET_DEFAULT_BATCH_SIZE;
1898             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => k,
1899                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_BATCH_SIZE,
1900                                                       p_number_value    => b_bundles(k).batch_size,
1901                                                       x_property_id     => l_property_id);
1902          ELSE
1903             IF b_bundles(k).batch_size < 1 THEN
1904                l_msg := 'Invalid batch_size specified: "'||b_bundles(k).batch_size||'".';
1905                fnd_oam_debug.log(3, l_ctxt, l_msg);
1906                MARK_OBJECT_AS_ERRORED(k,
1907                                       l_msg);
1908                RAISE PROGRAM_ERROR;
1909             END IF;
1910          END IF;
1911          fnd_oam_debug.log(1, l_ctxt, 'Batch Size: '||b_bundles(k).batch_size);
1912 
1913          --validate the min_parallel_unit_weight
1914          IF b_bundles(k).min_parallel_unit_weight IS NULL THEN
1915             b_bundles(k).min_parallel_unit_weight := GET_DFLT_MIN_PARALLEL_WEIGHT;
1916             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => k,
1917                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_MIN_PARALLEL_WEIGHT,
1918                                                       p_number_value    => b_bundles(k).min_parallel_unit_weight,
1919                                                       x_property_id     => l_property_id);
1920          ELSE
1921             IF b_bundles(k).min_parallel_unit_weight < 1 THEN
1922                l_msg := 'Invalid min_parallel_unit_weight specified: "'||b_bundles(k).min_parallel_unit_weight||'".';
1923                fnd_oam_debug.log(3, l_ctxt, l_msg);
1924                MARK_OBJECT_AS_ERRORED(k,
1925                                       l_msg);
1926                RAISE PROGRAM_ERROR;
1927             END IF;
1928          END IF;
1929          fnd_oam_debug.log(1, l_ctxt, 'Minimum unit weight to parallelize: '||b_bundles(k).min_parallel_unit_weight);
1930 
1931          k := b_bundles.NEXT(k);
1932       END LOOP;
1933 
1934       --if there's more than one bundle, loop through the bundles and give each one without a hostname one from l_host_names
1935       IF b_bundles.COUNT > 1 THEN
1936          k := b_bundles.FIRST;
1937          WHILE k IS NOT NULL LOOP
1938             l_host_name := b_bundles(k).target_hostname;
1939 
1940             IF l_host_name IS NULL THEN
1941                --just get the next one off of l_host_names
1942                b_bundles(k).target_hostname := l_host_names(l_host_names.FIRST);
1943                fnd_oam_debug.log(1, l_ctxt, 'Assigned host_name "'||l_host_name||'" to bundle_object_id: '||k);
1944                l_host_names.DELETE(l_host_names.FIRST);
1945                b_host_name_map(b_bundles(k).target_hostname) := k;
1946 
1947                FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id            => k,
1948                                                          p_property_name        => FND_OAM_DSCFG_API_PKG.G_PROP_TARGET_HOSTNAME,
1949                                                          p_varchar2_value       => b_bundles(k).target_hostname,
1950                                                          x_property_id          => l_property_id);
1951             END IF;
1952 
1953             k := b_bundles.NEXT(k);
1954          END LOOP;
1955       END IF;
1956 
1957       --make sure we've got as many b_bundles entries as expected by b_run.num_bundles.
1958       --As an invariant, we can't have more but if we have less we need to create some default bundles.
1959       IF b_bundles.COUNT < b_run.num_bundles THEN
1960          l_bundles_to_create := b_run.num_bundles - b_bundles.COUNT;
1961          fnd_oam_debug.log(1, l_ctxt, 'Creating '||l_bundles_to_create||' bundle objects...');
1962          FOR k in 1..l_bundles_to_create LOOP
1963             --create a bundle object
1964             FND_OAM_DSCFG_API_PKG.ADD_OBJECT(p_object_type      => FND_OAM_DSCFG_API_PKG.G_OTYPE_BUNDLE,
1965                                              x_object_id        => l_bundle_object_id);
1966             fnd_oam_debug.log(1, l_ctxt, 'Bundle object_id: '||l_bundle_object_id);
1967             b_objects(l_bundle_object_id) := CREATE_OBJECT_DEF(l_bundle_object_id,
1968                                                                FND_OAM_DSCFG_API_PKG.G_OTYPE_BUNDLE);
1969 
1970             --use the next available host name
1971             b_bundles(l_bundle_object_id) := CREATE_BUNDLE_DEF(l_host_names(l_host_names.FIRST));
1972             l_host_names.DELETE(l_host_names.FIRST);
1973             b_host_name_map(b_bundles(l_bundle_object_id).target_hostname) := l_bundle_object_id;
1974 
1975             --add properties for mandatory attributes
1976             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => l_bundle_object_id,
1977                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_TARGET_HOSTNAME,
1978                                                       p_varchar2_value  => b_bundles(l_bundle_object_id).target_hostname,
1979                                                       x_property_id     => l_property_id);
1980             fnd_oam_debug.log(1, l_ctxt, 'Target Hostname: '||b_bundles(l_bundle_object_id).target_hostname);
1981             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => l_bundle_object_id,
1982                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_WORKERS_ALLOWED,
1983                                                       p_number_value    => b_bundles(l_bundle_object_id).workers_allowed,
1984                                                       x_property_id     => l_property_id);
1985             fnd_oam_debug.log(1, l_ctxt, 'Workers Allowed: '||b_bundles(l_bundle_object_id).workers_allowed);
1986             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => l_bundle_object_id,
1987                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_BATCH_SIZE,
1988                                                       p_number_value    => b_bundles(l_bundle_object_id).batch_size,
1989                                                       x_property_id     => l_property_id);
1990             fnd_oam_debug.log(1, l_ctxt, 'Batch Size: '||b_bundles(l_bundle_object_id).batch_size);
1991             FND_OAM_DSCFG_API_PKG.ADD_OBJECT_PROPERTY(p_object_id       => l_bundle_object_id,
1992                                                       p_property_name   => FND_OAM_DSCFG_API_PKG.G_PROP_MIN_PARALLEL_WEIGHT,
1993                                                       p_number_value    => b_bundles(l_bundle_object_id).min_parallel_unit_weight,
1994                                                       x_property_id     => l_property_id);
1995             fnd_oam_debug.log(1, l_ctxt, 'Min Parallel Unit Weight: '||b_bundles(l_bundle_object_id).min_parallel_unit_weight);
1996          END LOOP;
1997       END IF;
1998 
1999       --At this point, we should have b_run.num_bundles entries in b_bundles and each should be valid, create entries for each.
2000       --TODO: when we make the run smarter so existing runs are partially re-compiled, this section will need to be
2001       --addressed to use the bundle_id.  For now, just overwrite it.
2002       k := b_bundles.FIRST;
2003       WHILE k IS NOT NULL LOOP
2004          --do the insert, update weight later
2005          INSERT INTO FND_OAM_DSCRAM_BUNDLES (BUNDLE_ID,
2006                                              RUN_ID,
2007                                              BUNDLE_STATUS,
2008                                              TARGET_HOSTNAME,
2009                                              WORKERS_ALLOWED,
2010                                              WORKERS_ASSIGNED,
2011                                              BATCH_SIZE,
2012                                              MIN_PARALLEL_UNIT_WEIGHT,
2013                                              CREATED_BY,
2014                                              CREATION_DATE,
2015                                              LAST_UPDATED_BY,
2016                                              LAST_UPDATE_DATE,
2017                                              LAST_UPDATE_LOGIN
2018                                              )
2019             VALUES
2020                (FND_OAM_DSCRAM_BUNDLES_S.NEXTVAL,
2021                 b_run.run_id,
2022                 FND_OAM_DSCRAM_UTILS_PKG.G_STATUS_UNPROCESSED,
2023                 b_bundles(k).target_hostname,
2024                 b_bundles(k).workers_allowed,
2025                 0,
2026                 b_bundles(k).batch_size,
2027                 b_bundles(k).min_parallel_unit_weight,
2028                 FND_GLOBAL.USER_ID,
2029                 SYSDATE,
2030                 FND_GLOBAL.USER_ID,
2031                 SYSDATE,
2032                 FND_GLOBAL.USER_ID)
2033          RETURNING bundle_id INTO b_bundles(k).bundle_id;
2034          fnd_oam_debug.log(1, l_ctxt, 'Bundle Object ID ('||k||') created bundle_id ('||b_bundles(k).bundle_id||')');
2035 
2036          --add the property for bundle id
2037          FND_OAM_DSCFG_API_PKG.SET_OR_ADD_OBJECT_PROPERTY(p_object_id           => k,
2038                                                           p_property_name       => FND_OAM_DSCFG_API_PKG.G_PROP_BUNDLE_ID,
2039                                                           p_number_value        => b_bundles(k).bundle_id,
2040                                                           x_property_id         => l_property_id);
2041 
2042          k := b_bundles.NEXT(k);
2043       END LOOP;
2044 
2045       COMMIT;
2046 
2047       --success
2048       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2049    EXCEPTION
2050       WHEN PROGRAM_ERROR THEN
2051          ROLLBACK;
2052          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2053          RAISE;
2054       WHEN OTHERS THEN
2055          ROLLBACK;
2056          --mark the run object
2057          MARK_OBJECT_AS_ERRORED(b_run.object_id,
2058                                 l_ctxt,
2059                                 SQLCODE,
2060                                 SQLERRM);
2061          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
2062          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2063          RAISE PROGRAM_ERROR;
2064    END;
2065 
2066    -- Private, gets the table_objects_def entity from the px_table_bound_map or creates one if necessary.
2067    FUNCTION GET_TABLE_OBJECTS_DEF(px_table_bound_map    IN OUT NOCOPY b_table_bound_map_type,
2068                                   p_table_owner         IN VARCHAR2,
2069                                   p_table_name          IN VARCHAR2)
2070       RETURN b_table_objects_def_type
2071    IS
2072       l_ctxt            VARCHAR2(60) := PKG_NAME||'GET_TABLE_OBJECTS_DEF';
2073 
2074       l_table_name_map          b_table_name_map_type;
2075       l_table_objects_def       b_table_objects_def_type; --allocate a new one using it's create each time
2076    BEGIN
2077       --see if the table_bound_map has this table_owner
2078       IF px_table_bound_map.EXISTS(p_table_owner) THEN
2079          IF px_table_bound_map(p_table_owner).EXISTS(p_table_name) THEN
2080             RETURN px_table_bound_map(p_table_owner)(p_table_name);
2081          ELSE
2082             --has owner, but no table_name entry
2083             l_table_objects_def := CREATE_TABLE_OBJECTS_DEF;
2084             px_table_bound_map(p_table_owner)(p_table_name) := l_table_objects_def;
2085             RETURN l_table_objects_def;
2086          END IF;
2087       ELSE
2088          --owner isn't present so add the table name to the name_map with a new table_objects_def
2089          l_table_objects_def := CREATE_TABLE_OBJECTS_DEF;
2090          l_table_name_map(p_table_name) := l_table_objects_def;
2091          px_table_bound_map(p_table_owner) := l_table_name_map;
2092          RETURN l_table_objects_def;
2093       END IF;
2094    EXCEPTION
2095       WHEN OTHERS THEN
2096          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
2097          RAISE;
2098    END;
2099 
2100    -- Private, helper to ADD_ENGINE_UNITS to do the physical insert
2101    PROCEDURE ADD_ENGINE_UNIT(p_task_id                          IN NUMBER,
2102                              p_unit_type                        IN VARCHAR2,
2103                              p_concurrent_group_unit_id         IN NUMBER DEFAULT NULL,
2104                              p_phase                            IN NUMBER DEFAULT NULL,
2105                              p_priority                         IN NUMBER DEFAULT NULL,
2106                              p_weight                           IN NUMBER DEFAULT NULL,
2107                              p_workers_allowed                  IN NUMBER DEFAULT NULL,
2108                              p_unit_object_owner                IN VARCHAR2 DEFAULT NULL,
2109                              p_unit_object_name                 IN VARCHAR2 DEFAULT NULL,
2110                              p_batch_size                       IN VARCHAR2 DEFAULT NULL,
2111                              p_error_fatality_level             IN VARCHAR2 DEFAULT NULL,
2112                              p_disable_splitting                IN VARCHAR2 DEFAULT NULL,
2113                              px_unit_id                         IN OUT NOCOPY NUMBER)
2114    IS
2115       l_ctxt            VARCHAR2(60) := PKG_NAME||'ADD_UNIT';
2116 
2117       l_unit_id         NUMBER;
2118    BEGIN
2119       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2120 
2121       IF px_unit_id IS NULL THEN
2122          SELECT FND_OAM_DSCRAM_UNITS_S.NEXTVAL
2123             INTO px_unit_id
2124             FROM dual;
2125       END IF;
2126 
2127       INSERT INTO FND_OAM_DSCRAM_UNITS (UNIT_ID,
2128                                         TASK_ID,
2129                                         CONCURRENT_GROUP_UNIT_ID,
2130                                         UNIT_TYPE,
2131                                         UNIT_STATUS,
2132                                         PHASE,
2133                                         PRIORITY,
2134                                         WEIGHT,
2135                                         SUGGEST_WORKERS_ALLOWED,
2136                                         WORKERS_ASSIGNED,
2137                                         UNIT_OBJECT_OWNER,
2138                                         UNIT_OBJECT_NAME,
2139                                         BATCH_SIZE,
2140                                         ERROR_FATALITY_LEVEL,
2141                                         SUGGEST_DISABLE_SPLITTING,
2142                                         CREATED_BY,
2143                                         CREATION_DATE,
2144                                         LAST_UPDATED_BY,
2145                                         LAST_UPDATE_DATE,
2146                                         LAST_UPDATE_LOGIN
2147                                         )
2148          VALUES
2149             (px_unit_id,
2150              p_task_id,
2151              p_concurrent_group_unit_id,
2152              p_unit_type,
2153              FND_OAM_DSCRAM_UTILS_PKG.G_STATUS_UNPROCESSED,
2154              p_phase,
2155              p_priority,
2156              p_weight,
2157              p_workers_allowed,
2158              0,
2159              p_unit_object_owner,
2160              p_unit_object_name,
2161              p_batch_size,
2162              p_error_fatality_level,
2163              p_disable_splitting,
2164              FND_GLOBAL.USER_ID,
2165              SYSDATE,
2166              FND_GLOBAL.USER_ID,
2167              SYSDATE,
2168              FND_GLOBAL.USER_ID)
2169          RETURNING UNIT_ID INTO l_unit_id;
2170 
2171       px_unit_id := l_unit_id;
2172 
2173       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2174    EXCEPTION
2175       WHEN OTHERS THEN
2176          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
2177          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2178          RAISE;
2179    END;
2180 
2181    --Simple helper to get the next unit id from its sequence.
2182    FUNCTION GET_NEXT_UNIT_ID
2183       RETURN NUMBER
2184    IS
2185       l_retval          NUMBER;
2186    BEGIN
2187       SELECT FND_OAM_DSCRAM_UNITS_S.NEXTVAL
2188          INTO l_retval
2189          FROM DUAL;
2190       RETURN l_retval;
2191    END;
2192 
2193    -- Helper to ADD_ENGINE_UNITS to update the logical and physical weight counters.
2194    PROCEDURE INTEGRATE_WEIGHTS(px_parent_logical_weight         IN OUT NOCOPY NUMBER,
2195                                px_parent_physical_weight        IN OUT NOCOPY NUMBER,
2196                                p_child_logical_weight           IN NUMBER,
2197                                p_child_physical_weight          IN NUMBER)
2198    IS
2199    BEGIN
2200       IF px_parent_logical_weight IS NOT NULL THEN
2201          IF p_child_logical_weight IS NULL THEN
2202             px_parent_logical_weight := NULL;
2203          ELSE
2204             px_parent_logical_weight := px_parent_logical_weight + p_child_logical_weight;
2205          END IF;
2206       END IF;
2207       px_parent_physical_weight := px_parent_physical_weight + NVL(p_child_physical_weight, 0);
2208    END;
2209 
2210    -- create a dml entry for a given unit
2211    PROCEDURE ADD_ENGINE_DML(p_unit_id           IN NUMBER,
2212                             p_stmt              IN VARCHAR2,
2213                             p_where_clause      IN VARCHAR2 DEFAULT NULL,
2214                             p_priority          IN NUMBER DEFAULT NULL,
2215                             p_weight            IN NUMBER DEFAULT NULL,
2216                             x_dml_id            OUT NOCOPY NUMBER)
2217    IS
2218       l_ctxt    VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_DML';
2219       l_dml_id  NUMBER;
2220    BEGIN
2221       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2222 
2223       INSERT INTO FND_OAM_DSCRAM_DMLS (DML_ID,
2224                                        UNIT_ID,
2225                                        PRIORITY,
2226                                        WEIGHT,
2227                                        DML_STMT,
2228                                        DML_WHERE_CLAUSE,
2229                                        CREATED_BY,
2230                                        CREATION_DATE,
2231                                        LAST_UPDATED_BY,
2232                                        LAST_UPDATE_DATE,
2233                                        LAST_UPDATE_LOGIN
2234                                        )
2235          VALUES
2236             (FND_OAM_DSCRAM_DMLS_S.NEXTVAL,
2237              p_unit_id,
2238              p_priority,
2239              p_weight,
2240              p_stmt,
2241              p_where_clause,
2242              FND_GLOBAL.USER_ID,
2243              SYSDATE,
2244              FND_GLOBAL.USER_ID,
2245              SYSDATE,
2246              FND_GLOBAL.USER_ID)
2247          RETURNING DML_ID INTO l_dml_id;
2248 
2249       x_dml_id := l_dml_id;
2250 
2251       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2252    EXCEPTION
2253       WHEN OTHERS THEN
2254          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Exception: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
2255          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2256          RAISE;
2257    END;
2258 
2259    -- Helper to construct a DML DELETE statement and add it to a unit.
2260    FUNCTION ADD_ENGINE_DELETE_DML(p_unit_id             IN NUMBER,
2261                                   p_object_id           IN NUMBER,
2262                                   x_weight              OUT NOCOPY NUMBER)
2263       RETURN BOOLEAN
2264    IS
2265       l_ctxt    VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_DELETE_DML';
2266 
2267       l_delete_def      b_dml_delete_stmt_def_type;
2268       l_stmt            VARCHAR2(4000);
2269       l_weight          NUMBER;
2270       l_dml_id          NUMBER;
2271    BEGIN
2272       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2273 
2274       --get a reference
2275       l_delete_def := b_dml_delete_stmts(p_object_id);
2276 
2277       --construct the delete stmt
2278       l_stmt := 'DELETE FROM '||l_delete_def.table_owner||'.'||l_delete_def.table_name;
2279 
2280       --compute the weight
2281       l_weight := CEIL(NVL(l_delete_def.weight, B_DELETE_WEIGHT_MODIFIER *
2282                                                 FND_OAM_DSCFG_UTILS_PKG.GET_TABLE_WEIGHT(l_delete_def.table_owner,
2283                                                                                          l_delete_def.table_name)));
2284       --add the dml
2285       ADD_ENGINE_DML(p_unit_id,
2286                      l_stmt,
2287                      l_delete_def.where_clause,
2288                      p_priority => B_BASE_PRIORITY_DELETES,
2289                      p_weight   => l_weight,
2290                      x_dml_id   => l_dml_id);
2291 
2292       --success
2293       x_weight := l_weight;
2294       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2295       RETURN TRUE;
2296    EXCEPTION
2297       WHEN OTHERS THEN
2298          MARK_OBJECT_AS_ERRORED(p_object_id,
2299                                 l_ctxt,
2300                                 SQLCODE,
2301                                 SQLERRM);
2302          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2303          RAISE PROGRAM_ERROR;
2304    END;
2305 
2306    -- Helper to construct a DML DELETE statement and add it to a unit.
2307    FUNCTION ADD_ENGINE_UPDATE_DMLS(p_unit_id            IN NUMBER,
2308                                    p_table_owner        IN VARCHAR2,
2309                                    p_table_name         IN VARCHAR2,
2310                                    p_where_clause       IN VARCHAR2,
2311                                    px_column_map        IN OUT NOCOPY b_column_name_map_type,
2312                                    x_logical_weight     OUT NOCOPY NUMBER,
2313                                    x_physical_weight    OUT NOCOPY NUMBER)
2314       RETURN BOOLEAN
2315    IS
2316       l_ctxt    VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_UPDATE_DMLS';
2317 
2318       l_dml_id          NUMBER;
2319 
2320       l_logical_weight          NUMBER := 0;
2321       l_physical_weight         NUMBER := 0;
2322       l_initial_set_clause      VARCHAR2(10) := ' SET ';
2323       l_set_snippet             VARCHAR2(4000);
2324       l_set_clause              VARCHAR2(4000);
2325       l_prefix                  VARCHAR2(100);
2326       l_prefix_len              NUMBER;
2327       l_suffix                  VARCHAR2(4000);
2328       l_suffix_len              NUMBER;
2329       l_column                  VARCHAR2(30);
2330       l_column_count            NUMBER;
2331       l_table_weight            NUMBER;
2332 
2333       l_weight          NUMBER;
2334       k                 NUMBER;
2335       l_msg             VARCHAR2(4000);
2336    BEGIN
2337       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2338 
2339       --prep the dml prefix and suffix
2340       l_prefix := 'UPDATE '||p_table_owner||'.'||p_table_name;
2341       IF p_where_clause IS NOT NULL THEN
2342          l_suffix := ' WHERE ';
2343          IF length(l_suffix) + length(p_where_clause) > B_STMT_MAXLEN THEN
2344             l_msg := 'Table '||p_table_owner||'.'||p_table_name||' has a where clause that is too long.';
2345             fnd_oam_debug.log(3, l_ctxt, l_msg);
2346             MARK_OBJECT_AS_ERRORED(b_run.object_id,
2347                                    l_msg);
2348             RAISE PROGRAM_ERROR;
2349          ELSE
2350             l_suffix := l_suffix||p_where_clause;
2351          END IF;
2352       ELSE
2353          l_suffix := '';
2354       END IF;
2355       l_prefix_len := length(l_prefix);
2356       l_suffix_len := NVL(length(l_suffix), 0);
2357 
2358       --get the table weight
2359       l_table_weight := FND_OAM_DSCFG_UTILS_PKG.GET_TABLE_WEIGHT(p_table_owner,
2360                                                                  p_table_name);
2361 
2362       -- initialize pieces we use to identify the currently accumulating sql statement
2363       l_set_clause := l_initial_set_clause;
2364       l_column_count := 0;
2365       l_weight := l_table_weight;
2366 
2367       --loop over the list of columns
2368       l_column := px_column_map.FIRST;
2369       WHILE l_column IS NOT NULL LOOP
2370          --compose the column=value snippet
2371          l_set_snippet := l_column||'='||b_dml_update_segments(px_column_map(l_column)).new_column_value;
2372          IF l_column_count <> 0 THEN
2373             l_set_snippet := ', '||l_set_snippet;
2374          END IF;
2375          --see if we have room
2376          IF l_prefix_len + length(l_set_clause) + length(l_set_snippet) + l_suffix_len <= B_STMT_MAXLEN THEN
2377             --has room, add the snippet, adjust the weight and increment our counters
2378             l_set_clause := l_set_clause||l_set_snippet;
2379             IF b_dml_update_segments(px_column_map(l_column)).weight_modifier IS NOT NULL THEN
2380                l_weight := l_weight * b_dml_update_segments(px_column_map(l_column)).weight_modifier;
2381             END IF;
2382             l_column_count := l_column_count + 1;
2383             l_column := px_column_map.NEXT(l_column);
2384          ELSE
2385             --no room left
2386             IF l_column_count = 0 THEN
2387                --no room for the first element, this is bad
2388                l_msg := 'This set clause alone is too large('||to_char(l_prefix_len + length(l_set_clause) + length(l_set_snippet) + l_suffix_len)||') for a single statement with the where clause provided.';
2389                fnd_oam_debug.log(3, l_ctxt, l_msg);
2390                MARK_OBJECT_AS_ERRORED(px_column_map(l_column),
2391                                       l_msg);
2392                RAISE PROGRAM_ERROR;
2393             END IF;
2394 
2395             --make a dml for the currently accumulated statement, the suffix is only used to do length limitations, it is
2396             --discarded at this point and the formal where clause is passed as the other param.
2397             ADD_ENGINE_DML(p_unit_id,
2398                            l_prefix||l_set_clause,
2399                            p_where_clause       => p_where_clause,
2400                            p_priority           => B_BASE_PRIORITY_UPDATES,
2401                            p_weight             => l_weight,
2402                            x_dml_id             => l_dml_id);
2403             INTEGRATE_WEIGHTS(l_logical_weight,
2404                               l_physical_weight,
2405                               l_weight,
2406                               l_weight);
2407 
2408             --re-initialize the statement variables, don't increment the column so we see it on the next loop
2409             l_set_clause := l_initial_set_clause;
2410             l_column_count := 0;
2411             l_weight := l_table_weight;
2412          END IF;
2413       END LOOP;
2414 
2415       --get any remaining statement if we exited the loop with in-progress columns
2416       IF l_column_count > 0 THEN
2417             ADD_ENGINE_DML(p_unit_id,
2418                            l_prefix||l_set_clause,
2419                            p_where_clause       => p_where_clause,
2420                            p_priority           => B_BASE_PRIORITY_UPDATES,
2421                            p_weight             => l_weight,
2422                            x_dml_id             => l_dml_id);
2423          INTEGRATE_WEIGHTS(l_logical_weight,
2424                            l_physical_weight,
2425                            l_weight,
2426                            l_weight);
2427       END IF;
2428 
2429       --success
2430       x_logical_weight := l_logical_weight;
2431       x_physical_weight := l_physical_weight;
2432       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2433       RETURN TRUE;
2434    EXCEPTION
2435       WHEN PROGRAM_ERROR THEN
2436          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2437          RAISE;
2438       WHEN OTHERS THEN
2439          MARK_OBJECT_AS_ERRORED(b_run.object_id,
2440                                 l_ctxt,
2441                                 SQLCODE,
2442                                 SQLERRM);
2443          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2444          RAISE PROGRAM_ERROR;
2445    END;
2446 
2447    -- create a plsql entry for a given unit
2448    PROCEDURE ADD_ENGINE_PLSQL(p_unit_id                IN NUMBER,
2449                               p_plsql_text             IN VARCHAR2,
2450                               p_priority               IN NUMBER DEFAULT NULL,
2451                               p_weight                 IN NUMBER DEFAULT NULL,
2452                               x_plsql_id               OUT NOCOPY NUMBER)
2453    IS
2454       l_ctxt    VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_PLSQL';
2455 
2456       l_plsql_id  NUMBER;
2457    BEGIN
2458       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2459 
2460       INSERT INTO FND_OAM_DSCRAM_PLSQLS (PLSQL_ID,
2461                                          UNIT_ID,
2462                                          PRIORITY,
2463                                          WEIGHT,
2464                                          PLSQL_TEXT,
2465                                          CREATED_BY,
2466                                          CREATION_DATE,
2467                                          LAST_UPDATED_BY,
2468                                          LAST_UPDATE_DATE,
2469                                          LAST_UPDATE_LOGIN
2470                                        )
2471          VALUES
2472             (FND_OAM_DSCRAM_PLSQLS_S.NEXTVAL,
2473              p_unit_id,
2474              p_priority,
2475              p_weight,
2476              p_plsql_text,
2477              FND_GLOBAL.USER_ID,
2478              SYSDATE,
2479              FND_GLOBAL.USER_ID,
2480              SYSDATE,
2481              FND_GLOBAL.USER_ID)
2482          RETURNING PLSQL_ID INTO l_plsql_id;
2483 
2484       x_plsql_id := l_plsql_id;
2485 
2486       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2487    EXCEPTION
2488       WHEN OTHERS THEN
2489          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Exception: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
2490          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2491          RAISE;
2492    END;
2493 
2494    -- Helper function to ADD_ENGINE_UNITS_FOR_DOMAIN to handle the table_bound objects of a specific domain.
2495    -- This is part of the parent's autonomous transaction and is split out primarily because it's too much
2496    -- code to include in an already large parent function.  Each table owner/table_name spawns its own unit
2497    -- or set of units in the case of a concurrent_meta_unit.
2498    FUNCTION ADD_ENGINE_TABLE_BOUND_UNITS(p_task_id                      IN NUMBER,
2499                                          px_table_bound_map             IN OUT NOCOPY b_table_bound_map_type,
2500                                          px_bound_operations_phase      IN OUT NOCOPY NUMBER,
2501                                          x_logical_weight               OUT NOCOPY NUMBER,
2502                                          x_physical_weight              OUT NOCOPY NUMBER)
2503       RETURN BOOLEAN
2504    IS
2505       l_ctxt            VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_TABLE_BOUND_UNITS';
2506 
2507       l_total_logical_weight    NUMBER := 0;
2508       l_total_physical_weight   NUMBER := 0;
2509       l_unit_logical_weight     NUMBER;
2510       l_unit_physical_weight    NUMBER;
2511       l_logical_weight          NUMBER;
2512       l_physical_weight         NUMBER;
2513 
2514       l_table_objects_def       b_table_objects_def_type; --allocate a new one using it's create each time
2515       l_unit_id                 NUMBER;
2516       l_dml_unit_id             NUMBER;
2517       l_plsql_unit_id           NUMBER;
2518       l_table_name              VARCHAR2(30);
2519       l_table_owner             VARCHAR2(30);
2520       l_has_dmls                BOOLEAN;
2521       l_has_plsqls              BOOLEAN;
2522       l_plsql_id                NUMBER;
2523       l_where_clause            VARCHAR2(4000);
2524       l_weight                  NUMBER;
2525       k                         NUMBER;
2526       j                         NUMBER;
2527    BEGIN
2528       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2529 
2530       -- Go through each owner/name
2531       -- combo and create units.  For now, each owner/name combo will produce one unit in a different phase.  Other tables
2532       -- in the same domain usually means overlap so they'd need a different phase anyhow.  Lumping all the operations
2533       -- on one table into a single unit is ok because if they want seperation, they should specify a different primary_domain.
2534       -- Finally, use and increment the passed in phases for each instead of using NULLs to keep NULL open for any ops the user
2535       -- wants to have run at the very end.
2536       l_table_owner := px_table_bound_map.FIRST;
2537       WHILE l_table_owner IS NOT NULL LOOP
2538          fnd_oam_debug.log(1, l_ctxt, 'Processing Table Owner: '||l_table_owner);
2539          --loop over the table names for this owner
2540          l_table_name := px_table_bound_map(l_table_owner).FIRST;
2541          WHILE l_table_name IS NOT NULL LOOP
2542             fnd_oam_debug.log(1, l_ctxt, 'Processing Table Name: '||l_table_name);
2543             --get a reference to the table_objects_def
2544             l_table_objects_def := px_table_bound_map(l_table_owner)(l_table_name);
2545 
2546             --see if we've got dmls and plsqls, this requires a concurrent unit
2547             l_has_dmls := (l_table_objects_def.update_map.COUNT > 0) OR (l_table_objects_def.delete_map.COUNT > 0);
2548             l_has_plsqls := (l_table_objects_def.plsqls.COUNT > 0);
2549             l_unit_logical_weight := 0;
2550             l_unit_physical_weight := 0;
2551 
2552             IF l_has_dmls AND l_has_plsqls THEN
2553                --get an id for the concurrent meta-unit, otherwise tag it with a NULL
2554                l_unit_id := GET_NEXT_UNIT_ID;
2555             ELSE
2556                l_unit_id := NULL;
2557             END IF;
2558             IF l_has_dmls THEN
2559                l_dml_unit_id := GET_NEXT_UNIT_ID;
2560             END IF;
2561             IF l_has_plsqls THEN
2562                l_plsql_unit_id := GET_NEXT_UNIT_ID;
2563             END IF;
2564 
2565             --create DMLs for each update where clause, can't loop on WHILE because we allow NULL keys
2566             j := l_table_objects_def.update_map.COUNT;
2567             l_where_clause := l_table_objects_def.update_map.FIRST;
2568             FOR k IN 1..j LOOP
2569                IF ADD_ENGINE_UPDATE_DMLS(l_dml_unit_id,
2570                                          l_table_owner,
2571                                          l_table_name,
2572                                          l_where_clause,
2573                                          l_table_objects_def.update_map(l_where_clause),
2574                                          l_logical_weight,
2575                                          l_physical_weight) THEN
2576                   INTEGRATE_WEIGHTS(l_unit_logical_weight,
2577                                     l_unit_physical_weight,
2578                                     l_logical_weight,
2579                                     l_physical_weight);
2580                END IF;
2581                l_where_clause := l_table_objects_def.update_map.NEXT(l_where_clause);
2582             END LOOP;
2583 
2584             --create DMLs for each delete where clause, can't loop on WHILE because we allow NULL keys
2585             j := l_table_objects_def.delete_map.COUNT;
2586             l_where_clause := l_table_objects_def.delete_map.FIRST;
2587             FOR k IN 1..j LOOP
2588                IF ADD_ENGINE_DELETE_DML(l_dml_unit_id,
2589                                         l_table_objects_def.delete_map(l_where_clause),
2590                                         l_weight) THEN
2591                   INTEGRATE_WEIGHTS(l_unit_logical_weight,
2592                                     l_unit_physical_weight,
2593                                     l_weight,
2594                                     l_weight);
2595                END IF;
2596                l_where_clause := l_table_objects_def.delete_map.NEXT(l_where_clause);
2597             END LOOP;
2598 
2599             --create plsqls for each table bound plsql
2600             j := l_table_objects_def.plsqls.FIRST;
2601             WHILE j IS NOT NULL LOOP
2602                k := l_table_objects_def.plsqls(j);
2603                --no need for a checked return value, throws an exception if it fails
2604                ADD_ENGINE_PLSQL(l_plsql_unit_id,
2605                                 b_plsql_texts(k).plsql_text,
2606                                 p_weight        => b_plsql_texts(k).weight,
2607                                 x_plsql_id      => l_plsql_id);
2608 
2609                INTEGRATE_WEIGHTS(l_unit_logical_weight,
2610                                  l_unit_physical_weight,
2611                                  b_plsql_texts(k).weight,
2612                                  b_plsql_texts(k).weight);
2613                j := l_table_objects_def.plsqls.NEXT(j);
2614             END LOOP;
2615 
2616             --create the necessary units
2617             IF l_unit_id IS NOT NULL THEN
2618                --concurrent_units case
2619                ADD_ENGINE_UNIT(p_task_id,
2620                                FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_CONC_GROUP,
2621                                p_phase                  => px_bound_operations_phase,
2622                                p_weight                 => l_unit_logical_weight,
2623                                p_unit_object_owner      => l_table_owner,
2624                                p_unit_object_name       => l_table_name,
2625                                px_unit_id               => l_unit_id);
2626                -- create child units, weight and phase are meaningless here
2627                IF l_dml_unit_id IS NOT NULL THEN
2628                   ADD_ENGINE_UNIT(p_task_id,
2629                                   FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_DML_SET,
2630                                   p_concurrent_group_unit_id    => l_unit_id,
2631                                   px_unit_id                    => l_dml_unit_id);
2632                END IF;
2633                IF l_plsql_unit_id IS NOT NULL THEN
2634                   ADD_ENGINE_UNIT(p_task_id,
2635                                   FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_PLSQL_SET,
2636                                   p_concurrent_group_unit_id    => l_unit_id,
2637                                   px_unit_id                    => l_plsql_unit_id);
2638                END IF;
2639             ELSE
2640                --create topmost unit depending on which unit_id isn't null
2641                IF l_dml_unit_id IS NOT NULL THEN
2642                   ADD_ENGINE_UNIT(p_task_id,
2643                                   FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_DML_SET,
2644                                   p_phase                       => px_bound_operations_phase,
2645                                   p_weight                      => l_unit_logical_weight,
2646                                   p_unit_object_owner           => l_table_owner,
2647                                   p_unit_object_name            => l_table_name,
2648                                   px_unit_id                    => l_dml_unit_id);
2649                ELSIF l_plsql_unit_id IS NOT NULL THEN
2650                   ADD_ENGINE_UNIT(p_task_id,
2651                                   FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_PLSQL_SET,
2652                                   p_phase                       => px_bound_operations_phase,
2653                                   p_weight                      => l_unit_logical_weight,
2654                                   p_unit_object_owner           => l_table_owner,
2655                                   p_unit_object_name            => l_table_name,
2656                                   px_unit_id                    => l_plsql_unit_id);
2657                END IF;
2658             END IF;
2659 
2660             --increment the phase
2661             px_bound_operations_phase := px_bound_operations_phase + B_PHASE_INCREMENT;
2662 
2663             fnd_oam_debug.log(1, l_ctxt, 'Unit logical/physical weight: ('||l_unit_logical_weight||')('||l_unit_physical_weight||')');
2664             --integrate the unit's logical/physical weights
2665             INTEGRATE_WEIGHTS(l_total_logical_weight,
2666                               l_total_physical_weight,
2667                               l_unit_logical_weight,
2668                               l_unit_physical_weight);
2669 
2670             l_table_name := px_table_bound_map(l_table_owner).NEXT(l_table_name);
2671          END LOOP;
2672          l_table_owner := px_table_bound_map.NEXT(l_table_owner);
2673       END LOOP;
2674 
2675       COMMIT;
2676 
2677       --success
2678       x_logical_weight := l_total_logical_weight;
2679       x_physical_weight := l_total_physical_weight;
2680       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2681       RETURN TRUE;
2682    EXCEPTION
2683       WHEN PROGRAM_ERROR THEN
2684          ROLLBACK;
2685          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2686          RAISE;
2687       WHEN OTHERS THEN
2688          ROLLBACK;
2689          --mark the run becaues we have no place better.
2690          MARK_OBJECT_AS_ERRORED(b_run.object_id,
2691                                 l_ctxt,
2692                                 SQLCODE,
2693                                 SQLERRM);
2694          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
2695          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
2696          RAISE;
2697    END;
2698 
2699    -- Private, helper to COMPILE_CONFIG_INSTANCE to dump out tasks/units for a specific primary domain.
2700    FUNCTION ADD_ENGINE_UNITS_FOR_DOMAIN(p_task_id                       IN NUMBER,
2701                                         p_primary_domain                IN VARCHAR2,
2702                                         px_truncate_phase               IN OUT NOCOPY NUMBER,
2703                                         px_unbound_plsql_phase          IN OUT NOCOPY NUMBER,
2704                                         px_bound_operations_phase       IN OUT NOCOPY NUMBER,
2705                                         x_logical_weight                OUT NOCOPY NUMBER,
2706                                         x_physical_weight               OUT NOCOPY NUMBER)
2707       RETURN BOOLEAN
2708    IS
2709       PRAGMA AUTONOMOUS_TRANSACTION;
2710 
2711       l_ctxt            VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_UNITS_FOR_DOMAIN';
2712 
2713       --Types used to dup-check truncates for a domain
2714       --Hash Table of table_name->truncate object_id
2715       TYPE l_truncate_table_name_map_t IS TABLE OF NUMBER INDEX BY VARCHAR2(30);
2716       --Hash Table of owner_name->table_name map
2717       TYPE l_truncate_table_owner_map_t IS TABLE OF l_truncate_table_name_map_t INDEX BY VARCHAR2(30);
2718 
2719       --temporarily collect truncates into this map
2720       l_truncate_table_owner_map        l_truncate_table_owner_map_t;
2721 
2722       --collect table_bound objects in the domain into this map to pass to ADD_ENGINE_TABLE_BOUND_UNITS
2723       l_table_bound_map         b_table_bound_map_type;
2724 
2725       --counters for the weight of this domain
2726       l_domain_logical_weight           NUMBER := 0;
2727       l_domain_physical_weight          NUMBER := 0;
2728 
2729       --temporary structures/variables
2730       l_table_objects_def               b_table_objects_def_type; --allocate a new one using it's create each time
2731       l_column_map                      b_column_name_map_type;
2732       l_segment                         b_dml_update_segment_def_type;
2733       l_delete_stmt                     b_dml_delete_stmt_def_type;
2734       l_truncate_stmt                   b_dml_truncate_stmt_def_type;
2735       l_plsql                           b_plsql_text_def_type;
2736       l_truncate_table_name_map         l_truncate_table_name_map_t;
2737 
2738       l_object_id               NUMBER;
2739       l_object_id2              NUMBER;
2740       l_unit_id                 NUMBER;
2741       l_dml_id                  NUMBER;
2742       l_plsql_id                NUMBER;
2743       l_weight                  NUMBER;
2744       l_table_owner             VARCHAR2(30);
2745       l_table_name              VARCHAR2(30);
2746       l_where_clause            VARCHAR2(4000);
2747       l_logical_weight          NUMBER;
2748       l_physical_weight         NUMBER;
2749       k                         NUMBER;
2750    BEGIN
2751       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
2752 
2753       -- loop through the domain's contents, grouping the objects
2754       k := b_primary_domains(p_primary_domain).FIRST;
2755       WHILE k IS NOT NULL LOOP
2756          l_object_id := b_primary_domains(p_primary_domain)(k);
2757          fnd_oam_debug.log(1, l_ctxt, 'Parsing object_id: '||l_object_id||'('||b_objects(l_object_id).object_type||')');
2758          --case the object type
2759          CASE b_objects(l_object_id).object_type
2760             WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_UPDATE_SEGMENT THEN
2761                l_segment := b_dml_update_segments(l_object_id);
2762 
2763                --get the table_objects_def reference to update
2764                l_table_objects_def := GET_TABLE_OBJECTS_DEF(l_table_bound_map,
2765                                                             l_segment.table_owner,
2766                                                             l_segment.table_name);
2767 
2768                --ADD this update segment to the table_objects_def if it doesn't conflict
2769 
2770                --see if the where clause exists
2771                l_where_clause := NVL(l_segment.where_clause, '');
2772                IF l_table_objects_def.update_map.EXISTS(l_where_clause) THEN
2773                   --see if the column name's been seen before
2774                   IF l_table_objects_def.update_map(l_where_clause).EXISTS(l_segment.column_name) THEN
2775                      --see if the values are the same, if not this is a conflict otherwise it's just a dupe
2776                      l_object_id2 := l_table_objects_def.update_map(l_where_clause)(l_segment.column_name);
2777                      IF (b_dml_update_segments(l_object_id2).new_column_value <> l_segment.new_column_value) THEN
2778                         MARK_OBJECT_AS_ERRORED(l_object_id,
2779                                                'New Column Value conflicts with Object ID: '||l_object_id2);
2780                      ELSE
2781                         MARK_OBJECT_WITH_WARNING(l_object_id,
2782                                                  l_ctxt,
2783                                                  'Duplicate of Object ID: '||l_object_id2);
2784                      END IF;
2785                   ELSE
2786                      --new column_name
2787                      l_table_objects_def.update_map(l_where_clause)(l_segment.column_name) := l_object_id;
2788                   END IF;
2789                ELSE
2790                   --new where clause
2791                   l_column_map.DELETE;
2792                   l_column_map(l_segment.column_name) := l_object_id;
2793                   l_table_objects_def.update_map(l_where_clause) := l_column_map;
2794                END IF;
2795 
2796                --set the table_objects_def back
2797                l_table_bound_map(l_segment.table_owner)(l_segment.table_name) := l_table_objects_def;
2798             WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_DELETE_STMT THEN
2799                l_delete_stmt := b_dml_delete_stmts(l_object_id);
2800 
2801                --get the table_objects_def reference to update
2802                l_table_objects_def := GET_TABLE_OBJECTS_DEF(l_table_bound_map,
2803                                                             l_delete_stmt.table_owner,
2804                                                             l_delete_stmt.table_name);
2805 
2806                --ADD this update segment to the table_objects_def if it doesn't conflict
2807 
2808                --see if the where clause exists
2809                l_where_clause := NVL(l_delete_stmt.where_clause, '');
2810                IF l_table_objects_def.delete_map.EXISTS(l_where_clause) THEN
2811                   --can't delete the same table/where clause twice, mark it as a dupe
2812                   MARK_OBJECT_WITH_WARNING(l_object_id,
2813                                            l_ctxt,
2814                                            'Duplicate of Object ID: '||l_table_objects_def.delete_map(l_where_clause));
2815                ELSE
2816                   --add it
2817                   fnd_oam_debug.log(1, l_ctxt, 'New where clause');
2818                   l_table_objects_def.delete_map(NVL(l_where_clause, '')) := l_object_id;
2819                END IF;
2820 
2821                --set the table_objects_def back
2822                l_table_bound_map(l_delete_stmt.table_owner)(l_delete_stmt.table_name) := l_table_objects_def;
2823             WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_DML_TRUNCATE_STMT THEN
2824                l_truncate_stmt := b_dml_truncate_stmts(l_object_id);
2825 
2826                --if the run mode isn't normal, truncates need to become deletes so they can be rolled back
2827                IF b_run.run_mode <> FND_OAM_DSCRAM_UTILS_PKG.G_MODE_NORMAL THEN
2828                   --since these deletes can take a long time, give a more accurate perf viewpoint by just skipping
2829                   --the truncate
2830                   MARK_OBJECT_WITH_WARNING(l_object_id,
2831                                            l_ctxt,
2832                                            'Skipping Truncate operation because of run mode: '||b_run.run_mode);
2833                   /*
2834                   --convert this truncate to a delete
2835                   --get the table_objects_def reference to update
2836                   l_table_objects_def := GET_TABLE_OBJECTS_DEF(l_table_bound_map,
2837                                                                l_truncate_stmt.table_owner,
2838                                                                l_truncate_stmt.table_name);
2839 
2840                   --ADD this delete segment to the table_objects_def if it doesn't conflict
2841 
2842                   --see if an entry exists for a NULL where clause
2843                   IF l_table_objects_def.delete_map.EXISTS(NULL) THEN
2844                      --can't delete the same table/where clause twice, mark it as a dupe
2845                      MARK_OBJECT_WITH_WARNING(l_object_id,
2846                                               l_ctxt,
2847                                               'Duplicate of Object ID: '||l_table_objects_def.delete_map(NULL));
2848                   ELSE
2849                      --add it
2850                      l_table_objects_def.delete_map(NULL) := l_object_id;
2851                   END IF;
2852 
2853                   --set the table_objects_def back
2854                   l_table_bound_map(l_truncate_stmt.table_owner)(l_truncate_stmt.table_name) := l_table_objects_def;
2855                   */
2856                ELSE
2857                   --instead of creating the truncate right away, look for a dup in the truncate_owner_map, store non-dups
2858                   --in this struct for processing outside of this domain objects loop.
2859                   IF l_truncate_table_owner_map.EXISTS(l_truncate_stmt.table_owner) THEN
2860                      IF l_truncate_table_owner_map(l_truncate_stmt.table_owner).EXISTS(l_truncate_stmt.table_name) THEN
2861                         --dupe
2862                         MARK_OBJECT_WITH_WARNING(l_object_id,
2863                                                  l_ctxt,
2864                                                  'Duplicate of Object ID: '||l_truncate_table_owner_map(l_truncate_stmt.table_owner)(l_truncate_stmt.table_name));
2865                      ELSE
2866                         --new table name
2867                         l_truncate_table_owner_map(l_truncate_stmt.table_owner)(l_truncate_stmt.table_name) := l_object_id;
2868                      END IF;
2869                   ELSE
2870                      --new owner
2871                      l_truncate_table_name_map.DELETE;
2872                      l_truncate_table_name_map(l_truncate_stmt.table_name) := l_object_id;
2873                      l_truncate_table_owner_map(l_truncate_stmt.table_owner) := l_truncate_table_name_map;
2874                   END IF;
2875 
2876                END IF;
2877             WHEN FND_OAM_DSCFG_API_PKG.G_OTYPE_PLSQL_TEXT THEN
2878                l_plsql := b_plsql_texts(l_object_id);
2879 
2880                --see if we've got a table owner and name, meaning the pl/sql is table bound
2881                IF l_plsql.table_owner IS NOT NULL and l_plsql.table_name IS NOT NULL THEN
2882                   --get the table_objects_def reference to update
2883                   l_table_objects_def := GET_TABLE_OBJECTS_DEF(l_table_bound_map,
2884                                                                l_plsql.table_owner,
2885                                                                l_plsql.table_name);
2886 
2887                   --ADD this plsql to the list of plsqls
2888 
2889                   l_table_objects_def.plsqls.EXTEND;
2890                   l_table_objects_def.plsqls(l_table_objects_def.plsqls.COUNT) := l_object_id;
2891 
2892                   --set the table_objects_def back
2893                   l_table_bound_map(l_plsql.table_owner)(l_plsql.table_name) := l_table_objects_def;
2894                ELSE
2895                   --unbound pl/sql, run in phases after truncate and also can't be split
2896                   BEGIN
2897                      l_unit_id := NULL;
2898                      ADD_ENGINE_UNIT(p_task_id,
2899                                      FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_PLSQL_SET,
2900                                      p_phase                    => px_unbound_plsql_phase,
2901                                      p_weight                   => l_plsql.weight,
2902                                      p_disable_splitting        => FND_API.G_TRUE,
2903                                      px_unit_id                 => l_unit_id);
2904 
2905                      --and its corresponding PL/SQL
2906                      ADD_ENGINE_PLSQL(l_unit_id,
2907                                       l_plsql.plsql_text,
2908                                       p_weight          => l_plsql.weight,
2909                                       x_plsql_id        => l_plsql_id);
2910 
2911                      --increment the unbound plsql phase
2912                      px_unbound_plsql_phase := px_unbound_plsql_phase + B_PHASE_INCREMENT;
2913                      INTEGRATE_WEIGHTS(l_domain_logical_weight,
2914                                        l_domain_physical_weight,
2915                                        l_plsql.weight,
2916                                        l_plsql.weight);
2917                   EXCEPTION
2918                      WHEN OTHERS THEN
2919                         --put the error in the object and move on
2920                         MARK_OBJECT_AS_ERRORED(l_object_id,
2921                                                l_ctxt,
2922                                                SQLCODE,
2923                                                SQLERRM);
2924                   END;
2925                END IF;
2926             ELSE
2927                --unmatched object type, shouldn't happen
2928                fnd_oam_debug.log(6, l_ctxt, 'Unknown Object Type: '||b_objects(l_object_id).object_type);
2929                RAISE PROGRAM_ERROR;
2930             END CASE;
2931 
2932          k := b_primary_domains(p_primary_domain).NEXT(k);
2933       END LOOP;
2934 
2935       --loop over the truncates, create solo units for each
2936       l_table_owner := l_truncate_table_owner_map.FIRST;
2937       WHILE l_table_owner IS NOT NULL LOOP
2938          l_table_name := l_truncate_table_owner_map(l_table_owner).FIRST;
2939          WHILE l_table_name IS NOT NULL LOOP
2940             l_object_id := l_truncate_table_owner_map(l_table_owner)(l_table_name);
2941             l_truncate_stmt := b_dml_truncate_stmts(l_object_id);
2942 
2943             --figure out the true weight to use
2944             l_weight := CEIL(NVL(l_truncate_stmt.weight, B_TRUNCATE_WEIGHT_MODIFIER *
2945                                                          FND_OAM_DSCFG_UTILS_PKG.GET_TABLE_WEIGHT(l_table_owner,
2946                                                                                                   l_table_name)));
2947             BEGIN
2948                l_unit_id := NULL;
2949                --even though truncates don't use AD splitting, store the unit object owner/name to simplify reporting
2950                ADD_ENGINE_UNIT(p_task_id,
2951                                FND_OAM_DSCRAM_UTILS_PKG.G_UNIT_TYPE_DML_SET,
2952                                p_phase                  => px_truncate_phase,
2953                                p_weight                 => l_weight,
2954                                p_unit_object_owner      => l_table_owner,
2955                                p_unit_object_name       => l_table_name,
2956                                p_disable_splitting      => FND_API.G_TRUE,
2957                                px_unit_id               => l_unit_id);
2958 
2959                --and its corresponding DML
2960                --TODO: See if the PURGE MATERIALIZED VIEW LOG option is acceptable here
2961                ADD_ENGINE_DML(l_unit_id,
2962                               'TRUNCATE TABLE '||l_table_owner||'.'||l_table_name,
2963                               p_weight  => l_weight,
2964                               x_dml_id  => l_dml_id);
2965 
2966                --increment the truncate phase and update the weight counters
2967                px_truncate_phase := px_truncate_phase + B_PHASE_INCREMENT;
2968                INTEGRATE_WEIGHTS(l_domain_logical_weight,
2969                                  l_domain_physical_weight,
2970                                  l_weight,
2971                                  l_weight);
2972             EXCEPTION
2973                WHEN OTHERS THEN
2974                   --put the error in the object and move on
2975                   MARK_OBJECT_AS_ERRORED(l_object_id,
2976                                          l_ctxt,
2977                                          SQLCODE,
2978                                          SQLERRM);
2979             END;
2980             l_table_name := l_truncate_table_owner_map(l_table_owner).NEXT(l_table_name);
2981          END LOOP;
2982          l_table_owner := l_truncate_table_owner_map.NEXT(l_table_owner);
2983       END LOOP;
2984 
2985       -- split out the logic to create the table bound units and integrate their collective weight into the domain's
2986       -- overall weight.
2987       IF ADD_ENGINE_TABLE_BOUND_UNITS(p_task_id,
2988                                       l_table_bound_map,
2989                                       px_bound_operations_phase,
2990                                       l_logical_weight,
2991                                       l_physical_weight) THEN
2992          INTEGRATE_WEIGHTS(l_domain_logical_weight,
2993                            l_domain_physical_weight,
2994                            l_logical_weight,
2995                            l_physical_weight);
2996       END IF;
2997 
2998       COMMIT;
2999 
3000       --success
3001       fnd_oam_debug.log(1, l_ctxt, 'Domain logical/physical weights: ('||l_domain_logical_weight||')('||l_domain_physical_weight||')');
3002       x_logical_weight := l_domain_logical_weight;
3003       x_physical_weight := l_domain_physical_weight;
3004       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3005       RETURN TRUE;
3006    EXCEPTION
3007       WHEN PROGRAM_ERROR THEN
3008          ROLLBACK;
3009          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3010          RAISE;
3011       WHEN OTHERS THEN
3012          ROLLBACK;
3013          --mark the run becaues we have no place better.
3014          MARK_OBJECT_AS_ERRORED(b_run.object_id,
3015                                 l_ctxt,
3016                                 SQLCODE,
3017                                 SQLERRM);
3018          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3019          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3020          RAISE;
3021    END;
3022 
3023    -- To support multiple bundles/bundle work partitioning, use this call to encapsulate the
3024    -- assignment of a task to a bundle based on whatever scheme we figure is best.  Returns the object_id
3025    -- of the bundle_metadata object.
3026    -- Invariant: b_bundles_metadata has at least one entry by now
3027    FUNCTION GET_BUNDLE_FOR_GROUP(p_group        b_dependency_group_def_type)
3028       RETURN NUMBER
3029    IS
3030       l_least_weight_id         NUMBER := NULL;
3031       l_least_weight            NUMBER := NULL;
3032       l_least_count_id          NUMBER := NULL;
3033       l_least_count             NUMBER := NULL;
3034 
3035       k                         NUMBER;
3036    BEGIN
3037       --skip assignment if there's only one, the common case
3038       IF b_bundles.COUNT > 1 THEN
3039          --find the bundle with the least physical_weight
3040          k := b_bundles.FIRST;
3041          WHILE k IS NOT NULL LOOP
3042             IF l_least_weight IS NULL OR b_bundles(k).assigned_physical_weight < l_least_weight THEN
3043                l_least_weight := b_bundles(k).assigned_physical_weight;
3044                l_least_weight_id := k;
3045             END IF;
3046             IF l_least_count IS NULL OR b_bundles(k).assigned_task_count < l_least_count THEN
3047                l_least_count := b_bundles(k).assigned_task_count;
3048                l_least_count_id := k;
3049             END IF;
3050             k := b_bundles.NEXT(k);
3051          END LOOP;
3052 
3053          --if the least weight is > 0, use it - otherwise use the least count which we know is incrementing.
3054          IF l_least_weight > 0 THEN
3055             RETURN l_least_weight_id;
3056          ELSE
3057             RETURN l_least_count_id;
3058          END IF;
3059       ELSE
3060          --only one bundle, return it's id
3061          RETURN b_bundles.FIRST;
3062       END IF;
3063    END;
3064 
3065    -- Private, helper to GENERATE_ENGINE_TASKS to dump out tasks/units for a specific dependency group
3066    FUNCTION ADD_ENGINE_TASK_WITH_UNITS(p_group_id               IN NUMBER)
3067       RETURN BOOLEAN
3068    IS
3069       PRAGMA AUTONOMOUS_TRANSACTION;
3070 
3071       l_ctxt            VARCHAR2(60) := PKG_NAME||'ADD_ENGINE_TASK_WITH_UNITS';
3072 
3073       l_bundle_object_id        NUMBER;
3074       l_task_id                 NUMBER;
3075       l_truncate_phase          NUMBER := B_BASE_PHASE_TRUNCATES;
3076       l_unbound_plsql_phase     NUMBER := B_BASE_PHASE_UNBOUND_PLSQLS;
3077       l_bound_operations_phase  NUMBER := B_BASE_PHASE_BOUND_OPERATIONS;
3078       l_task_logical_weight     NUMBER := 0;
3079       l_task_physical_weight    NUMBER := 0;
3080 
3081       l_domain                  VARCHAR2(120);
3082       l_weight                  NUMBER;
3083       l_logical_weight          NUMBER;
3084       l_physical_weight         NUMBER;
3085 
3086       k                         NUMBER;
3087       j                         NUMBER;
3088       l_msg                     VARCHAR2(4000);
3089    BEGIN
3090       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
3091 
3092       -- fetch a task_id to use for creating units
3093       SELECT FND_OAM_DSCRAM_TASKS_S.NEXTVAL
3094          INTO l_task_id
3095          FROM dual;
3096       b_dependency_groups(p_group_id).task_id := l_task_id;
3097       fnd_oam_debug.log(1, l_ctxt, 'Using task_id: '||l_task_id);
3098 
3099       -- loop through the primary domains, creating units for each kind of object in each domain - we don't allow
3100       -- collecting objects across primary domains.
3101       k := b_dependency_groups(p_group_id).primary_domains.FIRST;
3102       WHILE k IS NOT NULL LOOP
3103          l_domain := b_dependency_groups(p_group_id).primary_domains(k);
3104          fnd_oam_debug.log(1, l_ctxt, 'Processing primary_domain: '||l_domain);
3105          --for a given domain, create the units, only do weight calculation if it came back true for sucess.
3106          IF ADD_ENGINE_UNITS_FOR_DOMAIN(l_task_id,
3107                                         l_domain,
3108                                         l_truncate_phase,
3109                                         l_unbound_plsql_phase,
3110                                         l_bound_operations_phase,
3111                                         l_logical_weight,
3112                                         l_physical_weight) THEN
3113             --only integrate the logical_weight, physical_weight if user hasn't supplied a weight
3114             IF b_dependency_groups(p_group_id).weight IS NULL THEN
3115                INTEGRATE_WEIGHTS(l_task_logical_weight,
3116                                  l_task_physical_weight,
3117                                  l_logical_weight,
3118                                  l_physical_weight);
3119             END IF;
3120          END IF;
3121 
3122          k := b_dependency_groups(p_group_id).primary_domains.NEXT(k);
3123       END LOOP;
3124 
3125       fnd_oam_debug.log(1, l_ctxt, 'Task logical/physical weights: ('||l_task_logical_weight||')('||l_task_physical_weight||')');
3126       -- now that we've composed the task components, figure out what bundle to give it to and make the task
3127       fnd_oam_debug.log(1, l_ctxt, 'Getting bundle for group...');
3128       l_bundle_object_id := GET_BUNDLE_FOR_GROUP(b_dependency_groups(p_group_id));
3129       fnd_oam_debug.log(1, l_ctxt, 'Using Bundle_id: '||b_bundles(l_bundle_object_id).bundle_id);
3130       l_weight := NVL(b_dependency_groups(p_group_id).weight, l_task_logical_weight);
3131       fnd_oam_debug.log(1, l_ctxt, 'Using Task Weight: '||l_weight);
3132 
3133       --skip the PRIORITY column for now
3134       INSERT INTO FND_OAM_DSCRAM_TASKS (TASK_ID,
3135                                         BUNDLE_ID,
3136                                         TASK_STATUS,
3137                                         WEIGHT,
3138                                         WORKERS_ASSIGNED,
3139                                         CREATED_BY,
3140                                         CREATION_DATE,
3141                                         LAST_UPDATED_BY,
3142                                         LAST_UPDATE_DATE,
3143                                         LAST_UPDATE_LOGIN
3144                                         )
3145          VALUES
3146             (l_task_id,
3147              b_bundles(l_bundle_object_id).bundle_id,
3148              FND_OAM_DSCRAM_UTILS_PKG.G_STATUS_UNPROCESSED,
3149              l_weight,
3150              0,
3151              FND_GLOBAL.USER_ID,
3152              SYSDATE,
3153              FND_GLOBAL.USER_ID,
3154              SYSDATE,
3155              FND_GLOBAL.USER_ID);
3156 
3157       --update the bundle's assigned weight and task count
3158       b_bundles(l_bundle_object_id).assigned_task_count := b_bundles(l_bundle_object_id).assigned_task_count + 1;
3159       b_bundles(l_bundle_object_id).assigned_physical_weight := b_bundles(l_bundle_object_id).assigned_physical_weight + NVL(l_weight, l_task_physical_weight);
3160 
3161       COMMIT;
3162 
3163       --success
3164       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3165       RETURN TRUE;
3166    EXCEPTION
3167       WHEN PROGRAM_ERROR THEN
3168          ROLLBACK;
3169          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3170          --quick fail, cause other tasks to skip so we can write the error somewhere
3171          RAISE;
3172       WHEN OTHERS THEN
3173          ROLLBACK;
3174          --mark the run object
3175          MARK_OBJECT_AS_ERRORED(b_run.object_id,
3176                                 l_ctxt,
3177                                 SQLCODE,
3178                                 SQLERRM);
3179          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3180          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3181          RAISE PROGRAM_ERROR;
3182    END;
3183 
3184    -- Private, helper to COMPILE_CONFIG_INSTANCE to dump out tasks/units
3185    PROCEDURE GENERATE_ENGINE_TASKS
3186    IS
3187       l_ctxt            VARCHAR2(60) := PKG_NAME||'GENERATE_ENGINE_TASKS';
3188 
3189       l_group_id                NUMBER;
3190       l_ignore                  BOOLEAN;
3191 
3192       k                         NUMBER;
3193    BEGIN
3194       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
3195 
3196       -- loop through the dependency groups
3197       l_group_id := b_dependency_groups.FIRST;
3198       WHILE l_group_id IS NOT NULL LOOP
3199          --TODO: come up with a way to identify a task we've already compiled.
3200          --Problem: group_ids are transient and can be totally different based on the domain distribution.  re-compiling
3201          --and trying to match them up would require a full task comparison?
3202          --For now, just create a new task for each dependency group
3203          fnd_oam_debug.log(1, l_ctxt, 'Adding Task for group_id: '||l_group_id);
3204          l_ignore := ADD_ENGINE_TASK_WITH_UNITS(l_group_id);
3205 
3206          l_group_id := b_dependency_groups.NEXT(l_group_id);
3207       END LOOP;
3208 
3209       --success
3210       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3211    EXCEPTION
3212       WHEN PROGRAM_ERROR THEN
3213          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3214          RAISE;
3215       WHEN OTHERS THEN
3216          --mark the run object
3217          MARK_OBJECT_AS_ERRORED(b_run.object_id,
3218                                 l_ctxt,
3219                                 SQLCODE,
3220                                 SQLERRM);
3221          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3222          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3223          RAISE PROGRAM_ERROR;
3224    END;
3225 
3226    -- Private, helper to COMPILE_CONFIG_INSTANCE to write the updated weight of the run/bundles to the db.
3227    PROCEDURE UPDATE_RUN_BUNDLE_WEIGHTS
3228    IS
3229       PRAGMA AUTONOMOUS_TRANSACTION;
3230 
3231       l_ctxt            VARCHAR2(60) := PKG_NAME||'UPDATE_RUN_BUNDLE_WEIGHTS';
3232       l_weight          NUMBER;
3233       k                         NUMBER;
3234    BEGIN
3235       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
3236 
3237       k := b_bundles.FIRST;
3238       WHILE k IS NOT NULL LOOP
3239          l_weight := NVL(b_bundles(k).weight, b_bundles(k).assigned_physical_weight);
3240          --add this bundle's weight to the run's assigned_physical_weight
3241          b_run.assigned_physical_weight := b_run.assigned_physical_weight + l_weight;
3242          fnd_oam_debug.log(1, l_ctxt, 'Updating bundle_id('||b_bundles(k).bundle_id||') weight('||l_weight||')');
3243          UPDATE fnd_oam_dscram_bundles
3244             SET weight = l_weight
3245             WHERE bundle_id = b_bundles(k).bundle_id;
3246          k := b_bundles.NEXT(k);
3247       END LOOP;
3248 
3249       l_weight := NVL(b_run.weight, b_run.assigned_physical_weight);
3250       fnd_oam_debug.log(1, l_ctxt, 'Updating run_id('||b_run.run_id||') weight ('||l_weight||')');
3251       UPDATE fnd_oam_dscram_runs_b
3252          SET weight = l_weight
3253          WHERE run_id = b_run.run_id;
3254 
3255       COMMIT;
3256 
3257       --success
3258       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3259    EXCEPTION
3260       WHEN OTHERS THEN
3261          ROLLBACK;
3262          MARK_OBJECT_AS_ERRORED(b_run.object_id,
3263                                 l_ctxt,
3264                                 SQLCODE,
3265                                 SQLERRM);
3266          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3267          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3268          RAISE PROGRAM_ERROR;
3269    END;
3270 
3271    -- Private, helper to COMPILE_CONFIG_INSTANCE to write dirty objects to the db.
3272    PROCEDURE WRITE_DIRTY_OBJECTS
3273    IS
3274       PRAGMA AUTONOMOUS_TRANSACTION;
3275 
3276       l_ctxt            VARCHAR2(60) := PKG_NAME||'WRITE_DIRTY_OBJECTS';
3277 
3278       k                         NUMBER;
3279       l_error_found             BOOLEAN := FALSE;
3280    BEGIN
3281       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
3282 
3283       --TODO: see if we can make this bulk
3284       --TODO: need to write the target_type/target_id at some point - probably requires many changes.
3285       k := b_objects.FIRST;
3286       WHILE k IS NOT NULL LOOP
3287          IF b_objects(k).is_dirty THEN
3288             --all objects in b_objects should have db objects already created so just try to update
3289             BEGIN
3290                UPDATE fnd_oam_dscfg_objects
3291                   SET errors_found_flag = b_objects(k).new_errors_found_flag,
3292                   message = b_objects(k).new_message,
3293                   target_type = b_objects(k).target_type,
3294                   target_id = b_objects(k).target_id,
3295                   last_update_date = SYSDATE,
3296                   last_updated_by = FND_GLOBAL.USER_ID,
3297                   last_update_login = FND_GLOBAL.USER_ID
3298                   WHERE object_id = b_objects(k).object_id;
3299             EXCEPTION
3300                WHEN OTHERS THEN
3301                   fnd_oam_debug.log(3, l_ctxt, 'While writing object_id('||b_objects(k).object_id||'): Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3302                   l_error_found := TRUE;
3303             END;
3304          END IF;
3305          k := b_objects.NEXT(k);
3306       END LOOP;
3307 
3308       --commit whatever we could do
3309       COMMIT;
3310 
3311       --raise an error if we couldn't write something
3312       IF l_error_found THEN
3313          RAISE STORAGE_ERROR;
3314       END IF;
3315 
3316       --success
3317       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3318    EXCEPTION
3319       WHEN STORAGE_ERROR THEN
3320          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3321          RAISE;
3322       WHEN OTHERS THEN
3323          ROLLBACK;
3324          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3325          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3326          --raise a special kind of error to signal the caller
3327          RAISE STORAGE_ERROR;
3328    END;
3329 
3330    -- Public
3331    PROCEDURE COMPILE_CONFIG_INSTANCE(x_run_id   OUT NOCOPY NUMBER)
3332    IS
3333       l_ctxt            VARCHAR2(60) := PKG_NAME||'COMPILE_CONFIG_INSTANCE';
3334 
3335       l_config_instance_id      NUMBER;
3336    BEGIN
3337       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
3338 
3339       --get the config_instance_id, throws error if not initialized
3340       l_config_instance_id := FND_OAM_DSCFG_INSTANCES_PKG.GET_CURRENT_ID;
3341       fnd_oam_debug.log(1, l_ctxt, 'Using config instance id: '||l_config_instance_id);
3342 
3343       --clear the package state data
3344       fnd_oam_debug.log(1, l_ctxt, 'Resetting package state variables');
3345       b_objects.DELETE;
3346       b_dml_update_segments.DELETE;
3347       b_dml_delete_stmts.DELETE;
3348       b_dml_truncate_stmts.DELETE;
3349       b_primary_domains.DELETE;
3350       b_domain_metadata_map.DELETE;
3351       b_dependency_groups.DELETE;
3352       b_domain_to_group_map.DELETE;
3353       b_run.object_id := NULL;
3354       b_bundles.DELETE;
3355 
3356       --bulk fetch the objects from the database and fill the state
3357       fnd_oam_debug.log(1, l_ctxt, 'Fetching Compilable Objects');
3358       FETCH_COMPILABLE_OBJECTS(l_config_instance_id);
3359 
3360       --now that all the objects have been fetched and cached, traverse the caches
3361       --and figure out which primary domains are related to one another and group them into
3362       --dependency groups
3363       fnd_oam_debug.log(1, l_ctxt, 'Computing Dependency Groups');
3364       COMPUTE_DEPENDENCY_GROUPS;
3365 
3366       --ALL of the data should be parsed into groups and validated by this point.
3367 
3368       --dump out the engine entities, if any fail, skip processing the rest since we don't
3369       --have a solid way to
3370       BEGIN
3371          fnd_oam_debug.log(1, l_ctxt, 'Generating an Engine Run Entity');
3372          GENERATE_ENGINE_RUN(l_config_instance_id);
3373 
3374          --generate the engine bundles
3375          fnd_oam_debug.log(1, l_ctxt, 'Generating Engine Bundles');
3376          GENERATE_ENGINE_BUNDLES;
3377 
3378          --traverse the dependency groups and output
3379          fnd_oam_debug.log(1, l_ctxt, 'Generating Engine Tasks');
3380          GENERATE_ENGINE_TASKS;
3381 
3382          --write out the updated run and bundle weights
3383          UPDATE_RUN_BUNDLE_WEIGHTS;
3384       EXCEPTION
3385          WHEN PROGRAM_ERROR THEN
3386             -- a child function failed, it should have written a message somewhere.
3387             RAISE;
3388          WHEN OTHERS THEN
3389             --if we can write this error to the run, do so
3390             IF b_run.object_id IS NOT NULL THEN
3391                MARK_OBJECT_AS_ERRORED(b_run.object_id,
3392                                       l_ctxt,
3393                                       SQLCODE,
3394                                       SQLERRM);
3395             END IF;
3396             RAISE PROGRAM_ERROR;
3397       END;
3398 
3399       --finally write out all dirty objects
3400       WRITE_DIRTY_OBJECTS;
3401 
3402       --success
3403       x_run_id := b_run.run_id;
3404       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3405    EXCEPTION
3406       WHEN STORAGE_ERROR THEN
3407          -- only raised by write_dirty_objects to tell us it failed and we should skip trying to
3408          -- write the dirty objects again.
3409          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3410          RAISE;
3411       WHEN PROGRAM_ERROR THEN
3412          -- one of the child methods failed, try to write dirty objects out
3413          BEGIN
3414             WRITE_DIRTY_OBJECTS;
3415          EXCEPTION
3416             WHEN OTHERS THEN
3417                -- if we can't then there's nothing else we can do but raise the exception and hope
3418                -- there's something in the logs.
3419                null;
3420          END;
3421          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3422          RAISE;
3423       WHEN NO_DATA_FOUND THEN
3424          --just exit, this should only occur at the beginning before processing
3425          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3426          RAISE;
3427       WHEN OTHERS THEN
3428          -- this should only occur locally if something is really wrong, just raise it up
3429          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3430          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3431          RAISE;
3432    END;
3433 
3434    -- Public
3435    FUNCTION GET_DEFAULT_NUM_WORKERS
3436       RETURN NUMBER
3437    IS
3438       l_ctxt            VARCHAR2(60) := PKG_NAME||'GET_DEFAULT_NUM_WORKERS';
3439    BEGIN
3440       fnd_oam_debug.log(2, l_ctxt, 'ENTER');
3441 
3442       --make sure we've set the default_workers_allowed
3443       IF B_DEFAULT_WORKERS_ALLOWED IS NULL THEN
3444          BEGIN
3445             SELECT NVL(to_number(value), 1) - 1
3446                INTO B_DEFAULT_WORKERS_ALLOWED
3447                FROM v$system_parameter
3448                WHERE name = 'cpu_count';
3449 
3450             IF B_DEFAULT_WORKERS_ALLOWED <= 0 THEN
3451                B_DEFAULT_WORKERS_ALLOWED := 1;
3452             END IF;
3453          EXCEPTION
3454             WHEN OTHERS THEN
3455                B_DEFAULT_WORKERS_ALLOWED := 1;
3456          END;
3457       END IF;
3458       fnd_oam_debug.log(1, l_ctxt, 'Default Workers Allowed: '||B_DEFAULT_WORKERS_ALLOWED);
3459 
3460       fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3461       RETURN B_DEFAULT_WORKERS_ALLOWED;
3462    EXCEPTION
3463       WHEN OTHERS THEN
3464          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3465          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3466          RAISE;
3467    END;
3468 
3469    -- Public
3470    FUNCTION GET_DEFAULT_BATCH_SIZE
3471       RETURN NUMBER
3472    IS
3473       l_ctxt            VARCHAR2(60) := PKG_NAME||'GET_DEFAULT_BATCH_SIZE';
3474    BEGIN
3475       --currently static
3476       RETURN B_DEFAULT_BATCH_SIZE;
3477    EXCEPTION
3478       WHEN OTHERS THEN
3479          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3480          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3481          RAISE;
3482    END;
3483 
3484    -- Public
3485    FUNCTION GET_DFLT_VALID_CHECK_INTERVAL
3486       RETURN NUMBER
3487    IS
3488       l_ctxt            VARCHAR2(60) := PKG_NAME||'GET_DFLT_VALID_CHECK_INTERVAL';
3489    BEGIN
3490       --currently static
3491       RETURN B_DEFAULT_VALID_CHECK_INTERVAL;
3492    EXCEPTION
3493       WHEN OTHERS THEN
3494          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3495          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3496          RAISE;
3497    END;
3498 
3499    -- Public
3500    FUNCTION GET_DFLT_MIN_PARALLEL_WEIGHT
3501       RETURN NUMBER
3502    IS
3503       l_ctxt            VARCHAR2(60) := PKG_NAME||'GET_DFLT_MIN_PARALLEL_WEIGHT';
3504    BEGIN
3505       --currently static
3506       RETURN B_DEFAULT_MIN_PARALLEL_WEIGHT;
3507    EXCEPTION
3508       WHEN OTHERS THEN
3509          fnd_oam_debug.log(6, l_ctxt, 'Unexpected Error: (Code('||SQLCODE||'), Message("'||SQLERRM||'"))');
3510          fnd_oam_debug.log(2, l_ctxt, 'EXIT');
3511          RAISE;
3512    END;
3513 
3514 END FND_OAM_DSCFG_COMPILER_PKG;