Rem $Header: emcore/source/oracle/sysman/emdrep/sql/core/latest/jobs/jobs_engine_pkgbody.sql /st_emcore_10.2.0.4.4db11.2.0.3/1 2011/05/02 02:58:17 lsatyapr Exp $ Rem Rem jobs_engine_pkgbody.sql Rem Rem Copyright (c) 2002, 2011, Oracle and/or its affiliates. Rem All rights reserved. Rem Rem NAME Rem jobs_engine_pkgbody.sql - Rem Rem DESCRIPTION rem This is an internal package that provides low-level job system rem services such as scheduling and dispatching a step, and updating rem the status of a step that has finished executing Rem Rem NOTES Rem Rem Rem MODIFIED (MM/DD/YY) Rem lsatyapr 04/26/11 - Bug11699788 Fix calls to EM_CHECK.NOOP Rem anoopkum 01/19/09 - bug 7714073: submit and allow oms thread to Rem process bookkeeping step instead of dbms Rem thread(emkey issue) Rem lsatyapr 07/17/09 - Backport lsatyapr_bug-7721346 from Rem st_emcore_10.2.0.1.0 Rem lsatyapr 07/17/09 - Bug7721346 Fix get_creds_metadata Rem lsatyapr 06/11/09 - Bug8592095 - Remove deprecated command blocks Rem lsatyapr 03/27/09 - Default values to null while copying params Rem lsatyapr 10/24/08 - Fix indexing in purge criteria Rem lsatyapr 10/12/08 - Backport lsatyapr_bug-7425991 from main Rem lsatyapr 10/09/08 - Add helper proc for large_param ref cnt Rem lsatyapr 10/06/08 - Backport lsatyapr_bug-7357441 from Rem st_emcore_10.2.0.1.0 Rem lsatyapr 09/19/08 - Support large param passing to nested job Rem chyu 09/02/08 - backporting 7119851: reduce the number of skipped Rem execution when the start time is quite far away in Rem the past Rem lsatyapr 07/10/08 - Bug7214155 Avoid exec reschedule on jobtype Rem upgrade Rem lsatyapr 07/02/08 - Handle false positive Rem jashukla 06/03/08 - Bug 7144619 remove internal identifiers Rem lsatyapr 05/05/08 - Bug7010921 Possible sql injection Rem lsatyapr 05/05/08 - Bug 6855856: Reorder call to reset_params Rem lsatyapr 05/05/08 - Backport bug #6528787 Rem lsatyapr 09/10/07 - Backport lsatyapr_bug-6369627 from main Rem lsatyapr 08/30/07 - Bug6369627 Incorrect next schedule (when job Rem completes across midnight) Rem ashugupt 09/10/07 - Backport ashugupt_bug-6390964 from main Rem msasidha 08/30/07 - bug 6376680 Rem sradhakr 08/11/07 - BLR backport of bug 5840623 to 10.2.0.4. Rem lsatyapr 07/26/07 - Backport lsatyapr_bug-6194413 from main Rem lsatyapr 07/17/07 - Bug6194413 Vanishing wait steps Rem rdabbott 07/28/07 - Backport rdabbott_bug-6194784 from main Rem rdabbott 07/19/07 - review Rem rdabbott 07/16/07 - fix 6194784: delete the job schedule when deleting Rem the job Rem lefeng 07/26/07 - Backport lefeng_bug-5069149 from main Rem ashugupt 07/26/07 - fix 6085259 - apply_prop_source should not Rem resolve cluster Rem lsatyapr 07/30/07 - Bug6271381 Retry of nested job Rem kmanicka 07/25/07 - Backport kmanicka_bug-6242897 from main Rem rdabbott 07/06/07 - order by exec_id in proc emd q to prevent deadlocks Rem rdabbott 06/26/07 - fix 6151563: emd loop and delete issue Rem rdabbott 07/02/07 - Backport rdabbott_bug-6151563 from Rem st_emcore_10.2.0.3.1db11 Rem neearora 06/20/07 - bug 6142074 Rem lsatyapr 05/30/07 - Bug6031714 Encrypt only non-key columns Rem lsatyapr 06/03/07 - Backport lsatyapr_bug_6060708a from main Rem kmanicka 05/24/07 - Implement 10.2.0.4 Trusted RemoteOp Rem lsatyapr 05/24/07 - Backport lsatyapr_bug-6060708 from main Rem lsatyapr 05/22/07 - Bug6060708 Do not encrypt cred paramsrc Rem kmanicka 05/22/07 - Backport kmanicka_bug-5890373 from main Rem rdabbott 05/22/07 - bug 5714960b: desupport user suspend with timeout Rem neearora 05/22/07 - added l_an_ack_sev in call to Rem QUEUE_METRIC_NOTIFICATIONS Rem shnavane 05/17/07 - Backport shnavane_bug-5470955 from main Rem lsatyapr 05/20/07 - Backport lsatyapr_bug-6020632 from main Rem lsatyapr 05/14/07 - Bug6020632 - Async op start time incorrect Rem lefeng 05/10/07 - Bug 6044147 Rem lsatyapr 05/09/07 - Backport lsatyapr_bug-6031714 from main Rem lsatyapr 05/07/07 - Bug6031714 Sec Violation encryption of job params Rem rdabbott 05/01/07 - Backport rdabbott_bug-5714960 from main Rem rdabbott 04/18/07 - suspend timeout message for old agent bounce Rem events Rem rdabbott 04/11/07 - fix 5714960: add error output if a step is resumed Rem due to timeout Rem lefeng 05/01/07 - Backport lefeng_bug-5967327 from main Rem kmanicka 04/26/07 - Backport kmanicka_bug-5895148 from main Rem nqureshi 04/25/07 - Rem kmanicka 05/10/06 - implement pdp Rem nqureshi 04/18/07 - XbranchMerge kmanicka_pdp5 from main Rem kmanicka 04/25/07 - Backport kmanicka_bug-5955919 from main Rem lsatyapr 04/16/07 - Backport jvishen_bug-5840734 from main Rem jvishen 03/27/07 - remove error if dispatcher picks step Rem lsatyapr 04/14/07 - Backport lsatyapr_bug-5589962 from main Rem lsatyapr 04/02/07 - Fix security violations due to package vars Rem lsatyapr 04/02/07 - Backport lsatyapr_bug-5494203 from main Rem lsatyapr 03/19/07 - Catch all exceptions in force_abort_execution Rem kmanicka 12/04/06 - Backport kmanicka_bug-5589962 from main Rem kmanicka 11/28/06 - Backport kmanicka_bug-5333409 from main Rem skini 11/22/06 - Fix bug 5677703 Rem skini 11/22/06 - Backport skini_bug-5668591 from main Rem kmanicka 10/23/06 - Backport kmanicka_bug-5610864 from main Rem kmanicka 20/10/06 - bug 5610864 Rem skini 10/05/06 - Lock job before updating target list on group Rem change Rem skini 09/28/06 - Fix bug with group membership changes Rem skini 10/20/06 - Backport skini_bug-5572753 from main Rem kmanicka 09/26/06 - Backport kmanicka_bug-5529138 from main Rem kmanicka 07/10/06 - bug 5365979 fetch job params with step Rem kmanicka 09/04/06 - Backport kmanicka_bug-5365979 from main Rem skini 08/09/06 - Backport skini_bug-5395103 from main Rem lsatyapr 08/06/06 - Backport lsatyapr_bug-5367073 from main Rem skini 08/04/06 - 5395103: Write to step error when error Rem notifications arrive Rem lsatyapr 08/03/06 - Fix bug 5367073 - Step after execAndSuspend fails Rem kmanicka 07/18/06 - Bug 5223528 Perf: Delete Target Rem kmanicka 08/01/06 - Backport kmanicka_bug-5223528 from main Rem kmanicka 08/01/06 - Backport kmanicka_bug5126621 from main Rem nqureshi 07/18/06 - fixing 5150121 INCORRECT TARGETS (0) SHOWN FOR A Rem RETRIED PATCH AGENT JOB Rem nqureshi 07/20/06 - Backport nqureshi_bug-5150121 from main Rem lsatyapr 07/25/06 - Backport lsatyapr_bug-4884900 from main Rem kmanicka 06/14/06 - fix bug 5034533 Rem kmanicka 07/18/06 - Backport kmanicka_bug-5034533 from main Rem lsatyapr 07/23/06 - Backport lsatyapr_bug-5237195 from main Rem lsatyapr 07/06/06 - Use MGMT_FLAT_TARGET_ASSOC instead of Rem MGMT_TARGETS_ASSOC in handle_membership_change Rem lsatyapr 06/06/06 - Fix bad sql in handle_membership_change (5237195) Rem kmanicka 07/19/06 - Backport kmanicka_bug-5172534 from main Rem kmanicka 04/04/06 - add interval for daily schedule Bug 5126621 Rem rrawat 05/31/06 - Bug-5173498 Rem rrawat 07/11/06 - Backport rrawat_bug-5173498 from main Rem jvishen 06/29/06 - fix #5241137 Rem jvishen 07/13/06 - Backport jvishen_bug-5241137_2 from main Rem kmanicka 04/19/06 - Backport kmanicka_bug5139414 from main Rem kmanicka 03/08/06 - fix bug 5172534 Rem kmanicka 03/08/06 - bug 5139414 fix lock_exec in process_wait_step() Rem lsatyapr 04/04/06 - Fix bug 4884900 - add rollback in Rem execute_param_sources Rem rdabbott 04/05/06 - fix 5103480: allow multiple steps to suspend in Rem one exec Rem skini 04/13/06 - Backport skini_bug-5103480 from main Rem skini 03/09/06 - Backport skini_bug-5071610 from main Rem skini 03/02/06 - Clear sequence number on system job restart Rem kmanicka 03/09/06 - Backport kmanicka_bug-5028782 from main Rem kmanicka 03/08/06 - workaround for bug 5028782 Rem skini 12/05/05 - Finish up fix for snapshot too old errors in Rem purge Rem skini 11/30/05 - Log errors, avoid ora-1555 in purge Rem skini 12/12/05 - Backport skini_bug-4741721 from main Rem skini 09/27/05 - Account for too many rows in process_restart Rem skini 09/22/05 - Handle multiple CA execs at the same scheduled Rem time Rem skini 09/22/05 - Fix to queries that look for simultaneous CA Rem executions Rem skini 09/20/05 - Run blackout callbacks in superuser mode Rem nqureshi 09/17/05 - Update CA owner before setting overridden creds Rem nqureshi 09/16/05 - Change check_modify_ca to ignore non-existent Rem templates Rem nqureshi 09/15/05 - added overloaded get_large_param function Rem dsahrawa 09/15/05 - bug 4601560, deadock between receiver and console Rem threads Rem dsahrawa 09/13/05 - bug 4603331, handle non single target to single Rem target change in job type versions Rem skini 09/11/05 - Fix restart of itserial stepset Rem rpinnama 09/08/05 - Modify create_temp_cp_ca_from_temp_cp to accept Rem src and dest CA names Rem jaysmith 09/07/05 - add are_cas_equivalent() Rem jaysmith 09/06/05 - set owner of template copy CAs Rem pkantawa 09/08/05 - Fix 4596664: guard against primary key violations Rem jaysmith 09/02/05 - never update CA parameters Rem rdabbott 09/07/05 - fix index hint for new table Rem rdabbott 08/31/05 - fix 4477974: perf of param selects Rem skini 09/02/05 - Fix evaluateOnRetry Rem skini 09/01/05 - Compute cred info on reschedule Rem skini 09/01/05 - Fix target_creds_deleted_ca Rem jaysmith 08/31/05 - before adding cluster types, maybe add default Rem target type Rem dcawley 08/30/05 - Change api Rem skini 08/30/05 - Do not suspend jobs that have Rem suspendOnNoCreds=false Rem skini 08/29/05 - Fix single-exec mt jobs Rem dsahrawa 08/29/05 - bug 4545056, bug in stop_execution Rem dsahrawa 08/29/05 - fix 4506779, order the step siblings Rem jaysmith 08/26/05 - do not add cluster types for hidden jobtypes Rem jaysmith 08/26/05 - increment ca refcount on one-shot assocs Rem jaysmith 08/25/05 - cluster types eligible for same jobtypes as Rem members Rem skini 08/22/05 - Get rid of ca_cred_info Rem skini 08/22/05 - Fix multitask job cred info Rem dsahrawa 08/19/05 - bug 4564273, explode task targets from Rem mgmt_job_parameter Rem nqureshi 08/10/05 - Fix large param query Rem jsadras 08/07/05 - lock_cas_for_target add target_type Rem rdabbott 08/04/05 - start 4477974: rm obs code in flatten t list Rem dsahrawa 08/01/05 - bug 4482130, clone multitask job type on Rem submission from library Rem skini 08/03/05 - Cutover all calls to stop_execution to ignore Rem waiting steps Rem skini 08/02/05 - Do not update start time when resuming Rem skini 08/01/05 - Cutover other callers of stop_execution to ignore Rem waiting exceptions Rem skini 08/01/05 - Fix orphaned execs issue when wait step is Rem deleted Rem skini 08/04/05 - Remove target guid from nested job cred info Rem skini 08/04/05 - Continue broken CA changes Rem dsahrawa 07/21/05 - bug 4496616, add support for vector and large Rem parameters for tasks Rem rpinnama 07/27/05 - Delete the CA when ca_refcount is zero Rem pkantawa 07/11/05 - Fix 4348291: Add target post delete callback Rem pkantawa 07/06/05 - Join with MGMT_TARGETS in get_nested_job_targets Rem skini 07/20/05 - Change message Rem skini 07/20/05 - Code review: move var declarations from inner Rem block to outer Rem skini 07/19/05 - Code review comments Rem skini 07/15/05 - Fix valueOf for large parameters Rem jsadras 07/08/05 - trigger ca change to add out parameters Rem skini 07/12/05 - Back out changes for bug 4448574 Rem skini 07/12/05 - Change upsert_flat_targets to only insert skipped Rem for timezone_target schedules Rem rdabbott 07/08/05 - fix 3262285: add start/end time error Rem scgrover 07/07/05 - add extended sql trace Rem skini 07/06/05 - Fix restart Rem skini 07/05/05 - Do not requeue jobs when rescheduling execution Rem skini 07/04/05 - Do not reschedule param srcs on retry Rem skini 06/28/05 - Fix bug 4452460: order executions during deletion Rem skini 06/28/05 - Pull in changes from beta fix 4432894 Rem dsahrawa 06/14/05 - bug 4148622, command block rewrite Rem rzazueta 07/06/05 - Fix 4435559 Rem pkantawa 06/27/05 - use correct column names for online help Rem rpinnama 06/25/05 - Fix 4448414 : Copy target CAs properly to Rem template copyh Rem skini 06/24/05 - Fix dispatcher death Rem nqureshi 06/21/05 - adding support for task help ids Rem pkantawa 06/09/05 - Fix 4394600: handle nested jobs correctly Rem pkantawa 06/06/05 - Fix 4322378: Update step status correctly Rem jaysmith 06/17/05 - use metric_type in ca params Rem jaysmith 05/27/05 - add violation context CA parameters Rem skini 06/21/05 - Fix bug with timezone regions Rem skini 06/15/05 - Catch unhandled errors in dispatcher Rem dsahrawa 06/08/05 - bug 4170383, fix deadlocks in jobs engine Rem dsahrawa 05/27/05 - fix bug 4305261, cannot edit single target jobs Rem with one stopped execution Rem skini 06/06/05 - grabtrans 'skini_bug-4377344' Rem skini 05/26/05 - Remove orphaned wait steps Rem skini 05/23/05 - Fix bug 4317856: deadlock caused by Rem stop_execution() Rem skini 05/23/05 - Remove commits from pre-delete callback Rem skini 05/22/05 - Add autonomous txn to auto methods Rem skini 05/20/05 - Bug 4377344: autonomous txns for suspend, Rem set_set_targets Rem rdabbott 05/25/05 - skini review: keep stop job run w/ jobid sig Rem order execs to avoid deadlock Rem rdabbott 04/19/05 - Fix 4316792: stop job run Rem pkantawa 05/20/05 - Fix 4324856: check for zero length before copying Rem large params Rem dsahrawa 05/26/05 - delete wait step when we get no_data_found in Rem process_wait_step Rem jaysmith 05/19/05 - create_target_ca_from_default Rem jaysmith 05/18/05 - get_ca_id handle target-type cas Rem kmanicka 05/03/05 - bug 4301435: fix get_adjusted_start_time while submit Rem kmanicka 05/12/05 - fix drop_user_ca Rem ashugupt 05/03/05 - Fix 4322177 Rem kmanicka 05/03/05 - bug 4335226 : fix cleanup_job_queue Rem pkantawa 05/04/05 - Fix 4345254: conditionalize to account for queued Rem jobs Rem pkantawa 05/02/05 - Fix 4337833: guard against no_data_found Rem dsahrawa 04/18/05 - fix but 4307471, cannot edit suspended jobs Rem jaysmith 04/18/05 - populate ca-specific params Rem pkantawa 04/13/05 - Rem dsahrawa 04/14/05 - fix null iterate param issue, grace period checks Rem dsahrawa 04/08/05 - look up parent's iterate index in case of generated Rem iterate params, delete wait execs on reschedule etc. Rem skini 04/14/05 - Continue work on broken CAs Rem skini 04/13/05 - Broken CAs Rem skini 04/11/05 - Fix issue with delete job Rem skuchero 04/08/05 - add force_stop_all_executions for StopJobVerb Rem pkantawa 03/28/05 - Set status to reassigned instead of suspended Rem dsahrawa 04/06/05 - add process_flatten_step for use from dispatcher Rem skini 04/06/05 - Remove check for times in the past Rem skini 04/04/05 - Fix hung switch steps Rem skini 04/01/05 - Copy large parameters Rem skini 03/29/05 - Fix runs for immediate schedule Rem skini 03/27/05 - Fix default step for switch stepsets Rem dsahrawa 03/28/05 - fix group targets for nested jobs Rem pkantawa 03/24/05 - Fix 4148622: validate callback name before Rem inserting Rem dsahrawa 03/25/05 - bug 4257417, validate grace period Rem dsahrawa 03/23/05 - use iterate param index when generating flat Rem target lists Rem skini 03/16/05 - Do not suspend system jobs on blackout Rem dsahrawa 03/17/05 - add new variant of is_single_target_job_type Rem pkantawa 03/11/05 - Fix 4153421: asynchronous delete Rem kmanicka 03/07/05 - Impl IMMEDIATE Schedule Rem dsahrawa 03/08/05 - take allTargets and parent job's single-targetness into Rem account for generation of flatten steps Rem kmanicka 03/09/05 - Impl JOB_STATUS_EXPIRED Rem rkhandel 03/11/05 - Rem pkantawa 03/07/05 - Fix 4153421: add code for async delete_job Rem dsahrawa 02/08/05 - impl association parameter sources Rem skini 03/03/05 - Make user, credentials, and props sources succeed Rem if parameters null Rem skini 03/02/05 - Allow vector parameters to be passed to nested Rem jobs Rem pkantawa 03/04/05 - use constant for fileTransfer command Rem kmanicka 02/10/05 - bug 4100454 Throw proper Exception in suspend and resume ops Rem kmanicka 02/21/05 - Bug 4185833: Fix command_type of PARAMSRC_STEP Rem ashugupt 02/11/05 - command vs osscript ER Rem shianand 02/17/05 - Rem skini 02/14/05 - Expose get_current_schedule for migration Rem kmanicka 02/07/05 - Bug 4163237: catch NO_DATA_FOUND while calling get_current_schedule Rem kmanicka 01/31/05 - bug 4097359 assign Full job to new_user in reassin_user_job Rem kmanicka 01/07/05 - added function daylight_adjusted_local_time Rem kmanicka 01/07/05 - cleared dangling tzregion parameters from apis Rem kmanicka 01/07/05 - always compute timezone in schedule_execution() Rem kmanicka 01/07/05 - Fix bug in process_wait_step use target_list_index Rem shianand 01/24/05 - Audit Functionality Added Rem jaysmith 01/21/05 - change grant_ca_privs parameters Rem jaysmith 01/27/05 - fix check for null CAs Rem ramalhot 01/17/05 - g_member_guid->g_contains_guid Rem skini 01/12/05 - Fix BO job interaction Rem skini 01/06/05 - Add API for cleaning up job queues Rem jaysmith 01/06/05 - remove cas correctly at target deletion time Rem mkiran 12/30/04 - 3976094: Call reset_params before job param Rem substitution and also in an EXCEPTION block Rem mkiran 01/05/05 - 3976094: If reset_params is called in a Rem procedure, call it in its EXCEPTION block also Rem nqureshi 01/07/05 - adding show_taregt_proeprties Rem pkantawa 01/05/05 - Update grace period during schedule update Rem skini 12/21/04 - Fix ca param issue Rem skini 12/20/04 - Fix bug in resuming jobs with grace period Rem rzazueta 12/14/04 - Fix system jobs in drop_ and reassign_user_jobs Rem skini 12/09/04 - Fix grace period suspend Rem skini 12/09/04 - Fix nested job deletion Rem skini 12/08/04 - Fix versioning issue for single-execution mt jobs Rem skini 11/30/04 - Call grant_full_job for CAs Rem skini 11/29/04 - Do not allow noneditable jobs to be saved to lib Rem skini 11/23/04 - Code Review comments Rem skini 11/20/04 - Fix exact fetch error with CAs Rem dsahrawa 11/17/04 - impl of job_id,tgt_list_idx lock for skipped Rem execs Rem skini 11/17/04 - Cutover credentials_disabled column to broken Rem column Rem pkantawa 11/16/04 - Add Skipped status Rem dsahrawa 11/05/04 - fixes for grace periods Rem skini 11/10/04 - Fix scheduling issues with times in the past Rem jaysmith 11/13/04 - set notification-type on CA annotations Rem jaysmith 11/10/04 - move notification queue out of trigger for CA Rem insert Rem skini 11/01/04 - Add insert_host_cred_target_types Rem dsahrawa 10/01/04 - impl start grace periods Rem skini 10/31/04 - Allow switchVarName to be vector parameter Rem skini 10/27/04 - Versioning support, part 2 Rem skini 10/08/04 - Versioning changes Rem jaysmith 10/28/04 - set triggering_severity at insert-time Rem jaysmith 10/26/04 - single-execution job tasks can have no targets Rem set Rem jaysmith 10/22/04 - allow CAs in get_multi_task_job_info Rem dsahrawa 10/18/04 - bug fix in task override creds Rem dsahrawa 10/14/04 - add nested job target_type Rem kmanicka 10/03/04 - changing col non_restartable to restartable Rem dsahrawa 10/06/04 - add get_nested_job_targets Rem skini 10/05/04 - Ensure that the user model callback gets called Rem when nested jobs are retried Rem rpinnama 10/01/04 - Add lock_cas_for_object API Rem rpinnama 10/01/04 - Add reset_ca_ctrs and delete_noref_cas Rem dsahrawa 09/29/04 - impl nested job override creds Rem kmanicka 09/20/04 - add user model callbacks for jobs and ca Rem pshishir 09/16/04 - Template APIs added Rem skini 09/14/04 - Implement error code propogation Rem skini 09/14/04 - new type of system job assoc with session Rem skini 09/13/04 - Do not fetch secret properties Rem dsahrawa 09/16/04 - grabtrans 'pshishir_cafix' Rem dsahrawa 09/14/04 - add get_multi_task_job_info Rem pshishir 09/14/04 - Rem skini 09/03/04 - Expire stopped jobs Rem kmanicka 09/03/04 - More retry fixes Rem kmanicka 08/25/04 - retry enhancement for executing pramSrc step during job retry Rem pkantawa 09/01/04 - grabtrans 'nqureshi_schedule_page' Rem nqureshi 08/30/04 - changed target type selection query Rem ramalhot 08/24/04 - cutover to new assoc tables Rem skini 08/22/04 - Fix scheduling bug with DOW and DOM schedules Rem kmanicka 08/20/04 - added property %oms_root% bug-3354959 Rem ashugupt 08/19/04 - removing fetching credentials values from Rem get_job_credentiametadata procedure Rem skini 08/18/04 - Fix bug 3623459 Rem skini 08/12/04 - Cutover all printlns to logs Rem pkantawa 08/09/04 - grabtrans 'nqureshi_naim_parameter_2' Rem skini 08/08/04 - Fix 3779716 Rem pshishir 07/12/04 - Modified delete_job, added delete_ca and process_delete_job Rem pshishir 07/12/04 - Shifted restart_job_execution from MGMT_JOB Rem pshishir 07/12/04 - Add check_modify_ca Rem pshishir 07/07/04 - Add Corrective action APIs Rem dsahrawa 08/05/04 - Execute submit time param sources for nested jobs Rem kmanicka 08/06/04 - fixing retry with prameters Rem skini 08/03/04 - Do not retry param sources Rem ashugupt 08/02/04 - Rem skini 08/02/04 - Catch exceptions for waiting executions Rem skini 08/02/04 - Fix retry issue Rem skini 07/29/04 - Code review comments Rem skini 07/27/04 - Fix bug with stopping skipped executions Rem skini 07/26/04 - Merge in bulldozed changes for bug 3748762 Rem ashugupt 07/26/04 - new stored procs for job creds ui Rem kmanicka 07/22/04 - Added support for retry Callback and new restart APIs Rem kmanicka 07/22/04 - Added support for retry Callback and new restart APIs Rem skini 07/21/04 - Fix successCods bug Rem jvishen 07/19/04 - Fix issue with full job Rem nqureshi 07/19/04 - added procedure to be used in parameters page Rem skini 07/19/04 - Implement notification support Rem skini 07/16/04 - Job Notifications support Rem dsahrawa 07/14/04 - Rem skini 07/12/04 - Reset cache after securityInfo Rem dcawley 07/07/04 - Increase user name size Rem dsahrawa 06/08/04 - skipped executions implementation Rem rzazueta 06/08/04 - Bypass blackout check if job created the blackout Rem skini 05/19/04 - Bug 2872779: encryption for parameters specified Rem by user when overrideUser=false Rem skini 05/18/04 - Account for param source steps during failvoer Rem dsahrawa 04/19/04 - use a prop to disable deletion of system job Rem skini 04/19/04 - Fix for stepsetstatus Rem skini 04/19/04 - Handle nested job visibility in vpd Rem skini 04/20/04 - Fix broken lockInfo Rem skini 04/20/04 - Fix edits of suspended jobs, stopped jobs Rem aholser 03/19/04 - add oms name to history Rem jsadras 03/17/04 - Added force stop,Handle_user_deletion changed to Rem use force stop and not delete system jobs Rem ramalhot 03/04/04 - Add stop_all_executions_with_id(p_job_id RAW) Rem rdabbott 02/03/04 - suspend, stop pending is actually a 'running' status Rem rdabbott 02/02/04 - handle individual status case in min/max Rem rdabbott 01/30/04 - Fix 3256113: create 'active' bucket Rem dsahrawa 02/10/04 - use parent job id in move_stopped_exec_to_history Rem aholser 02/05/04 - remove jobStepCount Rem dsahrawa 01/29/04 - fix job deletion for multi target jobs Rem sgrover 12/02/03 - add hints Rem skini 12/02/03 - FIx bug 3293058: monthly schedules Rem sgrover 12/02/03 - add hints Rem dsahrawa 11/24/03 - use % to escape % in params Rem skini 11/16/03 - Overridden credentials for clusters Rem dsahrawa 11/14/03 - bug 3202830 Rem dsahrawa 11/13/03 - fix jobs srgs Rem lgloyd 11/12/03 - Rem lgloyd 11/12/03 - perf: remove user defined fn calls Rem dsahrawa 11/12/03 - bug 3003221 Rem skini 11/06/03 - Cannot suspend completed executions Rem skini 11/15/03 - Fix day-of-year schedule when start day is today Rem skini 11/13/03 - Fix 3189897 Rem dsahrawa 11/12/03 - bug 3003221 Rem skini 11/06/03 - Cannot suspend completed executions Rem dsahrawa 10/28/03 - bug 3219261 Rem rdabbott 10/17/03 - fix 3191047: delete the last run should delete job Rem dsahrawa 10/17/03 - bugs 3188944,3189225 Rem dsahrawa 10/16/03 - change for test dispatcher Rem dsahrawa 10/10/03 - bug 3178340 Rem skini 10/02/03 - Rem skini 10/01/03 - Rem skini 10/01/03 - Rem skini 09/30/03 - Queues Rem skini 09/29/03 - Remove order by from dispatcher query Rem skini 09/25/03 - Fix repos_end_time Rem skini 09/17/03 - Credentials for databases Rem skini 09/15/03 - Allow clusters Rem skini 09/13/03 - Fixes for cluster targets: bug 3063951 Rem skini 09/10/03 - Implement suspend job Rem skini 09/10/03 - Fix last run problem Rem skini 09/08/03 - timezone changes Rem skini 09/08/03 - Allow all groups Rem skini 09/08/03 - Add user deleted callbacks Rem skini 09/07/03 - Fix resume Rem skini 09/06/03 - Agent-bound job fixes Rem skini 09/05/03 - Fix schedule issues, agent-bound support Rem skini 09/03/03 - Support for groups Rem skini 09/01/03 - Fix job edit Rem skini 08/30/03 - Implement restartable, editable, suspendable Rem skini 08/28/03 - Fix stop for system jobs Rem skini 08/27/03 - Fix edit for single-target jobs Rem skini 08/26/03 - Rem skini 08/25/03 - Continue with groups impl Rem skini 08/24/03 - Support for single-target jobs Rem skini 08/22/03 - Group support Rem skini 08/22/03 - Group/property subst support Rem skini 08/21/03 - Group/property subst support Rem skini 08/20/03 - Rem skini 08/18/03 - Add setOverride Rem skini 08/18/03 - Credentials backend support Rem skini 08/15/03 - Remove RETURNING from update_stepset_refcount Rem skini 08/11/03 - Fix bug in system job retry Rem aholser 07/25/03 - remove privileges before deleting job Rem skini 07/25/03 - Rem skini 07/25/03 - Remove java procedures Rem skini 07/21/03 - Retry on known errors Rem skini 07/16/03 - Job library support Rem skini 07/08/03 - Target deletion perf fixes Rem skini 06/27/03 - Log errors Rem skini 06/26/03 - Fix resume Rem skini 06/23/03 - Sec check for edit job Rem skini 06/16/03 - get_large_param returns null when parameter cannot be found Rem skini 06/11/03 - Large parameter support Rem skini 06/08/03 - Rem skini 05/18/03 - Use scheduled time for schedules Rem skini 05/12/03 - Add edit API, security checks for new usermodel Rem skini 05/07/03 - Continue with parameter source execution changes Rem skini 04/11/03 - Execute parameter sources as steps Rem skini 03/24/03 - Clarify error message Rem skini 03/20/03 - Add validate_job_type Rem skini 02/21/03 - Change in param size to 4000 Rem dcawley 12/13/02 - Add JOB_DELETED call to MGMT_USER Rem skini 04/30/03 - Fix yearly schedule bug 2695743 Rem skini 04/21/03 - Do not heartbeat any more Rem skini 03/21/03 - Remove unwanted select for update Rem skini 01/31/03 - Fix dispatcher sql Rem skini 12/09/02 - Fix bug with years Rem skini 12/07/02 - Complete fix for scheduling bug Rem skini 12/06/02 - Fix scheduling issue with empty iterative stepsets Rem skini 11/25/02 - Rem skini 11/24/02 - Jobs and blackouts Rem skini 11/22/02 - Blackout windows Rem skini 11/26/02 - Fix exttargets trigger Rem skini 10/11/02 - Fix inner loop in user source Rem skini 10/09/02 - Continue work on 2549136 Rem skini 10/08/02 - Persistify extended target list: bug 2549136 Rem skini 10/14/02 - Add bulk update method Rem rdabbott 10/04/02 - review: dont select the exec id, use param Rem rdabbott 10/03/02 - Fix restart counting bug Rem rdabbott 10/02/02 - do not schedule restarted jobs Rem skini 10/03/02 - Fix restart bug with iterative stepsets Rem skini 09/24/02 - Fix parameter too large errors Rem skini 09/03/02 - Finish up implementing locking Rem skini 08/30/02 - implement locking Rem skini 08/28/02 - Implement suspend timeout Rem skini 08/27/02 - Implement event-based suspends Rem skini 08/21/02 - Make empty iterative stepsets succeed Rem skini 07/22/02 - Fix iterative stepset abort Rem skini 08/08/02 - Support locking output in NOWAIT mode Rem skini 07/12/02 - Change in target_name column size Rem skini 07/10/02 - Fix system jobs Rem skini 07/02/02 - Changes for deleteTarget and system jobs Rem skini 06/14/02 - continue implementing blackouts Rem rpinnama 06/08/02 - Remove reference to emd_url. Rem skini 06/12/02 - Rem skini 06/07/02 - Support for TIMEZONE_SPECIFIED Rem aholser 05/21/02 - remove dbms_output causing sporadic difs. Rem rpinnama 05/15/02 - rpinnama_reorg_rep_scripts Rem rpinnama 05/15/02 - Created rem skini 04/19/02 - Remove autonomous transaction rem skini 04/18/02 - SMP_MGMT_JOB=>MGMT_JOB rem skini 04/16/02 - Commit autonomous transaction rem aholser 04/10/02 - performance logging. rem skini 04/15/02 - Make get_output_writer autonomous rem rdabbott 04/08/02 - expose subst param rem skini 04/03/02 - Fix end time issues rem skini 03/27/02 - Change boolean to integer in calls to delete_job_execution rem skini 03/25/02 - Fix for restart rem skini 03/21/02 - Change dispatcher to get one set off steps at a time rem skini 03/15/02 - Fix 2266912: restart does not work correctly rem skini 03/14/02 - Bug 2266922: Abort execution when param source fails rem rpatti 02/25/02 - use function to get current user rem skini 02/27/02 - Implement option to nuke system jobs rem skini 02/20/02 - Fix param sources for nested jobs rem aholser 02/05/02 - fix merge error rem skini 02/05/02 - Fix bug 2206570: account for jobs within iterative stepsets rem skini 01/31/02 - history table rem skini 01/30/02 - More timestamp issues rem skini 01/28/02 - timezone support rem skini 01/24/02 - Schedules, contd rem skini 01/23/02 - Schedules, contd rem skini 01/21/02 - Implement target deletion, schedules rem skini 01/18/02 - Implement security info, purging rem skini 01/16/02 - Encryption, system jobs rem skini 01/09/02 - Fix switch stepset abort, support system jobs rem rpinnama 12/28/01 - Change constants. rem skini 12/27/01 - Implement support for container paths rem rpinnama 12/26/01 - Update update_step_status. rem edemembe 12/27/01 - Removing target name/type and metric name/column references rem skini 11/20/01 - Implement agentbound jobs rem skini 11/16/01 - Job system stability, continued rem skini 11/14/01 - Break up steps into long and short steps rem skini 11/12/01 - Support overrrideUser in parameter sources rem skini 11/01/01 - Implement restart rem skini 11/01/01 - Call reset_params rem skini 10/30/01 - Tune dispatcher query rem skini 10/26/01 - Fix job name length problem rem tjaiswal 10/25/01 - Fix exception errors rem aholser 10/16/01 - remove drops rem skini 10/18/01 - Drop credential_type column rem skini 10/16/01 - Fully support nested jobs rem skini 10/16/01 - Fix param source bug rem skini 10/05/01 - Support for parameter sources rem skini 09/20/01 - Async rem skini 09/17/01 - Change iterative stepset definition rem skini 09/14/01 - Serialize updates Rem CREATE OR REPLACE PACKAGE BODY MGMT_JOB_ENGINE AS -- Package level variables to hold all job types currently -- being deleted s_job_types SMP_EMD_STRING_ARRAY; s_major_versions SMP_EMD_INTEGER_ARRAY; --------------------------------------------------------- s_curr_params MGMT_JOB_PARAM_LIST := null; s_curr_targets MGMT_JOB_TARGET_LIST := null; s_fix_flatten_steps BOOLEAN := false; CRLF CONSTANT VARCHAR2(5) := CHR(13)||CHR(10); --heuristic to determine if a job/ca should be deleted synchronously or --asynchronously DELETE_JOB_UPPER_BOUND CONSTANT NUMBER := 100; -- internally using STEPTYPE_BOOKKEEPING=0 for special bookkeeping steps STEPTYPE_BOOKKEEPING constant NUMBER(2) := 0; -- constants for bookkeeping step BOOKKEEPING_STEP_ID constant NUMBER(2) := -2; BOOKKEEPING_STEP_NAME constant VARCHAR2(64) := 'bookkeeping_step'; -- Used with purge policy NEGATE_STRING CONSTANT VARCHAR2(5) := 'NOT'; JOB_BASED_CRIT_CLAUSE CONSTANT VARCHAR2(4000) := ' AND e.job_id $NEGATE$ IN ( SELECT job_id FROM MGMT_JOB WHERE is_library = 0 AND $FILTER$ (SELECT purge_value FROM MGMT_JOB_PURGE_VALUES WHERE policy_name = :pname AND criterion_index = :idx$INDEX$)) '; -- FORWARD DECLARATIONS FUNCTION insert_scheduled_entry(p_job_id RAW, p_execution_id RAW, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_step_name VARCHAR2, p_step_type INTEGER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_parent_step_id INTEGER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER, p_command_type NUMBER DEFAULT null) return INTEGER; FUNCTION lock_executions(p_execution_id RAW, p_nowait BOOLEAN DEFAULT FALSE) RETURN NUMBER; FUNCTION lock_job(p_job_id RAW) RETURN NUMBER; FUNCTION get_execution_status(p_job_id RAW, p_execution_id RAW, p_source_exec_id RAW, p_target_list_index NUMBER, p_start_time DATE, p_status_in NUMBER, p_current_status NUMBER) RETURN NUMBER; FUNCTION to_region(p_offset NUMBER) RETURN VARCHAR2; PROCEDURE suspend_job_execution(p_execution_id IN RAW, p_status NUMBER, p_timeout NUMBER); PROCEDURE resume_job_execution(p_execution_id IN RAW, p_status NUMBER, p_start_grace_period NUMBER, p_suspend_timeout NUMBER DEFAULT 0); PROCEDURE handle_target_params(p_target_names_param VARCHAR2, p_target_types_param VARCHAR2, p_target_names IN OUT NOCOPY MGMT_JOB_VECTOR_PARAMS, p_target_types IN OUT NOCOPY MGMT_JOB_VECTOR_PARAMS, p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_handle_clusters BOOLEAN, p_error_code INTEGER); FUNCTION fetch_job_parameters(p_job_id RAW, p_execution_id RAW, p_step_name VARCHAR2, p_step_type VARCHAR2, p_submission NUMBER, p_paramsrc_step_type NUMBER DEFAULT NULL) RETURN NUMBER; PROCEDURE insert_execution_into_queue(p_queue_name VARCHAR2, p_execution_id RAW); PROCEDURE schedule_executions_from_queue(p_queue_id RAW, p_nowait BOOLEAN); PROCEDURE remove_execution_from_queue(p_queue_id VARCHAR2, p_execution_id VARCHAR2); FUNCTION get_cred_set_metadata(p_set_name VARCHAR2, p_set_target_type VARCHAR2, p_type_name VARCHAR2, p_type_target_type VARCHAR2, p_target_type_meta_ver VARCHAR2 ) RETURN MGMT_JOB_CRED_SET_ARRAY; FUNCTION get_cred_type_metadata(p_type_name VARCHAR2, p_type_target_type VARCHAR2, p_target_type_meta_ver VARCHAR2 ) RETURN MGMT_JOB_CRED_TYPE_RECORD; FUNCTION get_start_grace_period(p_job_id RAW) RETURN MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; PROCEDURE update_step_set_status(p_stepset_id INTEGER, p_stepset_name VARCHAR2, p_last_step_status INTEGER, p_error_code NUMBER, p_error_code_category NUMBER); -- Update the status of the execution, and move it to history PROCEDURE move_execution_to_history(p_job_id RAW, p_execution_id RAW, p_status NUMBER, p_start_time DATE, p_end_time DATE, p_schedule_next BOOLEAN DEFAULT true, p_error_code NUMBER DEFAULT 0, p_error_code_category NUMBER DEFAULT STATUS_CATEGORY_APP); PROCEDURE schedule_nested_job(p_job_id RAW, p_parent_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER, p_insert_wait_step BOOLEAN DEFAULT FALSE); PROCEDURE schedule_next_job_execution(p_job_id RAW, p_execution_id RAW, p_last_start_time DATE, p_last_end_time DATE, p_status NUMBER); PROCEDURE insert_execution_params(p_job_id RAW, p_execution_id RAW, p_source_exec_id RAW, p_status NUMBER); FUNCTION compute_timezone_region(p_job_id RAW, p_target_guid RAW, p_target_list_index NUMBER, p_schedule MGMT_JOB_SCHEDULE_RECORD) RETURN VARCHAR2; PROCEDURE check_active_executions(p_job_id IN RAW); FUNCTION get_num_executions(p_job_id IN RAW) RETURN NUMBER; PROCEDURE do_async_delete_job(p_job_id IN RAW); PROCEDURE do_delete_job(p_job_id IN RAW, p_commit INTEGER DEFAULT 0); PROCEDURE do_delete_ca(p_job_id IN RAW, p_commit INTEGER DEFAULT 0); PROCEDURE process_delete_job(p_job_id RAW, p_commit INTEGER DEFAULT 0); FUNCTION has_priv_on_job_targets(p_username IN VARCHAR2, p_job_id IN RAW) RETURN NUMBER; FUNCTION has_priv_on_ca_targets(user_name_in IN VARCHAR2, ca_guid_in IN RAW) RETURN NUMBER; FUNCTION process_reassign_user_jobs(p_user_name IN VARCHAR2, p_new_user_name IN VARCHAR2, type_in IN NUMBER, isCA IN NUMBER) RETURN NUMBER; PROCEDURE find_job_type_ids(p_job_type_id IN RAW, p_job_type_id_array IN OUT MGMT_JOB_GUID_ARRAY); FUNCTION does_job_type_require_creds(p_job_type_id IN RAW, p_creds_jobtype_map IN OUT MGMT_CREDENTIAL_JOB_TYPE_MAP) RETURN NUMBER; -- END FORWARD DECLARATIONS -- Encapsulation of all MGMT_GLOBAL.SYSDATE_* calls. The -- debug version of these functions are used during testing -- and it allows setting of current time to any desired value. FUNCTION SYSDATE_UTC RETURN DATE IS BEGIN RETURN MGMT_GLOBAL.SYSDATE_UTC; END SYSDATE_UTC; FUNCTION SYSDATE_TZRGN(tzrgn_in VARCHAR2) RETURN DATE IS BEGIN RETURN MGMT_GLOBAL.SYSDATE_TZRGN(tzrgn_in); END SYSDATE_TZRGN; -- Encapsulation of all to_char(SYSTIMESTAMP, 'TZR') calls to get the -- timezone of repository. The debug version of these function is used -- during testing and it allows setting of current timezone of repository -- to any desired value. FUNCTION GET_REPOSITORY_TIMEZONE RETURN VARCHAR2 IS BEGIN RETURN to_char(SYSTIMESTAMP, 'TZR'); END GET_REPOSITORY_TIMEZONE; /** * Convert the passed number of minutes into an interval value that may be added * to a timestamp. */ FUNCTION mins_to_interval(p_mins INTEGER) RETURN NUMBER IS BEGIN RETURN p_mins/(24 * 60); END; -- Returns true if the specified status is a flavor of suspended FUNCTION is_system_suspended_status(p_status NUMBER) RETURN BOOLEAN IS BEGIN RETURN p_status=SUSPENDED_STATUS OR p_status=SUSPENDED_LOCK_STATUS OR p_status=SUSPENDED_BLACKOUT_STATUS OR p_status=SUSPENDED_EVENT_STATUS OR p_status=AGENTDOWN_STATUS OR p_status=SUSPENDED_CREDS_STATUS OR p_status=REASSIGNED_STATUS; END; -- Return the name and owner of the job PROCEDURE get_job_name_and_owner(p_job_id RAW, p_job_name_out OUT VARCHAR2, p_job_owner_out OUT VARCHAR2) IS BEGIN SELECT job_name, job_owner INTO p_job_name_out, p_job_owner_out FROM MGMT_JOB WHERE job_id=p_job_id; END; -- Return the most recent versions of the specified job type PROCEDURE get_max_versions(p_job_type VARCHAR2, p_major_version_out OUT NUMBER, p_minor_version1_out OUT NUMBER, p_minor_version2_out OUT NUMBER, p_job_type_id_out OUT RAW) IS BEGIN SELECT MAX(major_version) INTO p_major_version_out FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=p_job_type; IF p_major_version_out IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_TYPE_ERR, 'The specified job type does not exist'); END IF; SELECT minor_version1, minor_version2, job_type_id INTO p_minor_version1_out, p_minor_version2_out, p_job_type_id_out FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=p_job_type AND major_version=p_major_version_out; END; PROCEDURE get_max_versions(p_job_type VARCHAR2, p_major_version_out OUT NUMBER, p_job_type_id_out OUT RAW) IS l_minor_version1 MGMT_JOB_TYPE_INFO.minor_version1%TYPE; l_minor_version2 MGMT_JOB_TYPE_INFO.minor_version2%TYPE; BEGIN get_max_versions(p_job_type, p_major_version_out, l_minor_version1, l_minor_version2, p_job_type_id_out); END; -- Return the job type id corresponding to the most recent -- minor version for the specified job FUNCTION get_job_type_id(p_job_id RAW) RETURN RAW IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_nested MGMT_JOB.nested%TYPE; l_nested_job_type_id MGMT_JOB.nested_job_type_id%TYPE; BEGIN SELECT nested, nested_job_type_id INTO l_nested, l_nested_job_type_id FROM MGMT_JOB WHERE job_id=p_job_id; IF l_nested=1 THEN RETURN l_nested_job_type_id; END IF; SELECT job_type_id INTO l_job_type_id FROM MGMT_JOB_TYPE_MAX_VERSIONS mv, MGMT_JOB j WHERE j.job_id=p_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version; RETURN l_job_type_id; END; -- Return the job type id associated with the execution IF the -- specified job id is the parent job associated with the -- execution. If not, return the job type id of the nested job FUNCTION get_job_type_id(p_job_id RAW, p_execution_id RAW) RETURN RAW IS l_parent_job_id MGMT_JOB.job_id%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN SELECT job_id, job_type_id INTO l_parent_job_id, l_job_type_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; -- If the job id is the one associated with the parent job, then -- the job type id for the execution (or any retries from the execution) -- is fixed. IF l_parent_job_id = p_job_id THEN RETURN l_job_type_id; END IF; -- This is a nested job; return the nested job's job type id -- Note that get_job_type_id(job_id) does the right thing -- for nested jobs RETURN get_job_type_id(p_job_id); END; -- Return true if the specified jobtype is single-target FUNCTION is_single_target_job_type_id(p_job_type_id RAW) RETURN BOOLEAN IS l_single_target NUMBER; BEGIN SELECT single_target INTO l_single_target FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = p_job_type_id; RETURN (l_single_target=1); END is_single_target_job_type_id; -- Return true if the specified jobtype is single-target -- Note. we will use the latest version of the job -- TBD. in this case we should mandate that when a job -- is edited, we cannot edit whether it is single target -- or not, otherwise this will fail. FUNCTION is_single_target_job_type(p_job_type_name VARCHAR2) RETURN BOOLEAN IS l_job_major_version NUMBER; l_job_type_id RAW(16); BEGIN get_max_versions(p_job_type_name, l_job_major_version, l_job_type_id); RETURN is_single_target_job_type_id(l_job_type_id); END is_single_target_job_type; -- Return 1 if the specified jobtype is single-target -- TBD. in this case we should mandate that when a job -- is edited, we cannot edit whether it is single target -- or not, otherwise this will fail. PROCEDURE is_single_target_job_type(p_job_type_name VARCHAR2, p_single_target_out OUT NUMBER) IS l_job_major_version NUMBER; l_job_type_id RAW(16); BEGIN get_max_versions(p_job_type_name, l_job_major_version, l_job_type_id); SELECT single_target INTO p_single_target_out FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_job_type_id; END is_single_target_job_type; -- Return true if this is a single-target job FUNCTION is_single_target_job(p_job_id RAW) RETURN BOOLEAN IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id); RETURN is_single_target_job_type_id(l_job_type_id); END; -- Is this nested job a candidate for flattening of the target list? FUNCTION is_nested_flatten_candidate(p_parent_job_id RAW, p_nested_job_type VARCHAR2, p_all_targets BOOLEAN) return BOOLEAN IS BEGIN RETURN ((is_single_target_job_type_id(p_parent_job_id)) AND (is_single_target_job_type(p_nested_job_type)) AND (NOT p_all_targets)) OR (( NOT is_single_target_job_type_id(p_parent_job_id)) AND (is_single_target_job_type(p_nested_job_type)) AND (p_all_targets)) OR (( NOT is_single_target_job_type_id(p_parent_job_id)) AND (is_single_target_job_type(p_nested_job_type)) AND (NOT p_all_targets)); END; -- Return true if the job type is "restartable" -- and the job is not marked as non restartable FUNCTION is_restartable(p_job_id RAW) RETURN BOOLEAN IS l_is_restartable NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id); -- Check if the job is restartable SELECT restartable INTO l_is_restartable FROM MGMT_JOB WHERE job_id=p_job_id; IF l_is_restartable = 0 THEN RETURN FALSE; END IF; -- Now check whether the job type is restartable SELECT restartable INTO l_is_restartable FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; RETURN (l_is_restartable=1); END; -- Return true if the job type is "suspendable" FUNCTION is_suspendable(p_job_id RAW) RETURN BOOLEAN IS l_is_suspendable NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id); SELECT suspendable INTO l_is_suspendable FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; RETURN (l_is_suspendable=1); END; -- Return true if the job type is "editable" FUNCTION is_editable(p_job_id RAW) RETURN BOOLEAN IS l_is_editable NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id); SELECT editable INTO l_is_editable FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; RETURN (l_is_editable=1); END; -- Return the target guid for a single-target execution FUNCTION get_single_target_guid(p_job_id RAW, p_target_list_index NUMBER, p_active_out OUT NUMBER) RETURN RAW IS l_single_target NUMBER; l_target_guid MGMT_TARGETS.target_guid%TYPE; BEGIN -- For single-target jobs, extract the target guid that the -- job is submitted against and use that to schedule the -- execution IF is_single_target_job(p_job_id) THEN SELECT target_guid, active INTO l_target_guid, p_active_out FROM MGMT_JOB_FLAT_TARGETS WHERE job_id=p_job_id AND target_list_index=p_target_list_index; RETURN l_target_guid; ELSE RETURN NULL; END IF; END; -- Return the job id; raise an appropriate exception if it -- can't be found -- This function should be used only for jobs, and not for corrective actions FUNCTION get_job_id(p_job_name VARCHAR2, p_owner VARCHAR2, p_is_library NUMBER DEFAULT 0) RETURN RAW IS l_job_id MGMT_JOB.job_id%TYPE; l_owner MGMT_JOB.job_owner%TYPE := p_owner; BEGIN IF l_owner IS NULL THEN l_owner := MGMT_USER.get_current_em_user; END IF; SELECT job_id INTO l_job_id FROM MGMT_JOB WHERE job_name=UPPER(p_job_name) AND job_owner=UPPER(l_owner) AND is_library=p_is_library AND nested=0 AND is_corrective_action=0; RETURN l_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Job ' || p_job_name || ':' || p_job_name || ' does not exist'); END; -- Return the job id, given the execution id FUNCTION get_job_id(p_execution_id RAW) RETURN RAW IS l_job_id MGMT_JOB.job_id%TYPE; BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; RETURN l_job_id; END; -- Note. The caller should always lock the execution -- before calling this cleanup routine. PROCEDURE cleanup_execution(p_job_id RAW, p_execution_id RAW, p_delete_params BOOLEAN DEFAULT FALSE) IS BEGIN DELETE FROM MGMT_JOB_EXECUTION WHERE execution_id = p_execution_id; DELETE FROM MGMT_JOB_HISTORY WHERE execution_id = p_execution_id; IF p_delete_params THEN DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id = p_execution_id; END IF; END; -- static constructor for MGMT_JOB_SCHEDULE_RECORD FUNCTION get_job_schedule_record(p_frequency_code NUMBER, p_start_time DATE, p_end_time DATE, p_execution_hours NUMBER, p_execution_minutes NUMBER, p_interval NUMBER, p_months MGMT_JOB_INT_ARRAY, p_days MGMT_JOB_INT_ARRAY, p_timezone_info NUMBER, p_timezone_target_index NUMBER, p_timezone_offset NUMBER, p_timezone_region VARCHAR2, p_start_grace_period NUMBER DEFAULT NO_START_GRACE) RETURN MGMT_JOB_SCHEDULE_RECORD IS BEGIN return MGMT_JOB_SCHEDULE_RECORD(p_frequency_code, p_start_time, p_end_time, p_start_grace_period, p_execution_hours, p_execution_minutes, p_interval, p_months, p_days, p_timezone_info, p_timezone_target_index, p_timezone_offset, p_timezone_region); END; -- used to construct immediate schedule FUNCTION get_immediate_schedule_record RETURN MGMT_JOB_SCHEDULE_RECORD IS BEGIN return get_job_schedule_record(IMMEDIATE_FREQUENCY_CODE, null, null, 0, 0, 0, null, null, TIMEZONE_REPOSITORY, 0, 0, null); END; -- Should we allow the mgmt_job_execplan insert trigger to -- munge the entries in there? Typically, the answer is -- yes for job type registration and no for multitask -- (or regular, when it is implemented) jobtype cloning -- This is the setter. Note that this is per session. -- The default is to not munge entries. PROCEDURE fix_flatten_steps(val BOOLEAN) IS BEGIN s_fix_flatten_steps := val; END fix_flatten_steps; -- This is the getter. This is per session once again. FUNCTION fixing_flatten_steps RETURN BOOLEAN IS BEGIN RETURN s_fix_flatten_steps; END fixing_flatten_steps; -- Log an error message with the specified error code -- p_id is the ID of the job/execution being processed -- p_execution: Set to false if the current error is for -- a job PROCEDURE log_error(p_id RAW, p_error_code NUMBER, p_error_message VARCHAR2, p_execution BOOLEAN DEFAULT true) IS l_prologue VARCHAR2(100); BEGIN IF p_execution THEN l_prologue := 'Execution ID ' || RAWTOHEX(p_id); ELSE l_prologue := 'Job ID ' || RAWTOHEX(p_id); END IF; MGMT_LOG.log_error(MODULE_NAME, p_error_code, l_prologue || ' : ' || p_error_message); END; -- Forcibly abort a running or scheduled execution. Used -- when the dispatcher or update_step_status encounters -- a fatal error. PROCEDURE force_abort_execution(p_execution_id RAW, p_message VARCHAR2) IS l_count NUMBER; l_job_id MGMT_JOB.job_id%TYPE; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_start_time DATE; l_end_time DATE; l_job_step_id NUMBER; l_job_name MGMT_JOB.job_name%TYPE; BEGIN BEGIN SELECT j.job_id, start_time, timezone_region, job_name INTO l_job_id, l_start_time, l_tzregion, l_job_name FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE j.job_id=e.job_id AND execution_id=p_execution_id; UPDATE MGMT_JOB_EXECUTION SET step_status=ABORTED_STATUS WHERE execution_id=p_execution_id AND step_status NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS); -- Write an error message corresponding to the job entry -- Get the step id of the topmost step BEGIN SELECT step_id INTO l_job_step_id FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND parent_step_id=-1; EXCEPTION WHEN NO_DATA_FOUND THEN -- Can happen if we get this error on reschedule l_job_step_id := insert_scheduled_entry(l_job_id, p_execution_id, -1, -1, RESTART_MODE_FAILURE, l_job_name, STEPTYPE_JOB, null, -1, -1, SYSDATE_UTC(), l_tzregion, SCHEDULED_STATUS); UPDATE MGMT_JOB_EXECUTION SET step_status=ABORTED_STATUS WHERE step_id=l_job_step_id; END; write_step_error_message(l_job_step_id, 'Failed due to Unexpected error. No future executions will be scheduled. Details: ' || p_message); move_execution_to_history(l_job_id, p_execution_id, ABORTED_STATUS, l_start_time, MGMT_GLOBAL.SYSDATE_UTC, false); EXCEPTION -- No matter what happens, log the error. This procedure is not supposed to -- propagate any errors as it is a forced abort. WHEN OTHERS THEN log_error(p_execution_id, SQLCODE, SQLERRM || CHR(10) || DBMS_UTILITY.format_error_stack); END; END; -- This is to serve as a sink node for the internal calls we have where NOOP is -- flagged. The data to these calls is already vetted, so the reason for this -- function is to act as a forced override of the zsql tool FUNCTION cleanse(p_data VARCHAR2) RETURN VARCHAR2 IS BEGIN RETURN p_data; END; -- Substitute variables (of the form %var%) in a step parameter -- with the values of correspondingly named job parameters -- Fetch the execution parameters. Notice that we fetch all -- parameters at once since we do not expect that a typical -- execution will have more than 20-30 parameters -- If we're doing special processing for multitask jobs when -- building credential set information, p_task_name will be -- non-null. PROCEDURE fetch_curr_params(p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2) IS l_index NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN -- rda todo: perf - decrypt after the query (avoid pl/sql vs sql switches)? -- but there shouldn't be many of them, so OK for now... IF p_task_name IS NOT NULL THEN l_job_type_id := get_job_type_id(p_job_id); SELECT --+ index(MGMT_JOB_STEP_PARAMS) MGMT_JOB_PARAM_RECORD(param_name, parameter_type, MGMT_JOB_ENGINE.decrypt_scalar(encrypted, scalar_value), MGMT_JOB_ENGINE.decrypt_vector(encrypted, vector_value)) BULK COLLECT INTO s_curr_params FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=l_job_type_id AND step_name=p_task_name AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); ELSE SELECT --+ index(MGMT_JOB_PARAMETER) MGMT_JOB_PARAM_RECORD(parameter_name, parameter_type, MGMT_JOB_ENGINE.decrypt_scalar(encrypted, scalar_value), MGMT_JOB_ENGINE.decrypt_vector(encrypted, vector_value)) BULK COLLECT INTO s_curr_params FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); END IF; -- Add job system-specific scalars l_index := s_curr_params.COUNT; s_curr_params.extend(9); s_curr_params(l_index+1) := MGMT_JOB_PARAM_RECORD('job_id', PARAM_TYPE_SCALAR, cleanse(p_job_id), null); s_curr_params(l_index+2) := MGMT_JOB_PARAM_RECORD('job_execution_id', PARAM_TYPE_SCALAR, cleanse(p_execution_id), null); s_curr_params(l_index+3) := MGMT_JOB_PARAM_RECORD('job_owner', PARAM_TYPE_SCALAR, cleanse(p_job_owner), null); s_curr_params(l_index+4) := MGMT_JOB_PARAM_RECORD('job_name', PARAM_TYPE_SCALAR, cleanse(p_job_name), null); s_curr_params(l_index+5) := MGMT_JOB_PARAM_RECORD('emd_root', PARAM_TYPE_SCALAR, '%emd_root%', null); s_curr_params(l_index+6) := MGMT_JOB_PARAM_RECORD('perlbin', PARAM_TYPE_SCALAR, '%perlbin%', null); s_curr_params(l_index+7) := MGMT_JOB_PARAM_RECORD('scriptsdir', PARAM_TYPE_SCALAR, '%scriptsdir%', null); s_curr_params(l_index+8) := MGMT_JOB_PARAM_RECORD('oms_root', PARAM_TYPE_SCALAR, '%oms_root%', null); s_curr_params(l_index+9) := MGMT_JOB_PARAM_RECORD('job_default_shell', PARAM_TYPE_SCALAR, '%job_default_shell%', null); END; FUNCTION get_all_job_params(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER) RETURN MGMT_JOB_PARAM_LIST IS l_job_name VARCHAR2(256); l_job_owner VARCHAR2(256); l_iterate_param VARCHAR2(64); l_iterate_param_index NUMBER(8); l_curr_params MGMT_JOB_PARAM_LIST; l_index NUMBER; BEGIN SELECT job_name, job_owner, iterate_param, iterate_param_index INTO l_job_name, l_job_owner,l_iterate_param, l_iterate_param_index FROM MGMT_JOB j, MGMT_JOB_EXECUTION e WHERE j.job_id=e.job_id AND e.step_id=p_step_id; reset_params(); fetch_curr_params(p_job_id,p_execution_id,NULL,p_step_id, l_iterate_param,l_iterate_param_index, l_job_name,l_job_owner); l_curr_params := s_curr_params; l_index := l_curr_params.COUNT; l_curr_params.extend(3); l_curr_params(l_index+1) := MGMT_JOB_PARAM_RECORD('job_step_id', PARAM_TYPE_SCALAR, p_step_id, null); l_curr_params(l_index+2) := MGMT_JOB_PARAM_RECORD('job_iterate_param', PARAM_TYPE_SCALAR, l_iterate_param, null); l_curr_params(l_index+3) := MGMT_JOB_PARAM_RECORD('job_iterate_index', PARAM_TYPE_SCALAR, l_iterate_param_index, null); reset_params(); RETURN l_curr_params; EXCEPTION WHEN NO_DATA_FOUND THEN reset_params(); RETURN NULL; END; -- Obtain the value of the specified "target" parameter FUNCTION get_target_param_value(p_job_id RAW, p_execution_id RAW, p_param_name VARCHAR2, p_param_index NUMBER) RETURN VARCHAR2 IS l_num_targets NUMBER; l_target MGMT_JOB_TARGET_RECORD; BEGIN IF s_curr_targets IS NULL THEN SELECT count(1) INTO l_num_targets FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=p_execution_id; s_curr_targets := MGMT_JOB_TARGET_LIST(); s_curr_targets.extend(l_num_targets); END IF; IF p_param_index IS NULL OR p_param_index <= 0 THEN RETURN null; END IF; IF p_param_index > s_curr_targets.COUNT THEN RETURN null; END IF; IF s_curr_targets(p_param_index) IS NULL THEN SELECT --+ index(jt) MGMT_JOB_TARGET_RECORD(target_name, target_type) INTO l_target FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE jt.job_id=p_job_id AND jt.execution_id=p_execution_id AND jt.target_index=p_param_index AND jt.target_guid=t.target_guid; s_curr_targets(p_param_index) := l_target; END IF; IF p_param_name='job_target_names' THEN RETURN s_curr_targets(p_param_index).target_name; ELSIF p_param_name='job_target_types' THEN RETURN s_curr_targets(p_param_index).target_type; END IF; END; FUNCTION is_generated_iterate_param(p_iterate_param VARCHAR2) RETURN BOOLEAN IS BEGIN RETURN (p_iterate_param IS NOT NULL) AND ((p_iterate_param LIKE 'target\_names\_%\_________________________________' ESCAPE '\') OR (p_iterate_param LIKE 'target\_types\_%\_________________________________' ESCAPE '\')); END; -- 'Extract the iterate index from the iterate param name. -- This is used for generated iterative parallel stepsets -- that have had their target lists flattened. FUNCTION get_iterate_index_from_param(p_iterate_param VARCHAR2) RETURN NUMBER IS l_iterate_index NUMBER := 0; l_index_start NUMBER := 0; l_index_end NUMBER := 0; l_temp VARCHAR2(4000); BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('get_iterate_index_from_param:enter, iterate_param=' || p_iterate_param, MODULE_NAME); END IF; IF p_iterate_param IS NULL THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('get_iterate_index_from_param:exit, iterate_param=' || p_iterate_param || ', iterate_param was null', MODULE_NAME); END IF; RETURN l_iterate_index; END IF; -- unfortunately reg exp matching is only available starting 10g -- NOTE. Dont mess with the number of underscores here! IF NOT is_generated_iterate_param(p_iterate_param) THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('get_iterate_index_from_param:exit, iterate_param=' || p_iterate_param || ', iterate_param did not match pattern', MODULE_NAME); END IF; RETURN l_iterate_index; END IF; -- we start looking for underscores after target_names l_index_start := INSTR(p_iterate_param, '_', 1, 2); l_index_end := INSTR(p_iterate_param, '_', 1, 3); IF l_index_start > 0 AND l_index_end > 0 THEN l_temp := SUBSTR(p_iterate_param, l_index_start+1, l_index_end-l_index_start-1); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('get_iterate_index_from_param:iterate_param=' || p_iterate_param || ', extracted iterate index ' || l_temp, MODULE_NAME); END IF; l_iterate_index := TO_NUMBER(l_temp); END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('get_iterate_index_from_param:iterate_param=' || p_iterate_param || ', returning iterate index ' || l_iterate_index, MODULE_NAME); END IF; RETURN l_iterate_index; EXCEPTION WHEN OTHERS THEN RETURN 0; END; -- Obtain the scalar value for the specified parameter -- If p_task_name is not null, then we are fetching parameters -- on behalf of a task in a multi-task job. FUNCTION get_scalar_value(p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_param_name VARCHAR2, p_flattened BOOLEAN DEFAULT FALSE) RETURN VARCHAR2 IS pos NUMBER := -1; BEGIN IF s_curr_params IS NULL THEN fetch_curr_params(p_job_id, p_execution_id, p_task_name, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner); END IF; -- Special-case for some system params IF p_param_name='job_step_id' THEN RETURN p_step_id; ELSIF p_param_name='job_iterate_param' THEN RETURN p_iterate_param; ELSIF p_param_name='job_iterate_index' THEN IF p_flattened THEN RETURN p_iterate_param_index; ELSE pos := get_iterate_index_from_param(p_iterate_param); IF pos > 0 THEN RETURN pos; ELSE RETURN p_iterate_param_index; END IF; END IF; END IF; -- Search through the parameter list for this parameter FOR i IN 1..s_curr_params.COUNT LOOP IF s_curr_params(i).param_name=p_param_name THEN pos := i; EXIT; END IF; END LOOP; IF pos < 0 THEN RETURN null; END IF; IF s_curr_params(pos).param_type=PARAM_TYPE_SCALAR THEN RETURN s_curr_params(pos).scalar_value; ELSE IF s_curr_params(pos).vector_value IS NOT NULL AND s_curr_params(pos).vector_value.COUNT >0 THEN RETURN s_curr_params(pos).vector_value(1); ELSE RETURN null; END IF; END IF; END; -- Obtain the value of the specified vector parameter at the -- specified index -- If p_task_name is not null, then we are fetching parameters -- on behalf of a task in a multi-task job. FUNCTION get_vector_value(p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_param_name VARCHAR2, p_param_index NUMBER) RETURN VARCHAR2 IS pos NUMBER := -1; BEGIN IF s_curr_params IS NULL THEN fetch_curr_params(p_job_id, p_execution_id, p_task_name, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner); END IF; -- Special case for target parameters, which are always vectors IF p_param_name='job_target_names' OR p_param_name='job_target_types' THEN RETURN get_target_param_value(p_job_id, p_execution_id, p_param_name, p_param_index); END IF; -- Search through the parameter list for this parameter FOR i IN 1..s_curr_params.COUNT LOOP IF s_curr_params(i).param_name=p_param_name THEN pos := i; EXIT; END IF; END LOOP; IF pos <= 0 THEN RETURN null; END IF; IF s_curr_params(pos).param_type=PARAM_TYPE_SCALAR THEN IF p_param_index=1 THEN RETURN s_curr_params(pos).scalar_value; ELSE RETURN null; END IF; ELSE IF s_curr_params(pos).vector_value IS NOT NULL AND s_curr_params(pos).vector_value.COUNT >0 AND p_param_index IS NOT NULL AND p_param_index > 0 AND s_curr_params(pos).vector_value.COUNT >= p_param_index THEN RETURN s_curr_params(pos).vector_value(p_param_index); ELSE RETURN null; END IF; END IF; END; -- Forward declaration for extract_index() FUNCTION extract_index(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_param_value IN OUT VARCHAR2, p_vector_param IN OUT BOOLEAN, p_flattened BOOLEAN DEFAULT FALSE) RETURN NUMBER; -- Extract and return the value of a parameter from a string that is -- positioned at the start of the parameter -- Return the value of the parameter. Return the rest of the string -- in p_param_value -- If p_task_name is not null, then we are fetching parameters -- on behalf of a task in a multi-task job. FUNCTION get_subst_value(p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_param_value IN OUT VARCHAR2, p_flattened BOOLEAN DEFAULT FALSE) RETURN VARCHAR2 IS l_next_pos NUMBER; l_ret_value VARCHAR2(5000); l_vector_param BOOLEAN := false; l_param_name VARCHAR2(1000); l_index NUMBER; BEGIN -- Look for the trailing '%' l_next_pos := INSTR(p_param_value, '%'); -- This is the case when there is only one % in the whole string -- or this is the last % in the string IF l_next_pos <= 0 THEN RETURN '%'; END IF; -- This is the case when there are escaped percents IF l_next_pos = 1 THEN p_param_value := SUBSTR(p_param_value, 2); RETURN '%'; END IF; -- Extract the parameter name (between the two '%'s) l_param_name := SUBSTR(p_param_value, 1, l_next_pos-1); -- Strip it from the original p_param_value := SUBSTR(p_param_value, l_next_pos+1); -- check if the param ends with escaped "%" eg. "%param_name%%" -- this should become "%param_name%" -- and not "param_value%" where param_value may be "null" IF SUBSTR(p_param_value, 1, 1) = '%' THEN p_param_value := SUBSTR(p_param_value, 2); RETURN '%' || l_param_name || '%'; END IF; -- Now we have to check if the parameter is a vector IF SUBSTR(p_param_value, 1, 1) = '[' THEN l_vector_param := true; l_index := extract_index(p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, p_param_value, l_vector_param, p_flattened); END IF; IF l_vector_param THEN RETURN get_vector_value(p_job_id, p_execution_id, p_task_name, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_param_name, l_index); ELSE RETURN get_scalar_value(p_job_id, p_execution_id, p_task_name, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_param_name, p_flattened); END IF; END; -- Extract the index from the string that starts with the leading [ -- If the string is not well-formed ([index]), return -1 FUNCTION extract_index(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_param_value IN OUT VARCHAR2, p_vector_param IN OUT BOOLEAN, p_flattened BOOLEAN DEFAULT FALSE) RETURN NUMBER IS l_end_pos NUMBER := 1; l_index_str VARCHAR2(500); l_temp_str VARCHAR2(500); l_index_value VARCHAR2(200); l_ret_value NUMBER; BEGIN l_end_pos := INSTR(p_param_value, ']'); IF l_end_pos <= 0 THEN p_vector_param := false; RETURN -1; END IF; l_index_str := SUBSTR(p_param_value, 2, l_end_pos-2); p_param_value := SUBSTR(p_param_value, l_end_pos+1); -- See whether the index is a constant or is itself a number IF SUBSTR(l_index_str, 1, 1) = '%' THEN -- This is a reference to a parameter l_temp_str := SUBSTR(l_index_str, 2); l_index_value := get_subst_value(p_job_id, p_execution_id, null, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_temp_str, p_flattened); ELSE -- This must be a constant l_index_value := l_index_str; END IF; -- Make sure the parameter is properly formatted BEGIN IF l_index_value IS NOT NULL THEN l_ret_value := TO_NUMBER(l_index_value); RETURN l_ret_value; ELSE -- we were able to extract some value from th index p_vector_param := false; p_param_value := '[' || l_index_value || ']' || p_param_value; RETURN -1; END IF; EXCEPTION -- This is a badly formatted number WHEN OTHERS THEN p_vector_param := false; p_param_value := '[' || l_index_str || ']' || p_param_value; RETURN -1; END; END; PROCEDURE reset_params IS BEGIN s_curr_params := null; s_curr_targets := null; END; -- Substitute parameter values -- If p_task_name is not null, then we are fetching parameters -- on behalf of a task in a multi-task job. FUNCTION substitute_params(p_job_id VARCHAR2, p_execution_id VARCHAR2, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_param_value VARCHAR2, p_flattened BOOLEAN DEFAULT FALSE, p_task_name VARCHAR2 DEFAULT NULL) RETURN VARCHAR2 IS l_curr_pos INTEGER := 1; l_param_value VARCHAR2(5000) := p_param_value; l_ret_value VARCHAR2(5000) := ''; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('substitute_params:enter, job_id=' || p_job_id || ',exec_id=' || p_execution_id || ',step_id=' || p_step_id || ',iterate_param=' || p_iterate_param || ',iterate_param_index=' || p_iterate_param_index || ',job=' || p_job_name || ',owner=' || p_job_owner || ',param_value=' || p_param_value, MODULE_NAME); END IF; WHILE l_curr_pos > 0 LOOP -- Find start of the current parameter l_curr_pos := INSTR(l_param_value, '%'); IF l_curr_pos > 0 THEN -- Append everything upto the '%' to l_ret_value l_ret_value := l_ret_value || SUBSTR(l_param_value, 1, l_curr_pos-1); -- remove it from the original l_param_value := SUBSTR(l_param_value, l_curr_pos+1); -- do param substitution l_ret_value := l_ret_value || get_subst_value(p_job_id, p_execution_id, p_task_name, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_param_value, p_flattened); END IF; END LOOP; -- Append the remaining part of the string to ret_value l_ret_value := l_ret_value || l_param_value; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('substitute_params:exit, job_id=' || p_job_id || ',exec_id=' || p_execution_id || ',step_id=' || p_step_id || ',iterate_param=' || p_iterate_param || ',iterate_param_index=' || p_iterate_param_index || ',job=' || p_job_name || ',owner=' || p_job_owner || ',param_value=' || p_param_value || ', returning ' || l_ret_value, MODULE_NAME); END IF; RETURN l_ret_value; END; -- Return True(1) if the specified integer is present in the specified -- comma-separated integer list pattern -- Return true if p_num_str matches p_num FUNCTION num_match(p_num_str VARCHAR2, p_num NUMBER) RETURN NUMBER IS l_num_str VARCHAR2(1000) := TRIM(p_num_str); BEGIN -- Special-case for '*' IF l_num_str='*' THEN RETURN 1; END IF; IF p_num=TO_NUMBER(l_num_str) THEN RETURN 1; ELSE RETURN 0; END IF; EXCEPTION -- Invalid number, return false WHEN OTHERS THEN RETURN 0; END; -- Return True(1) if the specified integer is present in the specified -- comma-separated integer list pattern FUNCTION match_number(p_pattern VARCHAR2, p_num NUMBER) RETURN NUMBER IS l_comma_pos NUMBER := 1; l_pattern VARCHAR2(5000) := p_pattern; l_curr_number_str VARCHAR2(5000); BEGIN -- Extract each element in the comma-separated list WHILE l_comma_pos > 0 LOOP l_comma_pos := INSTR(l_pattern, ','); IF l_comma_pos > 0 THEN l_curr_number_str := SUBSTR(l_pattern, 1, l_comma_pos-1); l_pattern := SUBSTR(l_pattern, l_comma_pos+1); IF num_match(l_curr_number_str, p_num)=1 THEN RETURN 1; END IF; END IF; END LOOP; IF l_pattern IS NOT NULL THEN RETURN num_match(l_pattern, p_num); ELSE RETURN 0; END IF; END; -- Decrypt a scalar value, if necessary FUNCTION decrypt_scalar(p_is_encrypted INTEGER, p_scalar_value VARCHAR2) RETURN VARCHAR2 IS BEGIN IF p_is_encrypted=1 AND p_scalar_value IS NOT NULL THEN RETURN decrypt(p_scalar_value); ELSE RETURN p_scalar_value; END IF; END; -- Decrypt a vector value, if necessary FUNCTION decrypt_vector(p_is_encrypted INTEGER, p_vector_value MGMT_JOB_VECTOR_PARAMS) RETURN MGMT_JOB_VECTOR_PARAMS IS l_vector_value MGMT_JOB_VECTOR_PARAMS; BEGIN IF p_is_encrypted=1 AND p_vector_value IS NOT NULL THEN l_vector_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_value.extend(p_vector_value.count); FOR i IN 1..l_vector_value.count LOOP IF p_vector_value(i) IS NOT NULL THEN l_vector_value(i) := decrypt(p_vector_value(i)); END IF; END LOOP; RETURN l_vector_value; ELSE RETURN p_vector_value; END IF; END; -- Encrypt a vector value, if necessary FUNCTION encrypt_vector(p_encrypt INTEGER, p_vector_value MGMT_JOB_VECTOR_PARAMS) RETURN MGMT_JOB_VECTOR_PARAMS IS l_vector_value MGMT_JOB_VECTOR_PARAMS; BEGIN IF p_encrypt=1 AND p_vector_value IS NOT NULL THEN l_vector_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_value.extend(p_vector_value.count); FOR i IN 1..p_vector_value.count LOOP IF p_vector_value(i) IS NOT NULL THEN l_vector_value(i) := encrypt(p_vector_value(i)); END IF; END LOOP; RETURN l_vector_value; ELSE RETURN p_vector_value; END IF; END; -- Encrypt a scalar value, if necessary FUNCTION encrypt_scalar(p_encrypt INTEGER, p_scalar_value VARCHAR2) RETURN VARCHAR2 IS BEGIN IF p_encrypt=1 AND p_scalar_value IS NOT NULL THEN RETURN encrypt(p_scalar_value); ELSE RETURN p_scalar_value; END IF; END; -- Make a copy of the large parameters in the job specified by -- p_source_job_id to the job specified by p_job_id PROCEDURE copy_large_params(p_job_id IN RAW, p_source_job_id IN RAW) IS l_dest_large_value_id RAW(16); l_dest_large_value_clob CLOB; BEGIN FOR crec IN (SELECT parameter_name, param_value FROM MGMT_JOB_PARAMETER p, MGMT_JOB_LARGE_PARAMS lp WHERE job_id=p_source_job_id AND execution_id=NO_EXECUTION AND parameter_type=PARAM_TYPE_LARGE AND p.large_value=lp.param_id) LOOP BEGIN SELECT param_value INTO l_dest_large_value_clob FROM MGMT_JOB_PARAMETER p, MGMT_JOB_LARGE_PARAMS lp WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND parameter_name=crec.parameter_name AND parameter_type=PARAM_TYPE_LARGE AND p.large_value=lp.param_id; EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO MGMT_JOB_PARAMETER(job_id, execution_id, parameter_name, parameter_type, large_value) VALUES (p_job_id, NO_EXECUTION, crec.parameter_name, PARAM_TYPE_LARGE, SYS_GUID()) RETURNING large_value INTO l_dest_large_value_id; INSERT INTO MGMT_JOB_LARGE_PARAMS(param_id, param_value) VALUES (l_dest_large_value_id, empty_clob()) RETURNING param_value INTO l_dest_large_value_clob; END; IF DBMS_LOB.getlength(crec.param_value) > 0 THEN DBMS_LOB.copy(l_dest_large_value_clob, crec.param_value, DBMS_LOB.getlength(crec.param_value)); END IF; END LOOP; END; -- Update the reference count for a just-scheduled stepset PROCEDURE update_stepset_refcount(p_stepset_id NUMBER) IS l_num_children NUMBER; l_num_children_done NUMBER; l_stepset_type MGMT_JOB_EXECUTION.step_type%TYPE; l_stepset_name MGMT_JOB_EXECUTION.step_name%TYPE; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('update_stepset_refcount:enter stepset_id=' || p_stepset_id, MODULE_NAME); END IF; SELECT NVL(SUM(decode(step_status, COMPLETED_STATUS, 1, ABORTED_STATUS, 1, FAILED_STATUS, 1, 0)), 0), COUNT(*) INTO l_num_children_done, l_num_children FROM MGMT_JOB_EXECUTION WHERE parent_step_id=p_stepset_id; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('update_stepset_refcount: stepset_id=' || p_stepset_id || ',num_children=' || l_num_children || ',num_children_done=' || l_num_children_done, MODULE_NAME); END IF; UPDATE MGMT_JOB_EXECUTION SET num_children=l_num_children, num_children_completed=l_num_children_done WHERE step_id=p_stepset_id; BEGIN SELECT step_name, step_type INTO l_stepset_name, l_stepset_type FROM MGMT_JOB_EXECUTION WHERE step_id=p_stepset_id; EXCEPTION WHEN NO_DATA_FOUND THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('update_stepset_refcount: stepset_id=' || p_stepset_id || ', child steps all done, ignoring no_data_found', MODULE_NAME); END IF; RETURN; END; -- If all scheduled children are done AND this is a parallel -- or iterative parallel stepset, then compute the status of -- the stepset as well IF l_stepset_type=STEPTYPE_PARALLEL_STEPSET OR l_stepset_type=STEPTYPE_ITPLL_STEPSET THEN -- Note: the last argument has no meaning for parallel -- and iterative parallel stepsets IF l_num_children=l_num_children_done THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('update_stepset_refcount: stepset_id=' || p_stepset_id || ', in an iterative setpset and all children are done' , MODULE_NAME); END IF; update_step_set_status(p_stepset_id, l_stepset_name, COMPLETED_STATUS, 0, STATUS_CATEGORY_APP); END IF; END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('update_stepset_refcount:exit stepset_id=' || p_stepset_id, MODULE_NAME); END IF; END; -- Insert a new scheduled entry for a step in a job FUNCTION insert_scheduled_entry(p_job_id RAW, p_execution_id RAW, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_step_name VARCHAR2, p_step_type INTEGER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_parent_step_id INTEGER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER, p_command_type NUMBER DEFAULT null) return INTEGER IS new_step_id INTEGER; l_num_children INTEGER; BEGIN INSERT INTO MGMT_JOB_EXECUTION (job_id, execution_id, step_id, source_step_id, original_step_id, restart_mode, step_name, step_type, command_type, iterate_param, iterate_param_index, parent_step_id, step_status, start_time, timezone_region) VALUES (p_job_id, p_execution_id, MGMT_JOB_SEQUENCE.NEXTVAL, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, p_step_type, p_command_type, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_update_status, p_start_time, p_tzregion) RETURNING step_id into new_step_id; -- Update the num_children count of the parent step set IF p_parent_step_id != -1 AND p_step_type NOT IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC, STEPTYPE_START_WAIT_STEP, STEPTYPE_GRACE_WAIT_STEP, STEPTYPE_FLATTEN_TARGETS_STEP) THEN SELECT num_children INTO l_num_children FROM MGMT_JOB_EXECUTION WHERE step_id=p_parent_step_id; l_num_children := l_num_children+1; UPDATE MGMT_JOB_EXECUTION SET num_children=l_num_children WHERE step_id=p_parent_step_id; END IF; return new_step_id; END; -- Schedule a serial stepset PROCEDURE schedule_serial_stepset(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) IS l_initial_stepname VARCHAR2(64); l_initial_steptype NUMBER(2); l_step_id INTEGER; BEGIN -- Figure out the first step to schedule: there should be exactly -- one step that has a SERIAL_BEGIN incoming edge from this stepset SELECT step_name, step_type INTO l_initial_stepname, l_initial_steptype FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND incoming_edge_type = ETYPE_SERIAL_BEGIN AND origin_step_name = p_step_name; -- Insert an entry for this stepset l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, STEPTYPE_SERIAL_STEPSET, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, p_update_status); schedule(p_job_id, p_execution_id, l_step_id, p_source_step_id, p_original_step_id, p_restart_mode, p_job_type_id, l_initial_stepname, l_initial_steptype, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); update_stepset_refcount(l_step_id); END; -- Fetch the default step for the switch stepset PROCEDURE get_switch_default_step(p_job_type_id RAW, p_step_name VARCHAR2, p_initial_step_name_out OUT VARCHAR2, p_initial_step_type_out OUT VARCHAR2) IS BEGIN SELECT step_name, step_type INTO p_initial_step_name_out, p_initial_step_type_out FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND origin_step_name = p_step_name AND incoming_edge_type = ETYPE_SWITCH_BEGIN AND switch_case_val = DEFAULT_SWITCH_VALUE; EXCEPTION WHEN NO_DATA_FOUND THEN p_initial_step_name_out := null; p_initial_step_type_out := null; END; -- Abort the specified stepset PROCEDURE abort_stepset(p_stepset_id NUMBER, p_message VARCHAR2) IS BEGIN update_step_status(p_stepset_id, ABORTED_STATUS, 0, STATUS_CATEGORY_APP, false, true); update_stepset_refcount(p_stepset_id); END; -- Schedule a switch stepset PROCEDURE schedule_switch_stepset(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) IS l_switch_var_name MGMT_JOB_EXECPLAN.switch_var_name%TYPE; l_switch_var_index MGMT_JOB_EXECPLAN.switch_var_index%TYPE; l_switch_var_value MGMT_JOB_EXECPLAN.switch_case_val%TYPE; l_param_type MGMT_JOB_PARAMETER.parameter_type%TYPE; l_scalar_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_initial_step_name MGMT_JOB_EXECPLAN.step_name%TYPE; l_initial_step_type MGMT_JOB_EXECPLAN.step_type%TYPE; l_stepset_id MGMT_JOB_EXECUTION.step_id%TYPE; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_vector_values MGMT_JOB_VECTOR_PARAMS; BEGIN -- Insert an entry for this stepset l_stepset_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, STEPTYPE_SWITCH_STEPSET, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, p_update_status); -- Figure out the step/stepset/job that needs to be scheduled. -- Get the switch variable name. SELECT switch_var_name, switch_var_index INTO l_switch_var_name, l_switch_var_index FROM MGMT_JOB_EXECPLAN WHERE job_type_id = p_job_type_id AND step_name = p_step_name; -- The switch var index may be an indirection IF l_switch_var_index IS NOT NULL THEN get_job_name_and_owner(p_job_id, l_job_name, l_job_owner); l_switch_var_index := substitute_params( p_job_id, p_execution_id, -1, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, l_switch_var_index); reset_params; END IF; -- Get the value for the switch variable name BEGIN SELECT parameter_type, decrypt_scalar(encrypted, scalar_value), decrypt_vector(encrypted, vector_value) INTO l_param_type, l_scalar_value, l_vector_values FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=l_switch_var_name AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); IF l_switch_var_index IS NOT NULL THEN IF l_param_type=PARAM_TYPE_VECTOR THEN IF l_vector_values IS NOT NULL AND l_switch_var_index <= l_vector_values.COUNT THEN l_switch_var_value := l_vector_values(l_switch_var_index); ELSE abort_stepset(l_stepset_id, 'Incorrectly specified switchVarIndex ' || l_switch_var_index); END IF; ELSE abort_stepset(l_stepset_id, 'Incorrectly specified switchVarName ' || l_switch_var_name); END IF; ELSE IF l_param_type=PARAM_TYPE_SCALAR THEN l_switch_var_value := l_scalar_value; ELSIF l_param_type=PARAM_TYPE_VECTOR AND l_vector_values IS NOT NULL AND l_vector_values.COUNT >= 1 THEN l_switch_var_value := l_vector_values(1); ELSE abort_stepset(l_stepset_id, 'Incorrectly specified switchVarName ' || l_switch_var_name); END IF; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- See if there is a default step get_switch_default_step(p_job_type_id, p_step_name, l_initial_step_name, l_initial_step_type); IF l_initial_step_name IS NULL THEN -- There is no scalar value for the switch variable -- being used in the switch stepset. Abort the stepset write_step_error_message(l_stepset_id, 'No scalar value available for switchVarName ' || l_switch_var_name); update_stepset_refcount(l_stepset_id); update_step_status(l_stepset_id, COMPLETED_STATUS, 0, STATUS_CATEGORY_APP); RETURN; END IF; END; -- Get the step that needs to be executed. -- one step that has a SERIAL_BEGIN incoming edge from this stepset -- with case value == switch_var_value IF l_switch_var_value IS NOT NULL THEN BEGIN SELECT step_name, step_type INTO l_initial_step_name, l_initial_step_type FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND origin_step_name = p_step_name AND incoming_edge_type = ETYPE_SWITCH_BEGIN AND switch_case_val = l_switch_var_value; EXCEPTION WHEN NO_DATA_FOUND THEN -- See if there is a default step; if so, execute that get_switch_default_step(p_job_type_id, p_step_name, l_initial_step_name, l_initial_step_type); -- Abort the stepset if no case value is selected. -- There is no scalar value for the switch variable -- being used in the switch stepset. IF l_initial_step_name IS NULL THEN write_step_error_message(l_stepset_id, 'Switch Stepset: no step in the switch stepset ''' || p_step_name || ''' matches the switchVarName ''' || l_switch_var_name || ''' with value ''' || l_switch_var_value || ''''); update_stepset_refcount(l_stepset_id); update_step_status(l_stepset_id, COMPLETED_STATUS, 0, STATUS_CATEGORY_APP); RETURN; END IF; END; END IF; -- Schedule the selected entity schedule(p_job_id, p_execution_id, l_stepset_id, p_source_step_id, p_original_step_id, p_restart_mode, p_job_type_id, l_initial_step_name, l_initial_step_type, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); update_stepset_refcount(l_stepset_id); EXCEPTION WHEN OTHERS THEN -- Bug 6855856: Call reset_params before abort_stepset reset_params(); abort_stepset(l_stepset_id, SQLERRM); END; -- Schedule a parallel stepset PROCEDURE schedule_parallel_stepset(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) IS l_step_id INTEGER; BEGIN -- Insert an entry for this stepset l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, STEPTYPE_PARALLEL_STEPSET, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, p_update_status); -- Schedule all steps that have a PARALLEL_BEGIN edge from this stepset FOR crec in (SELECT step_name, step_type FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND incoming_edge_type = ETYPE_PARALLEL_BEGIN AND origin_step_name = p_step_name) LOOP schedule(p_job_id, p_execution_id, l_step_id, p_source_step_id, p_original_step_id, p_restart_mode, p_job_type_id, crec.step_name, crec.step_type, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); END LOOP; update_stepset_refcount(l_step_id); END; FUNCTION int_array_to_varchar(p_int_array SMP_EMD_INTEGER_ARRAY) RETURN VARCHAR2 IS l_buffer VARCHAR2(4000); BEGIN IF p_int_array IS NULL or p_int_array.count = 0 THEN RETURN l_buffer; END IF; l_buffer := '('; FOR i in 1..p_int_array.count LOOP l_buffer := l_buffer || p_int_array(i); IF i != p_int_array.count THEN l_buffer := l_buffer || ','; END IF; END LOOP; l_buffer := l_buffer || ')'; RETURN l_buffer; END; -- Return the set of indexes of the child stepset that must -- be scheduled. FUNCTION fetch_iterative_param_indexes(p_job_id RAW, p_execution_id RAW, p_iterate_param VARCHAR2, p_iterate_param_filter VARCHAR2, p_schedule_parallel BOOLEAN, p_start_index NUMBER) RETURN SMP_EMD_INTEGER_ARRAY IS l_param_values MGMT_JOB_VECTOR_PARAMS; l_param_count INTEGER; l_param_indexes SMP_EMD_INTEGER_ARRAY := SMP_EMD_INTEGER_ARRAY(); l_serial_param_index SMP_EMD_INTEGER_ARRAY; l_parameter_type MGMT_JOB_PARAMETER.parameter_type%TYPE; l_target_list_index INTEGER; l_temp VARCHAR2(10); BEGIN IF EMDW_LOG.p_is_debug_set THEN IF p_schedule_parallel THEN l_temp := 'true'; ELSE l_temp := 'false'; END IF; EMDW_LOG.debug('fetch_iterative_param_indexes:enter job_id=' || p_job_id || ',exec_id=' || p_execution_id || ',iterate_param=' || p_iterate_param || ',iterate_filter=' || p_iterate_param_filter || ',sched_parallel=' || l_temp || ',start_index=' || p_start_index, MODULE_NAME); END IF; SELECT target_list_index INTO l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; -- Handle the target name and type params specially IF p_iterate_param = PARAM_TARGET_NAMES OR p_iterate_param = PARAM_TARGET_TYPES THEN IF p_iterate_param_filter IS NULL THEN SELECT target_index BULK COLLECT INTO l_param_indexes FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index ORDER BY target_index; ELSE IF p_iterate_param = PARAM_TARGET_NAMES THEN SELECT target_index BULK COLLECT INTO l_param_indexes FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index AND jt.target_guid=t.target_guid AND target_name like p_iterate_param_filter ORDER BY target_index; ELSE SELECT target_index BULK COLLECT INTO l_param_indexes FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index AND jt.target_guid=t.target_guid AND target_type like p_iterate_param_filter ORDER BY target_index; END IF; END IF; ELSE BEGIN SELECT parameter_type, decrypt_vector(encrypted, vector_value) INTO l_parameter_type, l_param_values FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=p_iterate_param AND parameter_type=PARAM_TYPE_VECTOR; EXCEPTION WHEN NO_DATA_FOUND THEN -- It is possible that they did not provide the parameter. -- Null the array, which will abort the stepset l_param_values := null; END; IF l_param_values is null OR l_param_values.count = 0 THEN -- Return an empty array, since nothing is to be scheduled IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('fetch_iterative_param_indexes: param_values is null, returning empty array', MODULE_NAME); END IF; RETURN SMP_EMD_INTEGER_ARRAY(); END IF; IF p_iterate_param_filter IS NULL THEN l_param_indexes.extend(l_param_values.count); FOR i in 1..l_param_values.count LOOP l_param_indexes(i) := i; END LOOP; ELSE l_param_count := 0; FOR i IN 1..l_param_values.count LOOP IF p_iterate_param_filter = l_param_values(i) THEN l_param_count := l_param_count+1; l_param_indexes.extend(1); l_param_indexes(l_param_count) := i; END IF; END LOOP; END IF; END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('fetch_iterative_param_indexes: param indexes is ' || int_array_to_varchar(l_param_indexes), MODULE_NAME); END IF; IF p_schedule_parallel THEN -- All parameters are scheduled in parallel IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('fetch_iterative_param_indexes: schedule_parallel, returning param_indexes', MODULE_NAME); END IF; RETURN l_param_indexes; END IF; -- If this is a iterativeSerial stepset, return the first -- index that is greater than or equal to startIndex l_serial_param_index := SMP_EMD_INTEGER_ARRAY(); FOR i in 1..l_param_indexes.count LOOP IF l_param_indexes(i) >= p_start_index THEN l_serial_param_index.extend(1); l_serial_param_index(1) := l_param_indexes(i); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('fetch_iterative_param_indexes: iterativeSerial stepset, serial param indexes is ' || int_array_to_varchar(l_serial_param_index) || ' returning first index that is greater than ' || p_start_index, MODULE_NAME); END IF; RETURN l_serial_param_index; END IF; END LOOP; -- If we're here, we did not find any parameter to schedule -- Return an empty array IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('fetch_iterative_param_indexes: no parameter found, returning empty array', MODULE_NAME); END IF; RETURN l_serial_param_index; END; -- Schedule an iterative parallel (parent) stepset -- p_schedule_parallel is set to true for parallel -- iterative stepsets; p_start_index is the index -- that was last scheduled. p_start_index will always -- be 1 for parallel iterative stepsets and for the -- first scheduling of a serial iterative stepset. It -- will have ever-increasing values for subsequent -- schedulings of serial iterative stepsets. -- This returns the number of childstepsets that were -- actually scheduled FUNCTION schedule_iterative_stepset(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_schedule_parallel BOOLEAN, p_start_index NUMBER, p_update_status NUMBER) RETURN INTEGER IS l_iterative_stepset_id INTEGER := 0; l_param_indexes SMP_EMD_INTEGER_ARRAY; l_origin_step_type MGMT_JOB_EXECPLAN.origin_step_type%TYPE; l_iterate_param MGMT_JOB_EXECPLAN.iterate_param%TYPE; l_iterate_param_filter MGMT_JOB_EXECPLAN.iterate_param_filter%TYPE; l_stepset_type INTEGER; BEGIN IF p_schedule_parallel THEN l_stepset_type := STEPTYPE_ITPLL_STEPSET; ELSE l_stepset_type := STEPTYPE_ITSERIAL_STEPSET; END IF; IF p_start_index = 1 THEN -- We are scheduling the iterative stepset for the first time. -- Note that the incoming iterate_param is ignored: if it is -- non-null, it belongs to an enclosing iterative stepset. An -- iterative stepset overrides the iterative param that -- might have been passed in via an enclosing iterative stepset. -- Insert an entry for the parent stepset, computing the param -- to iterate over from the execution plan. l_iterative_stepset_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, l_stepset_type, null, -1, p_parent_step_id, p_start_time, p_tzregion, p_update_status); ELSE l_iterative_stepset_id := p_parent_step_id; END IF; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling iterative stepset, index ' || p_start_index, MODULE_NAME); END IF; SELECT origin_step_type, iterate_param, iterate_param_filter INTO l_origin_step_type, l_iterate_param, l_iterate_param_filter FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=l_stepset_type; IF l_origin_step_type = STEPTYPE_FLATTEN_TARGETS_STEP THEN IF p_iterate_param_index = -1 THEN l_iterate_param := 'target_names_1_' || l_iterate_param; ELSE l_iterate_param := 'target_names_' || p_iterate_param_index || '_' || l_iterate_param; END IF; END IF; -- Fetch the parameter in question l_param_indexes := fetch_iterative_param_indexes(p_job_id, p_execution_id, l_iterate_param, l_iterate_param_filter, p_schedule_parallel, p_start_index); -- If no values were returned, end the stepset if -- This is a parallel iterative stepset OR -- This is the first index in a serial iterative stepset IF l_param_indexes is null OR l_param_indexes.count = 0 THEN IF p_schedule_parallel OR p_start_index = 1 THEN -- No param values were found; this stepset is considered -- to have completed by default write_step_output(l_iterative_stepset_id, 'Stepset did not have any qualifying steps'); update_stepset_refcount(l_iterative_stepset_id); update_step_status(l_iterative_stepset_id, COMPLETED_STATUS, 0, STATUS_CATEGORY_APP, false, true); END IF; RETURN 0; END IF; -- Schedule each individual stepset in parallel; note that in the -- case of serial iterative stepsets, there will only be one -- stepset to schedule. IF l_param_indexes IS NOT NULL AND l_param_indexes.COUNT > 0 THEN FOR i in 1..l_param_indexes.count LOOP IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling serial stepset ' || p_step_name || ', iterate param index is ' || l_param_indexes(i), MODULE_NAME); END IF; -- The child entry for an iterative stepset is a serial -- stepset that contains all the steps of the iterative -- stepset. This is scheduled in parallel against each target schedule(p_job_id, p_execution_id, l_iterative_stepset_id, p_source_step_id, p_original_step_id, p_restart_mode, p_job_type_id, p_step_name, STEPTYPE_SERIAL_STEPSET, l_iterate_param, l_param_indexes(i), p_start_time, p_tzregion, p_update_status); END LOOP; END IF; update_stepset_refcount(l_iterative_stepset_id); RETURN l_param_indexes.count; END; -- Compute the targets for the step PROCEDURE insert_step_targets(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_step_name VARCHAR2, p_job_type_id RAW, p_command_name VARCHAR2) IS l_target_name_params SMP_EMD_STRING_ARRAY; l_target_type_params SMP_EMD_STRING_ARRAY; l_targets MGMT_JOB_TARGET_LIST; BEGIN -- Special-case for the fileTransfer command IF p_command_name=COMMAND_FILE_TRANSFER THEN l_target_name_params := SMP_EMD_STRING_ARRAY(); l_target_name_params.extend(2); l_target_name_params := SMP_EMD_STRING_ARRAY(); l_target_name_params.extend(2); SELECT scalar_value BULK COLLECT INTO l_target_name_params FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND parameter_type=1 AND param_name IN ('sourceTargetName', 'destTargetName') ORDER by param_name; SELECT scalar_value BULK COLLECT INTO l_target_type_params FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND parameter_type=1 AND param_name IN ('sourceTargetType', 'destTargetType') ORDER by param_name; ELSE -- Look for potential target name parameters SELECT scalar_value BULK COLLECT INTO l_target_name_params FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND parameter_type=1 AND param_name like '%argetName%'; SELECT scalar_value BULK COLLECT INTO l_target_type_params FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND parameter_type=1 AND param_name like '%argetType'; IF l_target_name_params IS NULL OR l_target_type_params IS NULL THEN RETURN; END IF; IF l_target_name_params.COUNT != l_target_type_params.COUNT OR l_target_type_params.COUNT > 1 THEN RETURN; END IF; END IF; -- Compute the actual names and types l_targets := MGMT_JOB_TARGET_LIST(); l_targets.extend(l_target_name_params.COUNT); FOR i IN 1..l_targets.COUNT LOOP l_targets(i) := MGMT_JOB_TARGET_RECORD(null, null); l_targets(i).target_name := substitute_params( p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_target_name_params(i)); l_targets(i).target_type := substitute_params( p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_target_type_params(i)); END LOOP; set_step_targets(p_step_id, l_targets); reset_params(); -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Schedule a param source step PROCEDURE schedule_param_src(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER, p_step_type NUMBER) IS l_step_id NUMBER := 0; l_command_type NUMBER; l_system_job MGMT_JOB.system_job%TYPE; BEGIN BEGIN -- Obtain the command type of the step with which the paramsrc step -- is associated SELECT cmd.command_type INTO l_command_type FROM MGMT_JOB_EXECPLAN execplan, MGMT_JOB_COMMAND cmd WHERE execplan.job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=STEPTYPE_STEP AND execplan.command_name=cmd.command_name; EXCEPTION WHEN NO_DATA_FOUND THEN -- we can get a NO_DATA_FOUND if the paramsrc step is associated with -- with a stepset SELECT system_job INTO l_system_job FROM MGMT_JOB where job_id=p_job_id; IF l_system_job = USER_JOB THEN l_command_type := SHORT_RUNNING_COMMAND; ELSE l_command_type := SYSTEM_COMMAND; END IF; END; l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, p_update_status, l_command_type); END; -- Schedule a step PROCEDURE schedule_step(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) IS l_step_id INTEGER; l_command_name VARCHAR2(32); l_command_type NUMBER; l_system_job NUMBER; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; BEGIN -- Obtain the command name and type SELECT cmd.command_name, cmd.command_type INTO l_command_name, l_command_type FROM MGMT_JOB_EXECPLAN execplan, MGMT_JOB_COMMAND cmd WHERE execplan.job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=STEPTYPE_STEP AND execplan.command_name=cmd.command_name; -- If the job is a system job, it overrides the command type SELECT system_job, job_name, job_owner INTO l_system_job, l_job_name, l_job_owner FROM MGMT_JOB WHERE job_id=p_job_id; IF l_system_job > 0 THEN l_command_type := SYSTEM_COMMAND; END IF; -- Insert an entry to schedule this step and we're done! l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, STEPTYPE_STEP, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, p_update_status, l_command_type); -- Insert the step targets. insert_step_targets(p_job_id, p_execution_id, l_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, p_step_name, p_job_type_id, l_command_name); END; -- Extract nested job targets PROCEDURE extract_nested_job_targets(p_parent_job_type_id RAW, p_step_name VARCHAR2, p_target_names MGMT_JOB_VECTOR_PARAMS, p_target_types MGMT_JOB_VECTOR_PARAMS) IS BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('extract_nested_job_targets:enter,parent_job_type_id=' || p_parent_job_type_id || ',nested_job=' || p_step_name, MODULE_NAME); END IF; IF p_target_names IS NULL OR p_target_types IS NULL THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('extract_nested_job_targets:exit normal,parent_job_type_id=' || p_parent_job_type_id || ',nested_job=' || p_step_name || ', no targets to add', MODULE_NAME); END IF; RETURN; END IF; IF p_target_names.COUNT != p_target_types.COUNT THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'extracting nested job targets and found target names and types were of different sizes'); END IF; FOR idx in 1..p_target_names.COUNT LOOP IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('extract_nested_job_targets:,parent_job_type_id=' || p_parent_job_type_id || ',nested_job=' || p_step_name || ',inserting target ' || p_target_names(idx) || ':' || p_target_types(idx), MODULE_NAME); END IF; INSERT INTO MGMT_NESTED_JOB_TARGETS (job_type_id, step_name, step_type, target_name, target_type) VALUES (p_parent_job_type_id, p_step_name, STEPTYPE_JOB, p_target_names(idx), p_target_types(idx)); END LOOP; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('extract_nested_job_targets:exit normal,parent_job_type_id=' || p_parent_job_type_id || ',nested_job=' || p_step_name, MODULE_NAME); END IF; END; -- Insert targets for a nested job PROCEDURE insert_nested_job_targets(p_job_id RAW, p_parent_job_id RAW, p_execution_id RAW, p_target_list_index NUMBER, p_step_id NUMBER, p_parent_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER) IS l_flattened_targets MGMT_JOB_EXECPLAN.flattened_targets%TYPE; l_temp VARCHAR2(128); l_target_name MGMT_TARGETS.target_name%TYPE; l_target_type MGMT_TARGETS.target_type%TYPE; n NUMBER := 1; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_iterate_param_index NUMBER := 0; l_flattened BOOLEAN := TRUE; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('insert_nested_job_targets:enter,job_id=' || p_job_id || ',parent_job_id=' || p_parent_job_id || ',exec_id=' || p_execution_id, MODULE_NAME); END IF; get_job_name_and_owner(p_job_id, l_job_name, l_job_owner); SELECT flattened_targets INTO l_flattened_targets FROM MGMT_JOB_EXECPLAN WHERE job_type_id = p_parent_job_type_id AND step_name = p_step_name AND step_type = STEPTYPE_JOB; IF l_flattened_targets = 0 THEN FOR crec IN (SELECT target_name, target_type FROM MGMT_NESTED_JOB_TARGETS WHERE job_type_id=p_parent_job_type_id AND step_name=p_step_name AND step_type=STEPTYPE_JOB) LOOP -- Note that we use the parent job's id while substituting -- since we substitute params/targets from the parent job to -- the nested job l_target_name := substitute_params(p_parent_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, crec.target_name); l_target_type := substitute_params(p_parent_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, crec.target_type); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('insert_nested_job_targets:regular nested job target, job_id=' || p_job_id || ',parent_job_id=' || p_parent_job_id || ',exec_id=' || p_execution_id || ',inserting target=' || l_target_name || ',type=' || l_target_type, MODULE_NAME); END IF; INSERT INTO MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) SELECT p_job_id, p_execution_id, p_target_list_index, target_guid, n FROM MGMT_TARGETS WHERE target_name=l_target_name AND target_type=l_target_type; n := n+1; END LOOP; ELSE -- flatten step FOR crec IN (SELECT target_name, target_type FROM MGMT_NESTED_JOB_TARGETS WHERE job_type_id=p_parent_job_type_id AND step_name=p_step_name AND step_type=STEPTYPE_FLATTEN_TARGETS_STEP) LOOP -- Note that we use the parent job's id while substituting -- since we substitute params/targets from the parent job to -- the nested job l_iterate_param_index := get_iterate_index_from_param(p_iterate_param); IF l_iterate_param_index < 1 THEN l_iterate_param_index := p_iterate_param_index; l_flattened := FALSE; END IF; l_temp := '%target_names_' || l_iterate_param_index || '_' || crec.target_name || '%[%job_iterate_index%]'; l_target_name := substitute_params(p_parent_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, l_temp, l_flattened); l_temp := '%target_types_' || l_iterate_param_index || '_' || crec.target_name || '%[%job_iterate_index%]'; l_target_type := substitute_params(p_parent_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, l_temp, l_flattened); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('insert_nested_job_targets:flattened nested job targets, job_id=' || p_job_id || ',parent_job_id=' || p_parent_job_id || ',exec_id=' || p_execution_id || ',inserting target=' || l_target_name || ',type=' || l_target_type, MODULE_NAME); END IF; INSERT INTO MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) SELECT p_job_id, p_execution_id, p_target_list_index, target_guid, n FROM MGMT_TARGETS WHERE target_name=l_target_name AND target_type=l_target_type; n := n+1; END LOOP; END IF; -- We're done, so reset parameters for this session reset_params(); -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Resolve targets for a nested job PROCEDURE resolve_nested_job_targets(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER, p_job_type_id RAW, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_target_guids_out OUT MGMT_JOB_GUID_LIST) IS l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_target_name MGMT_TARGETS.target_name%TYPE; l_target_type MGMT_TARGETS.target_type%TYPE; l_target MGMT_JOB_TARGET_TYPE; l_target_guid MGMT_TARGETS.target_guid%TYPE; l_count NUMBER := 1; BEGIN p_target_guids_out := MGMT_JOB_GUID_LIST(); get_job_name_and_owner(p_job_id, l_job_name, l_job_owner); FOR crec IN (SELECT target_name, target_type FROM MGMT_NESTED_JOB_TARGETS WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=STEPTYPE_JOB) LOOP l_target_name := substitute_params(p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, crec.target_name); l_target_type := substitute_params(p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, crec.target_type); SELECT target_guid INTO l_target_guid FROM MGMT_TARGETS WHERE target_name = l_target_name AND target_type = l_target_type; p_target_guids_out.extend(1); p_target_guids_out(l_count) := MGMT_JOB_TARGET_TYPE(l_target_guid); l_count := l_count + 1; END LOOP; reset_params(); EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Substitute job parameter values for placeholders of the form %foo% -- in all the step params. All parameters are assumed to be unencrypted. PROCEDURE substitute_param_values(p_job_id VARCHAR2, p_execution_id VARCHAR2, p_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_step_params IN OUT NOCOPY MGMT_JOB_PARAM_LIST) IS l_vector_params MGMT_JOB_VECTOR_PARAMS; l_new_value MGMT_JOB_PARAMETER.scalar_value%TYPE; BEGIN -- It is important to call reset_params BEFORE each set of step params is -- processed; otherwise because of caching, there could be inconsistencies reset_params(); -- Loop through the params FOR i IN 1..p_step_params.count LOOP -- If the parameter is scalar, then substitute the values IF p_step_params(i).param_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_LARGE) AND INSTR(p_step_params(i).scalar_value, '%') > 0 THEN p_step_params(i).scalar_value := substitute_params(p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, p_step_params(i).scalar_value); ELSIF p_step_params(i).param_type=PARAM_TYPE_VECTOR THEN -- This is a vector parameter. -- Check whether the vector_value_source_param is set -- We will need to substitute for placeholders in *each* -- value in the vector param l_vector_params := p_step_params(i).vector_value; FOR j IN 1..l_vector_params.count LOOP IF INSTR(l_vector_params(j), '%') > 0 THEN l_vector_params(j) := substitute_params(p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, p_job_name, p_job_owner, l_vector_params(j)); END IF; END LOOP; p_step_params(i).vector_value := l_vector_params; END IF; END LOOP; -- It is important to call reset_params AFTER each set of step params is -- processed; otherwise because of caching, there could be inconsistencies reset_params(); -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Return the parameters for the specified step, properly substituted -- and decrypted. If p_all_params is 1, all parameters of the job -- are returned as correspondingly named step parameters -- If the parameter p_fill_encrypt_info is true, then the IN OUT -- parameter p_encrypt_info is filled out with the encryption information -- for each parameter: -- if 1, the parameter was originally encrypted; if 0, the parameter -- was not originally encrypted FUNCTION get_job_step_params(p_job_id RAW, p_execution_id RAW, p_step_name VARCHAR2, p_step_id NUMBER, p_job_type_id RAW, p_all_params NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_fill_encrypt_info BOOLEAN, p_encrypt_info IN OUT NOCOPY MGMT_JOB_INT_ARRAY) RETURN MGMT_JOB_PARAM_LIST IS l_step_params MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); n INTEGER := 1; l_scalar_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_vector_value MGMT_JOB_PARAMETER.vector_value%TYPE; l_parameter_type MGMT_JOB_PARAMETER.parameter_type%TYPE; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_encrypted MGMT_JOB_PARAMETER.encrypted%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; BEGIN get_job_name_and_owner(p_job_id, l_job_name, l_job_owner); SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=p_job_type_id; IF p_all_params=1 THEN -- We do not copy over large parameters to a step FOR crec in (SELECT parameter_name, parameter_type, encrypted, scalar_value, vector_value FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR)) LOOP l_scalar_value := decrypt_scalar(crec.encrypted, crec.scalar_value); l_vector_value := decrypt_vector(crec.encrypted, crec.vector_value); l_step_params.extend(1); l_step_params(n) := MGMT_JOB_PARAM_RECORD(crec.parameter_name, crec.parameter_type, l_scalar_value, l_vector_value); IF p_fill_encrypt_info THEN p_encrypt_info.extend(1); p_encrypt_info(n) := crec.encrypted; END IF; n := n+1; END LOOP; ELSE -- We have to decrypt each parameter, and then substitute it FOR crec in (SELECT param_name, parameter_type, encrypted, scalar_value, vector_value, value_of FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_job_type_id AND step_name=p_step_name) LOOP IF crec.value_of IS NOT NULL THEN BEGIN SELECT parameter_type, encrypted, scalar_value, vector_value INTO l_parameter_type, l_encrypted, l_scalar_value, l_vector_value FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=crec.value_of; IF l_encrypted = 1 THEN l_scalar_value := decrypt_scalar(l_encrypted, l_scalar_value); l_vector_value := decrypt_vector(l_encrypted, l_vector_value); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN l_scalar_value := NULL; l_vector_value := NULL; l_encrypted := 0; END; ELSE l_scalar_value := crec.scalar_value; l_vector_value := crec.vector_value; l_encrypted := crec.encrypted; l_parameter_type := crec.parameter_type; IF l_parameter_type = -1 THEN IF l_vector_value IS NOT NULL THEN l_parameter_type := PARAM_TYPE_VECTOR; ELSE l_parameter_type := PARAM_TYPE_SCALAR; END IF; END IF; END IF; IF (l_scalar_value IS NOT NULL OR l_vector_value IS NOT NULL) THEN l_step_params.extend(1); l_step_params(n) := MGMT_JOB_PARAM_RECORD(crec.param_name, l_parameter_type, l_scalar_value, l_vector_value); IF p_fill_encrypt_info THEN p_encrypt_info.extend(1); p_encrypt_info(n) := l_encrypted; END IF; n := n+1; END IF; END LOOP; -- These step params may have placeholders. -- For multitask jobs, do not do any substitution, -- since the parameters stored in the job step -- params table are meant to be passed verbatim to -- the task IF l_job_type_category!=JOBTYPE_CATEGORY_HIDDEN THEN substitute_param_values(p_job_id, p_execution_id, p_step_id, p_iterate_param, p_iterate_param_index, l_job_name, l_job_owner, l_step_params); END IF; END IF; return l_step_params; END; -- Externally callable version FUNCTION get_job_step_params(p_job_id RAW, p_execution_id RAW, p_step_name VARCHAR2, p_step_id NUMBER, p_all_params NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_fill_encrypt_info BOOLEAN, p_encrypt_info IN OUT NOCOPY MGMT_JOB_INT_ARRAY) RETURN MGMT_JOB_PARAM_LIST IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id, p_execution_id); RETURN get_job_step_params(p_job_id, p_execution_id, p_step_name, p_step_id, l_job_type_id, p_all_params, p_iterate_param, p_iterate_param_index, p_fill_encrypt_info, p_encrypt_info); END; -- Update the values of job parameters. All incoming parameter -- values are assumed to be unencrypted. -- If p_encrypt_info is non-null, then store each parameter -- encrypted/unencrypted, as directed by p_encrypt_info(i). -- If p_store_encrypted is true, then it overrides p_encrypt_info, and -- all are encrypted before storing. If false, then all parameters -- are stored unencrypted. -- If p_insert_only is set to true, then do nothing if the parameter -- is already present. If p_insert_only is true and the parameter -- is directed to be encrypted (either because p_encrypt_info(i) is -- true or p_store_encrypted is true) and the parameter already exists, -- then the parameter is encrypted if it is not already encrypted. -- If p_recompute_ext_tgt_list is true, the extended target list of the job -- is recomputed -- If p_at_submit is true (1), then consider the parameters to have -- been specified by the user PROCEDURE update_job_parameters(p_job_id RAW, p_execution_id RAW, p_param_list MGMT_JOB_PARAM_LIST, p_encrypt_info MGMT_JOB_INT_ARRAY, p_store_encrypted BOOLEAN DEFAULT false, p_insert_only BOOLEAN DEFAULT false, p_recompute_ext_tgt_list BOOLEAN DEFAULT false, p_at_submit NUMBER DEFAULT 0) IS l_execution_id RAW(16); l_encrypt INTEGER; l_scalar_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_vector_value MGMT_JOB_PARAMETER.vector_value%TYPE; l_insert_only BOOLEAN := p_insert_only; l_created_at_submit MGMT_JOB_PARAMETER.created_at_submit%TYPE; BEGIN IF p_execution_id IS NULL THEN l_execution_id := NO_EXECUTION; ELSE l_execution_id := p_execution_id; END IF; -- Attempt to update each parameter, translate to an -- insert if we can't update FOR i IN 1..p_param_list.count LOOP BEGIN IF p_param_list(i).param_type != PARAM_TYPE_LARGE THEN IF p_store_encrypted THEN l_encrypt := 1; ELSIF p_encrypt_info IS NOT NULL AND p_encrypt_info(i)=1 THEN l_encrypt := 1; ELSE l_encrypt := 0; END IF; l_scalar_value := encrypt_scalar(l_encrypt, p_param_list(i).scalar_value); l_vector_value := encrypt_vector(l_encrypt, p_param_list(i).vector_value); ELSE l_scalar_value := NULL; l_vector_value := NULL; END IF; INSERT INTO MGMT_JOB_PARAMETER ( job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, created_at_submit) VALUES (p_job_id, p_execution_id, p_param_list(i).param_name, p_param_list(i).param_type, l_encrypt, l_scalar_value, l_vector_value, p_at_submit); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN -- Large parameters cannot be updated here: ignore them IF p_param_list(i).param_type != PARAM_TYPE_LARGE THEN IF p_insert_only THEN SELECT created_at_submit INTO l_created_at_submit FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=l_execution_id AND parameter_name=p_param_list(i).param_name; IF l_created_at_submit=1 THEN l_insert_only := true; ELSE l_insert_only := false; END IF; ELSE l_insert_only := false; END IF; IF l_insert_only THEN -- Encrypt the parameter if specified; otherwise, do nothing IF l_encrypt=1 THEN UPDATE MGMT_JOB_PARAMETER SET scalar_value=encrypt_scalar(decode(encrypted, 1, 0, 1), scalar_value), vector_value=encrypt_vector(decode(encrypted, 1, 0, 1), vector_value), encrypted=1, created_at_submit=p_at_submit WHERE job_id=p_job_id AND execution_id=l_execution_id AND parameter_name=p_param_list(i).param_name AND encrypted=0; END IF; ELSE -- Note: if the parameter is currently encrypted, -- ensure that it stays encrypted IF l_encrypt=0 THEN SELECT encrypted INTO l_encrypt FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=p_param_list(i).param_name ; END IF; UPDATE MGMT_JOB_PARAMETER SET parameter_type=p_param_list(i).param_type, encrypted=l_encrypt, scalar_value=encrypt_scalar(l_encrypt, p_param_list(i).scalar_value), vector_value=encrypt_vector(l_encrypt, p_param_list(i).vector_value), created_at_submit=p_at_submit WHERE job_id=p_job_id AND execution_id=l_execution_id AND parameter_name=p_param_list(i).param_name ; END IF; END IF; END; END LOOP; IF p_recompute_ext_tgt_list THEN compute_extended_target_list(p_execution_id); END IF; END; -- Like update_job_parameters, above, but no encrypt_info PROCEDURE update_job_parameters(p_job_id RAW, p_execution_id RAW, p_param_list MGMT_JOB_PARAM_LIST, p_store_encrypted BOOLEAN DEFAULT false, p_insert_only BOOLEAN DEFAULT false, p_recompute_ext_list BOOLEAN DEFAULT false, p_at_submit NUMBER DEFAULT 0) IS BEGIN update_job_parameters(p_job_id, p_execution_id, p_param_list, null, p_store_encrypted, p_insert_only, p_recompute_ext_list, p_at_submit); END; -- The autonomous txn version PROCEDURE update_job_parameters_auto(p_job_id RAW, p_execution_id RAW, p_param_list MGMT_JOB_PARAM_LIST, p_encrypt_info MGMT_JOB_INT_ARRAY) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN update_job_parameters(p_job_id, p_execution_id, p_param_list, p_encrypt_info); COMMIT; END; -- pre-process the target list for a single target job PROCEDURE validate_single_target_list(p_job_id VARCHAR2, p_job_type_id RAW, p_target_type VARCHAR2, p_default_target_type VARCHAR2, p_targets MGMT_JOB_TARGET_LIST) IS l_job_target_type MGMT_TARGETS.target_type%TYPE := p_target_type; l_valid_target_types NUMBER; l_target_guids MGMT_JOB_GUID_ARRAY; l_target_types SMP_EMD_STRING_ARRAY; l_allowed_target_types SMP_EMD_STRING_ARRAY; l_index NUMBER := 0; l_cluster_target_types SMP_EMD_STRING_ARRAY; l_valid BOOLEAN; BEGIN DELETE FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=NO_EXECUTION; -- Figure out what the target type of the job is going to be IF l_job_target_type IS NULL THEN SELECT single_target_type BULK COLLECT INTO l_target_types FROM MGMT_JOB_SINGLE_TARGET_TYPES WHERE job_type_id=p_job_type_id; IF l_target_types.count = 1 THEN IF l_target_types(1) != ALL_TARGET_TYPES THEN l_job_target_type := l_target_types(1); END IF; END IF; -- If the job type operates upon multiple target types, -- see if a "default" target type has been defined IF l_job_target_type IS NULL THEN l_job_target_type := p_default_target_type; END IF; -- If the target type is still null, this means no -- default was specified and none was passed in -- with the job IF l_job_target_type IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Missing target type for single-target job'); END IF; ELSE -- Ensure that this is an expected target type for the job SELECT COUNT(1) INTO l_valid_target_types FROM MGMT_JOB_SINGLE_TARGET_TYPES WHERE job_type_id=p_job_type_id AND (single_target_type=l_job_target_type OR single_target_type=ALL_TARGET_TYPES); IF l_valid_target_types=0 THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Invalid target type for single-target job'); END IF; END IF; -- Update the target type of the job UPDATE MGMT_JOB SET target_type=l_job_target_type WHERE job_id=p_job_id; -- Obtain the group target types SELECT target_type BULK COLLECT INTO l_allowed_target_types FROM MGMT_TYPE_PROPERTIES WHERE (property_name=MGMT_GLOBAL.G_IS_GROUP_PROP AND property_value='1') OR (property_name=MGMT_GLOBAL.G_IS_COMPOSITE_PROP AND property_value='1'); -- Add the job target type to the list of allowable types l_allowed_target_types.extend(1); l_allowed_target_types(l_allowed_target_types.COUNT) := l_job_target_type; -- Obtain the cluster type of the specified target type. -- Targets of the cluster type can also be submitted SELECT target_type BULK COLLECT INTO l_cluster_target_types FROM MGMT_TYPE_PROPERTIES WHERE property_name=MGMT_GLOBAL.G_CLUSTER_MEMBER_TYPE_PROP AND property_value=l_job_target_type; IF l_cluster_target_types IS NOT NULL AND l_cluster_target_types.COUNT > 0 THEN FOR i IN 1..l_cluster_target_types.COUNT LOOP l_allowed_target_types.extend(1); l_allowed_target_types(l_allowed_target_types.COUNT) := l_cluster_target_types(i); END LOOP; END IF; -- Loop through the targets, ensure they're either groups -- or of the right type FOR i IN 1..p_targets.COUNT LOOP l_valid := false; FOR j IN 1..l_allowed_target_types.COUNT LOOP IF p_targets(i).target_type = l_allowed_target_types(j) THEN l_valid := true; EXIT; END IF; END LOOP; IF NOT l_valid THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGETS_ERR, 'Target ' || p_targets(i).target_name || ':' || p_targets(i).target_type || ' is not of the expected type'); END IF; END LOOP; END; -- Process and insert the target list PROCEDURE insert_target_list(p_job_id RAW, p_targets MGMT_JOB_TARGET_LIST, p_target_list_index NUMBER) IS l_invalid_target_count NUMBER := 0; l_invalid_target_names MGMT_JOB_PARAMETER.scalar_value%TYPE; BEGIN DELETE FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND target_list_index=p_target_list_index; FOR j in 1..p_targets.count LOOP INSERT INTO MGMT_JOB_TARGET (job_id, execution_id, target_list_index, target_guid, target_index) SELECT p_job_id, NO_EXECUTION, p_target_list_index, target_guid, j FROM MGMT_TARGETS WHERE target_name=p_targets(j).target_name AND target_type=p_targets(j).target_type; IF SQL%ROWCOUNT = 0 THEN -- The target was not found l_invalid_target_count := l_invalid_target_count + 1; IF l_invalid_target_count = 1 THEN l_invalid_target_names := l_invalid_target_names || p_targets(j).target_name; ELSE l_invalid_target_names := l_invalid_target_names || ',' || p_targets(j).target_name; END IF; END IF; END LOOP; IF l_invalid_target_count > 0 THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGETS_ERR, 'The following targets are invalid: ' || l_invalid_target_names); END IF; END; -- Insert job targets for the specified job/execution. -- The target list replaces any existing targets PROCEDURE update_job_targets(p_job_id RAW, p_target_list_index NUMBER, p_targets MGMT_JOB_TARGET_LIST, p_target_type VARCHAR2) IS l_invalid_target_count NUMBER := 0; l_single_target_job MGMT_JOB_TYPE_INFO.single_target%TYPE; l_default_target_type MGMT_JOB_TYPE_INFO.default_target_type%TYPE; l_target_list_index NUMBER := p_target_list_index; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id); BEGIN SELECT single_target, default_target_type INTO l_single_target_job, l_default_target_type FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_TYPE_ERR, 'Cannot find job type for job ' || p_job_id); END; IF l_single_target_job=1 THEN validate_single_target_list(p_job_id, l_job_type_id, p_target_type, l_default_target_type, p_targets); l_target_list_index := 0; END IF; insert_target_list(p_job_id, p_targets, l_target_list_index); END; -- Update the targets for the execution PROCEDURE update_job_exec_targets(p_job_id RAW, p_execution_id RAW) IS l_target_list_index NUMBER; BEGIN DELETE FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=p_execution_id; SELECT target_list_index INTO l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; -- Copy targets over from the job definition INSERT INTO MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) SELECT job_id, p_execution_id, target_list_index, target_guid, target_index FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND target_list_index=l_target_list_index; END; -- Update the job schedule. PROCEDURE update_job_schedule(p_job_id RAW, p_schedule MGMT_JOB_SCHEDULE_RECORD) IS l_schedule_id MGMT_JOB_SCHEDULE.schedule_id%TYPE; BEGIN DELETE FROM MGMT_JOB_SCHEDULE WHERE schedule_id=(SELECT schedule_id FROM MGMT_JOB WHERE job_id=p_job_id); -- Insert the new schedule INSERT INTO MGMT_JOB_SCHEDULE(frequency_code, start_time, start_grace_period, end_time, execution_hours, execution_minutes, interval, months, days, timezone_info, timezone_target_index, timezone_offset, timezone_region) VALUES (p_schedule.frequency_code, p_schedule.start_time, p_schedule.start_grace_period, p_schedule.end_time, p_schedule.execution_hours, p_schedule.execution_minutes, p_schedule.interval, p_schedule.months, p_schedule.days, p_schedule.timezone_info, p_schedule.timezone_target_index, p_schedule.timezone_offset, p_schedule.timezone_region) RETURNING schedule_id INTO l_schedule_id; UPDATE MGMT_JOB SET schedule_id=l_schedule_id WHERE job_id=p_job_id; END; -- Copy a job entry from the source to the destination. What's copied -- are the master job entry, the job parameters, and the targets. PROCEDURE copy_job_entry(p_source_job_id RAW, p_dest_job_id RAW, p_dest_execution_id RAW, p_parent_job_id RAW) IS l_outer_job_id MGMT_JOB.job_id%TYPE; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('IN copy_job_entry(): source ' || p_source_job_id || ' destination ' || p_dest_job_id, MODULE_NAME); END IF; -- Copy the job id INSERT INTO MGMT_JOB (job_id, job_name, job_owner, job_description, job_type, job_type_major_version, target_type, nested, nested_job_type_id, parent_job_id, system_job, is_corrective_action) SELECT p_dest_job_id, job_name, job_owner, job_description, job_type, job_type_major_version, target_type, 1, nested_job_type_id, p_parent_job_id, system_job, is_corrective_action FROM MGMT_JOB WHERE job_id=p_source_job_id; -- Copy job parameters INSERT INTO MGMT_JOB_PARAMETER (job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit) SELECT p_dest_job_id, p_dest_execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit FROM MGMT_JOB_PARAMETER WHERE job_id=p_source_job_id; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Copied ' || SQL%ROWCOUNT || ' parameters', MODULE_NAME); END IF; -- Copy job targets INSERT INTO MGMT_JOB_TARGET (job_id, execution_id, target_list_index, target_guid, target_index) SELECT p_dest_job_id, p_dest_execution_id, 1, target_guid, target_index FROM MGMT_JOB_TARGET WHERE job_id=p_source_job_id; SELECT job_id INTO l_outer_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_dest_execution_id; MGMT_USER.nested_job_added(l_outer_job_id, p_dest_job_id); END; -- Resolve and insert credential values for nested jobs PROCEDURE resolve_nested_job_creds(p_parent_job_id RAW, p_execution_id RAW, p_new_job_id RAW, p_original_step_id NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_job_step_name VARCHAR2, p_job_owner VARCHAR2, p_parent_job_type_id RAW) IS l_target_name MGMT_NESTED_JOB_CRED_INFO.target_name%TYPE; l_target_type MGMT_NESTED_JOB_CRED_INFO.target_type%TYPE; l_credential_set_name MGMT_NESTED_JOB_CRED_INFO.credential_set_name%TYPE; l_container_location MGMT_NESTED_JOB_CRED_INFO.container_location%TYPE; l_credentials MGMT_JOB_CRED_ARRAY := MGMT_JOB_CRED_ARRAY(); l_cred_index INTEGER := 1; l_cred_rows MGMT_CRED_ROW_ARRAY; BEGIN FOR creds_cur IN (SELECT * FROM MGMT_NESTED_JOB_CRED_INFO WHERE job_type_id=p_parent_job_type_id AND nested_job_name=p_job_step_name) LOOP l_target_name := substitute_params(p_parent_job_id, p_execution_id, p_original_step_id, p_iterate_param, p_iterate_param_index, p_job_step_name, p_job_owner, creds_cur.target_name); IF l_target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME THEN l_target_name := null; END IF; l_target_type := substitute_params(p_parent_job_id, p_execution_id, p_original_step_id, p_iterate_param, p_iterate_param_index, p_job_step_name, p_job_owner, creds_cur.target_type); l_credential_set_name := substitute_params(p_parent_job_id, p_execution_id, p_original_step_id, p_iterate_param, p_iterate_param_index, p_job_step_name, p_job_owner, creds_cur.credential_set_name); l_container_location := substitute_params(p_parent_job_id, p_execution_id, p_original_step_id, p_iterate_param, p_iterate_param_index, p_job_step_name, p_job_owner, creds_cur.container_location); SELECT MGMT_CRED_ROW_RECORD(credential_set_column, decode(credential_value, null, null, substitute_params(p_parent_job_id, p_execution_id, p_original_step_id, p_iterate_param, p_iterate_param_index, p_job_step_name, p_job_owner, decrypt(credential_value)))) BULK COLLECT INTO l_cred_rows FROM MGMT_CREDENTIALS2 WHERE credential_guid=creds_cur.credential_guid; l_credentials.extend(1); l_credentials(l_cred_index) := MGMT_JOB_CRED_RECORD.NEW(l_target_name, l_target_type, l_container_location, MGMT_CRED_RECORD.NEW(p_job_owner, l_credential_set_name, l_cred_rows)); l_cred_index := l_cred_index + 1; END LOOP; reset_params; IF l_credentials.count > 0 THEN -- Note. is_library is 0 by default for the newly inserted job MGMT_CREDENTIAL.set_job_credentials(p_new_job_id, l_credentials); END IF; END; -- Insert entries for a nested job FUNCTION insert_nested_job(p_parent_job_id RAW, p_execution_id RAW, p_parent_job_type_id RAW, p_job_step_name VARCHAR2, p_parent_step_id INTEGER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_source_step_id NUMBER, p_original_step_id NUMBER) RETURN RAW IS l_new_job_id RAW(16); l_target_type MGMT_JOB_EXECPLAN.nested_job_target_type%TYPE; l_nested_job_type MGMT_JOB_EXECPLAN.nested_job_type%TYPE; l_all_params MGMT_JOB_EXECPLAN.all_params%TYPE; l_all_targets MGMT_JOB_EXECPLAN.all_targets%TYPE; l_flattened_targets MGMT_JOB_EXECPLAN.flattened_targets%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_job_owner VARCHAR2(256); l_parent_target_list_index INTEGER; l_system_job NUMBER; l_encrypt_info MGMT_JOB_INT_ARRAY := MGMT_JOB_INT_ARRAY(); l_source_job_id MGMT_JOB.job_id%TYPE; l_outer_job_id MGMT_JOB.job_id%TYPE; l_nested_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_nested_job_major_version MGMT_JOB_TYPE_INFO.major_version%TYPE; l_parent_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_restart_mode MGMT_JOB_EXECPLAN.restart_mode%TYPE; l_params SMP_EMD_STRING_ARRAY := null; l_valueof_params SMP_EMD_STRING_ARRAY := null; l_large_value MGMT_JOB_PARAMETER.large_value%TYPE; l_large_params SMP_EMD_STRING_ARRAY := null; l_large_values MGMT_JOB_GUID_ARRAY; l_parameter_type MGMT_JOB_PARAMETER.parameter_type%TYPE; l_is_corrective_action MGMT_JOB.is_corrective_action%TYPE; BEGIN -- If this nested job entry is a "restart always" step, -- reinsert everything from scratch. Otherwise, -- Copy the source entry if this was a restart IF p_source_step_id > 0 THEN l_parent_job_type_id := get_job_type_id(p_parent_job_id, p_execution_id); SELECT restart_mode INTO l_restart_mode FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_parent_job_type_id AND step_name=p_job_step_name AND step_type=STEPTYPE_JOB; IF l_restart_mode = RESTART_MODE_FAILURE THEN SELECT job_id INTO l_source_job_id FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_step_id AND rownum=1; l_new_job_id := SYS_GUID(); copy_job_entry(l_source_job_id, l_new_job_id, p_execution_id, p_parent_job_id); RETURN l_new_job_id; END IF; END IF; SELECT target_list_index INTO l_parent_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; -- Obtain the type of the nested job SELECT nested_job_type, nested_job_target_type, all_targets, all_params, flattened_targets INTO l_nested_job_type, l_target_type, l_all_targets, l_all_params, l_flattened_targets FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_parent_job_type_id AND step_name=p_job_step_name AND step_type=STEPTYPE_JOB; -- Use the most recent version of the nested job type get_max_versions(l_nested_job_type, l_nested_job_major_version, l_nested_job_type_id); SELECT job_owner, system_job INTO l_job_owner, l_system_job FROM MGMT_JOB WHERE job_id=p_parent_job_id; -- Inform the user model that a new job was added. Note that the -- user model expects the job id of the outermost job SELECT j.job_id, j.is_corrective_action INTO l_outer_job_id, l_is_corrective_action FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE execution_id=p_execution_id AND j.job_id=e.job_id; -- Insert a row into MGMT_JOB INSERT INTO MGMT_JOB (job_name, job_owner, job_description, job_type, job_type_major_version, target_type, nested, nested_job_type_id, parent_job_id, system_job, is_corrective_action) VALUES (p_job_step_name, l_job_owner, '', l_nested_job_type, l_nested_job_major_version, l_target_type, 1, l_nested_job_type_id, p_parent_job_id, l_system_job, l_is_corrective_action) RETURNING job_id INTO l_new_job_id; MGMT_USER.nested_job_added(l_outer_job_id, l_new_job_id); -- Insert the targets for the new job. Note there is just one -- target list for the job IF l_flattened_targets = 0 AND l_all_targets = 1 THEN INSERT into MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) SELECT l_new_job_id, p_execution_id, l_parent_target_list_index, target_guid, target_index FROM MGMT_JOB_TARGET WHERE job_id=p_parent_job_id AND execution_id=p_execution_id AND target_list_index=l_parent_target_list_index; ELSE -- Insert only specified targets insert_nested_job_targets(l_new_job_id, p_parent_job_id, p_execution_id, l_parent_target_list_index, -1, p_parent_job_type_id, p_job_step_name, p_iterate_param, p_iterate_param_index); END IF; -- Insert the job parameters. IF l_all_params=1 THEN -- Insert all the job parameters. Note: if a parameter is -- encrypted, we insert it encrypted as well. INSERT INTO MGMT_JOB_PARAMETER(job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value) SELECT l_new_job_id, p_execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value FROM MGMT_JOB_PARAMETER where job_id=p_parent_job_id AND execution_id=p_execution_id; ELSE -- Insert only specified parameters. Note that we use the parent -- job id since the nested job is a step in the parent job. -- Also note that we specify that encrypted parameters in the -- parent job remain encrypted in the nested job. -- The call to get_job_step_params() only handles scalar and -- vector parameters l_job_params := get_job_step_params(p_parent_job_id, p_execution_id, p_job_step_name, -1, p_parent_job_type_id, l_all_params, p_iterate_param, p_iterate_param_index, true, l_encrypt_info); update_job_parameters(l_new_job_id, p_execution_id, l_job_params, l_encrypt_info, false, false, false, 1); -- Insert any large parameters SELECT param_name, large_value BULK COLLECT INTO l_large_params, l_large_values FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_parent_job_type_id AND step_name=p_job_step_name AND parameter_type=PARAM_TYPE_LARGE; FOR i IN 1..l_large_params.COUNT LOOP BEGIN INSERT INTO MGMT_JOB_PARAMETER(job_id, execution_id, parameter_name, parameter_type, large_value) VALUES (l_new_job_id, p_execution_id, l_large_params(i), PARAM_TYPE_LARGE, l_large_values(i)); EXCEPTION WHEN NO_DATA_FOUND THEN NULL; WHEN OTHERS THEN log_error(p_execution_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception inserting large param: ' || SQLERRM); END; END LOOP; -- Insert any large parameters specified by the valueOf attribute SELECT param_name, value_of BULK COLLECT INTO l_params, l_valueof_params FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=p_parent_job_type_id AND step_name=p_job_step_name AND value_of IS NOT NULL; FOR i IN 1..l_params.COUNT LOOP BEGIN SELECT large_value, parameter_type INTO l_large_value, l_parameter_type FROM MGMT_JOB_PARAMETER WHERE job_id=p_parent_job_id AND execution_id=p_execution_id AND parameter_name=l_valueof_params(i) AND parameter_type=PARAM_TYPE_LARGE; INSERT INTO MGMT_JOB_PARAMETER(job_id, execution_id, parameter_name, parameter_type, large_value) VALUES (l_new_job_id, p_execution_id, l_params(i), PARAM_TYPE_LARGE, l_large_value); EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; END LOOP; FOR i IN 1..l_job_params.COUNT LOOP IF l_job_params(i).param_type = PARAM_TYPE_LARGE THEN BEGIN -- The name of the param in the parent job is passed as the scalar -- value corresponding to the name of the param in the nested job SELECT large_value INTO l_large_value FROM MGMT_JOB_PARAMETER WHERE parameter_name = l_job_params(i).scalar_value AND job_id = p_parent_job_id AND execution_id = p_execution_id AND parameter_type = PARAM_TYPE_LARGE; UPDATE MGMT_JOB_PARAMETER SET large_value = l_large_value WHERE parameter_name = l_job_params(i).param_name AND job_id = l_new_job_id AND execution_id = p_execution_id; EXCEPTION -- The large value was not present in the parent. Ignore, as we -- would have created an empty clob anyway WHEN NO_DATA_FOUND THEN NULL; END; END IF; END LOOP; END IF; -- resolve and insert nested job override credentials resolve_nested_job_creds(p_parent_job_id, p_execution_id, l_new_job_id, p_original_step_id, p_iterate_param, p_iterate_param_index, p_job_step_name, l_job_owner, p_parent_job_type_id); return l_new_job_id; EXCEPTION WHEN OTHERS THEN reset_params; RAISE; END; -- Suspend a job PROCEDURE suspend_job(p_job_id RAW) IS l_execution_ids MGMT_JOB_GUID_ARRAY; l_job_status MGMT_JOB.job_status%TYPE; BEGIN IF NOT is_suspendable(p_job_id) THEN raise_application_error(MGMT_GLOBAL.JOBTYPE_NON_SUSPEND_ERR, 'Cannot suspend job: job type marked as non-suspendable'); END IF; l_job_status := lock_job(p_job_id); IF l_job_status=JOB_STATUS_SUSPENDED THEN raise_application_error(MGMT_GLOBAL.JOB_SUSPENDED_ERR, 'Cannot suspend job: job currently suspended'); END IF; IF l_job_status=JOB_STATUS_REASSIGNED THEN raise_application_error(MGMT_GLOBAL.JOB_REASSIGNED_ERR, 'Cannot suspend job: job currently reassigned'); END IF; -- Suspend all active executions. Do not include executions -- suspended on lock or suspended on credentials unavailable SELECT execution_id BULK COLLECT INTO l_execution_ids FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status IN (SCHEDULED_STATUS, EXECUTING_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS); IF l_execution_ids IS NULL OR l_execution_ids.COUNT=0 THEN raise_application_error(MGMT_GLOBAL.JOB_SUSPEND_STOPPED_ERR, 'Cannot suspend job: no active executions to suspend'); END IF; FOR i IN 1..l_execution_ids.COUNT LOOP suspend_job_execution(l_execution_ids(i), SUSPENDED_STATUS, 0); END LOOP; UPDATE MGMT_JOB SET job_status=JOB_STATUS_SUSPENDED WHERE job_id=p_job_id; END; -- Resume a suspended job PROCEDURE resume_job(p_job_id RAW) IS l_execution_ids MGMT_JOB_GUID_ARRAY; l_job_status MGMT_JOB.job_status%TYPE; l_job_schedule MGMT_JOB_SCHEDULE_RECORD; l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN l_job_status := lock_job(p_job_id); IF l_job_status != JOB_STATUS_SUSPENDED THEN raise_application_error(MGMT_GLOBAL.JOB_RESUME_NOT_SUSPENDED_ERR, 'Cannot resume job: job is currently not suspended'); END IF; -- Get all suspended executions SELECT execution_id BULK COLLECT INTO l_execution_ids FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status=SUSPENDED_STATUS; UPDATE MGMT_JOB SET job_status=JOB_STATUS_ACTIVE WHERE job_id=p_job_id; IF l_execution_ids IS NULL OR l_execution_ids.COUNT=0 THEN RETURN; END IF; l_start_grace_period := get_start_grace_period(p_job_id); FOR i IN 1..l_execution_ids.COUNT LOOP resume_job_execution(l_execution_ids(i), SUSPENDED_STATUS, l_start_grace_period); END LOOP; END; -- Call all job retry callbacks PROCEDURE process_retry_callbacks(p_job_id IN RAW, p_exec_id IN RAW) IS l_retry_callbacks SMP_EMD_STRING_ARRAY; l_current_user MGMT_CREATED_USERS.user_name%TYPE := MGMT_USER.get_current_em_user; l_job_owner MGMT_CREATED_USERS.user_name%TYPE; BEGIN -- set user to the job owner SELECT job_owner INTO l_job_owner FROM MGMT_JOB WHERE job_id=p_job_id; SETEMUSERCONTEXT(l_job_owner,MGMT_USER.OP_SET_IDENTIFIER); SELECT callback_name BULK COLLECT INTO l_retry_callbacks FROM MGMT_JOB_CALLBACKS cb, MGMT_JOB_EXEC_SUMMARY e WHERE e.execution_id=p_exec_id AND cb.job_type_id=e.job_type_id AND callback_type=JOB_RETRY_CALLBACK; IF l_retry_callbacks IS NOT NULL AND l_retry_callbacks.count > 0 THEN FOR i IN 1..l_retry_callbacks.COUNT LOOP BEGIN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.qualified_sql_name(l_retry_callbacks(i)) || '(:1,:2,:3,:4); END;' USING JOB_RETRY_CALLBACK, EXECUTING_STATUS, p_job_id, p_exec_id; EXCEPTION WHEN OTHERS THEN log_error(p_exec_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception when invoking retry callback ' || l_retry_callbacks(i) || ':' || SQLERRM); END; END LOOP; END IF; -- set back to the original user SETEMUSERCONTEXT(l_current_user,MGMT_USER.OP_SET_IDENTIFIER); EXCEPTION WHEN OTHERS THEN IF l_current_user IS NOT NULL THEN SETEMUSERCONTEXT(l_current_user,MGMT_USER.OP_SET_IDENTIFIER); END IF; RAISE; -- Re-raise the exception END; -- Process the execution callbacks of the job PROCEDURE process_suspend_callbacks(p_job_id RAW, p_execution_id RAW, p_status NUMBER) IS l_suspend_callbacks SMP_EMD_STRING_ARRAY; BEGIN -- Call all suspend callbacks SELECT callback_name BULK COLLECT INTO l_suspend_callbacks FROM MGMT_JOB_CALLBACKS cb, MGMT_JOB_EXEC_SUMMARY e WHERE e.execution_id=p_execution_id AND cb.job_type_id=e.job_type_id AND callback_type=EXECUTION_SUSPENDED_CALLBACK; IF l_suspend_callbacks IS NOT NULL AND l_suspend_callbacks.count > 0 THEN FOR i IN 1..l_suspend_callbacks.COUNT LOOP BEGIN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.qualified_sql_name(l_suspend_callbacks(i)) || '(:1,:2,:3,:4); END;' USING EXECUTION_SUSPENDED_CALLBACK, p_status, p_job_id, p_execution_id; EXCEPTION WHEN OTHERS THEN log_error(p_execution_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception when invoking suspend callback ' || l_suspend_callbacks(i) || ':' || SQLERRM); END; END LOOP; END IF; END; -- Note. The caller is assumed to have locked the -- execution _before_ calling this routine. PROCEDURE convert_exec_to_waiting(p_job_id RAW, p_execution_id RAW, p_start_time DATE, p_tzregion VARCHAR2, p_step_type NUMBER, p_start_grace_period NUMBER) IS l_step_name MGMT_JOB_EXECUTION.step_name%TYPE; l_status_detail MGMT_JOB_EXEC_SUMMARY.status_detail%TYPE; l_start_time DATE; l_step_id MGMT_JOB_EXECUTION.step_id%TYPE; BEGIN IF p_step_type = STEPTYPE_START_WAIT_STEP THEN l_step_name := 'start_wait_step'; l_status_detail := START_WAITING_STATUS; l_start_time := p_start_time; ELSIF p_step_type = STEPTYPE_GRACE_WAIT_STEP THEN l_step_name := 'grace_wait_step'; l_status_detail := GRACE_WAITING_STATUS; l_start_time := p_start_time + mins_to_interval(p_start_grace_period); END IF; -- delete existing steps ... cleanup_execution(p_job_id, p_execution_id, true); -- and insert a wait step l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, -1, -1, RESTART_MODE_FAILURE, l_step_name, p_step_type, null, -1, -1, l_start_time, p_tzregion, WAITING_STATUS, WAIT_COMMAND); UPDATE MGMT_JOB_EXEC_SUMMARY SET status_detail = l_status_detail WHERE execution_id = p_execution_id; END; -- Internal method to suspend the execution of a job PROCEDURE suspend_job_execution(p_execution_id IN RAW, p_status NUMBER, p_timeout NUMBER) IS l_job_id MGMT_JOB.job_id%TYPE; l_status NUMBER; l_job_status MGMT_JOB.job_status%TYPE; l_expected_start_time MGMT_JOB_EXEC_SUMMARY.expected_start_time%TYPE; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_exec_status_detail MGMT_JOB_EXEC_SUMMARY.status_detail%TYPE; l_executing_count NUMBER; l_execution_status MGMT_JOB_EXEC_SUMMARY.status%TYPE := p_status; l_current_time DATE := SYSDATE_UTC(); l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN l_status := lock_executions(p_execution_id); -- Cannot suspend finished executions IF l_status IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, SKIPPED_STATUS, DELETE_PENDING_STATUS) THEN IF p_status = SUSPENDED_STATUS THEN raise_application_error(MGMT_GLOBAL.EXEC_SUSPEND_FINISHED_ERR, 'Cannot suspend finished executions'); ELSE RETURN; END IF; END IF; -- Suspended by user takes precedence over other forms of suspend. -- This is OK, since when suspended executions are resumed they -- may immediately enter one of the system suspended states -- depending on the state of the universe at that time IF l_status = SUSPENDED_STATUS THEN IF p_status = SUSPENDED_STATUS THEN raise_application_error(MGMT_GLOBAL.EXEC_SUSPENDED_ERR, 'Cannot suspend execution: execution currently suspended'); ELSE RETURN; END IF; END IF; SELECT j.job_id, j.job_status, e.expected_start_time, e.timezone_region, e.status_detail INTO l_job_id, l_job_status, l_expected_start_time, l_tzregion, l_exec_status_detail FROM MGMT_JOB j, MGMT_JOB_EXEC_SUMMARY e WHERE e.execution_id=p_execution_id AND j.job_id=e.job_id; -- If the job is suspended, individual executions cannot be -- suspended IF (p_status = SUSPENDED_STATUS) THEN IF (l_job_status = JOB_STATUS_SUSPENDED) THEN raise_application_error(MGMT_GLOBAL.EXEC_JOB_SUSPENDED_ERR, 'Cannot suspend execution: job currently suspended'); ELSIF (l_job_status = JOB_STATUS_REASSIGNED) THEN raise_application_error(MGMT_GLOBAL.EXEC_JOB_REASSIGNED_ERR, 'Cannot suspend execution: job currently reassigned'); END IF; END IF; -- Executions waiting on locks or events cannot be suspended IF l_status = SUSPENDED_EVENT_STATUS OR l_status = SUSPENDED_LOCK_STATUS THEN IF p_status = SUSPENDED_STATUS THEN raise_application_error(MGMT_GLOBAL.EXEC_SUSPENDED_ERR, 'Cannot suspend execution: execution suspended on EVENT or LOCK'); ELSE RETURN; END IF; END IF; l_start_grace_period := get_start_grace_period(l_job_id); -- Change the status of all scheduled steps to SUSPENDED. Note that -- we leave currently executing steps as they are; we do want to -- update their status when the steps finish IF l_status = SCHEDULED_STATUS AND l_start_grace_period != NO_START_GRACE THEN IF l_current_time > l_expected_start_time THEN convert_exec_to_waiting(l_job_id, p_execution_id, l_expected_start_time, l_tzregion, STEPTYPE_GRACE_WAIT_STEP, l_start_grace_period); ELSE convert_exec_to_waiting(l_job_id, p_execution_id, l_expected_start_time, l_tzregion, STEPTYPE_START_WAIT_STEP, l_start_grace_period); END IF; ELSIF p_status = SUSPENDED_STATUS THEN -- User suspend takes precedence over other forms of suspend UPDATE MGMT_JOB_EXECUTION SET step_status=p_status WHERE execution_id=p_execution_id AND step_status IN (SCHEDULED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS); SELECT COUNT(1) INTO l_executing_count FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND step_type=STEPTYPE_STEP AND step_status=EXECUTING_STATUS; IF l_executing_count > 0 THEN l_execution_status := SUSPEND_PENDING_STATUS; END IF; ELSE UPDATE MGMT_JOB_EXECUTION SET step_status=p_status WHERE execution_id=p_execution_id AND step_status=SCHEDULED_STATUS; END IF; UPDATE MGMT_JOB_EXEC_SUMMARY SET status=l_execution_status, suspend_timeout=p_timeout, suspend_time=l_current_time WHERE execution_id=p_execution_id; -- Update the row corresponding to parent steps -- of the suspended step(s) UPDATE MGMT_JOB_EXECUTION m SET step_status=l_execution_status WHERE execution_id=p_execution_id AND (parent_step_id=-1 OR EXISTS (SELECT 1 FROM (SELECT step_id FROM MGMT_JOB_EXECUTION START WITH execution_id=p_execution_id AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC, STEPTYPE_FLATTEN_TARGETS_STEP) AND step_status=p_status CONNECT BY step_id=prior parent_step_id) v WHERE v.step_id=m.step_id) ); process_suspend_callbacks(l_job_id, p_execution_id, l_execution_status); END; -- (External) Suspend the specified execution, step_id version PROCEDURE suspend_job_execution(p_step_id IN NUMBER, p_suspend_step IN NUMBER) IS l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; BEGIN SELECT execution_id into l_execution_id FROM MGMT_JOB_EXECUTION WHERE step_id=p_step_id; suspend_job_execution(l_execution_id, SUSPENDED_STATUS, 0); IF p_suspend_step = 1 THEN UPDATE MGMT_JOB_EXECUTION SET step_status=SUSPENDED_STATUS WHERE step_id=p_step_id AND step_status IN (SCHEDULED_STATUS, EXECUTING_STATUS); END IF; END; PROCEDURE suspend_job_execution_auto(p_step_id IN NUMBER, p_suspend_step IN NUMBER) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN suspend_job_execution(p_step_id, p_suspend_step); COMMIT; END; -- Note. All callers need to lock all executions of the job -- specified by p_job_id which are at the target list index -- specified by p_target_list_index _before_ calling this routine. -- This routine munges around with a few executions given a job_id -- and a target_list_index. PROCEDURE skip_exec_and_schedule_next(p_job_id RAW, p_target_list_index NUMBER, p_end_time DATE, p_execution_id RAW, p_skipped_reason NUMBER, p_on_resume BOOLEAN DEFAULT FALSE) IS l_source_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_next_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_next_exec_time DATE; l_expected_start_time MGMT_JOB_EXEC_SUMMARY.expected_start_time%TYPE; l_current_schedule MGMT_JOB_SCHEDULE_RECORD; l_target_guid MGMT_TARGETS.target_guid%TYPE; l_active NUMBER; l_exec_status NUMBER; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; BEGIN cleanup_execution(p_job_id, p_execution_id, true); -- While updating the execution, set the start time too as it is possible -- the execution has no grace period and was scheduled just after its -- expected start time UPDATE MGMT_JOB_EXEC_SUMMARY SET status = SKIPPED_STATUS, status_detail = p_skipped_reason, start_time = expected_start_time, end_time = p_end_time WHERE execution_id = p_execution_id RETURNING expected_start_time, source_execution_id INTO l_expected_start_time, l_source_exec_id; l_target_guid := get_single_target_guid(p_job_id, p_target_list_index, l_active); l_exec_status := get_execution_status(p_job_id, p_execution_id, l_source_exec_id, p_target_list_index, l_expected_start_time, SCHEDULED_STATUS, SCHEDULED_STATUS); BEGIN l_current_schedule := get_current_schedule(p_job_id); EXCEPTION WHEN NO_DATA_FOUND THEN -- we may get a NO_DATA_FOUND if this is a corrective action -- or a queued execution RETURN; END; IF p_on_resume OR (is_system_suspended_status(l_exec_status) AND (l_current_schedule.start_grace_period != NO_START_GRACE)) THEN schedule_next_job_execution(p_job_id, p_execution_id, l_expected_start_time, p_end_time, SCHEDULED_STATUS); ELSE l_tzregion := compute_timezone_region(p_job_id, l_target_guid, p_target_list_index, l_current_schedule); l_next_exec_time := get_next_execution_time(l_current_schedule, l_expected_start_time, l_tzregion, SYSDATE_UTC()); l_next_exec_id := schedule_execution(p_job_id, null, null, null, l_current_schedule, p_target_list_index, l_target_guid, l_expected_start_time, p_end_time, l_next_exec_time, null, WAITING_STATUS, START_WAITING_STATUS); END IF; END; -- Note. All callers need to lock all executions of the job -- specified by p_job_id which are at the target list index -- specified by p_target_list_index _before_ calling this routine. -- This routine munges around with a few executions given a job_id -- and a target_list_index. PROCEDURE skip_exec_and_schedule_next(p_job_id RAW, p_target_list_index NUMBER, p_end_time DATE, p_step_id NUMBER, p_skipped_reason NUMBER, p_on_resume BOOLEAN DEFAULT FALSE) IS l_current_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; BEGIN SELECT execution_id INTO l_current_exec_id FROM MGMT_JOB_EXECUTION WHERE step_id = p_step_id; skip_exec_and_schedule_next(p_job_id, p_target_list_index, p_end_time, l_current_exec_id, p_skipped_reason, p_on_resume); END; -- Resume a suspended job execution (internal) -- p_status is the current suspended status -- p_start_grace_period is the grace period -- p_suspend_timeout was this resume due to a timeout? What value? -- Note: only execAndSuspend steps can timeout. PROCEDURE resume_job_execution(p_execution_id IN RAW, p_status NUMBER, p_start_grace_period NUMBER, p_suspend_timeout NUMBER DEFAULT 0) IS l_count NUMBER; l_job_lock_status NUMBER; l_execution_status NUMBER; l_job_id MGMT_JOB.job_id%TYPE; l_resume_callbacks SMP_EMD_STRING_ARRAY; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_source_exec_id MGMT_JOB_EXEC_SUMMARY.source_execution_id%TYPE; l_status_detail MGMT_JOB_EXEC_SUMMARY.status_detail%TYPE; l_expected_start_time MGMT_JOB_EXEC_SUMMARY.expected_start_time%TYPE; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_suspend_time MGMT_JOB_EXEC_SUMMARY.suspend_time%TYPE; l_step_status NUMBER; l_job_status MGMT_JOB.job_status%TYPE; l_step_ids SMP_EMD_INTEGER_ARRAY; l_step_count NUMBER; l_source_step_id INTEGER; l_original_step_id INTEGER; l_completed_steps NUMBER; l_step_types MGMT_JOB_INT_ARRAY; BEGIN SELECT job_id, source_execution_id, target_list_index, expected_start_time, status_detail, timezone_region INTO l_job_id, l_source_exec_id, l_target_list_index, l_expected_start_time, l_status_detail, l_tzregion FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id AND status != DELETE_PENDING_STATUS; l_execution_status := lock_executions(p_execution_id); IF l_execution_status != p_status THEN -- Raise Exception when the caller is UI so that the -- UI Could catch it and show correct mesg IF p_status = SUSPENDED_STATUS THEN raise_application_error(MGMT_GLOBAL.EXEC_RESUME_NOT_SUSP_ERR, 'Cannot resume execution: execution is currently not suspended'); END IF; RETURN; END IF; -- If the job is suspended, individual executions cannot be -- resumed IF p_status=SUSPENDED_STATUS THEN SELECT job_status INTO l_job_status FROM MGMT_JOB j, MGMT_JOB_EXEC_SUMMARY e WHERE e.execution_id=p_execution_id AND j.job_id=e.job_id; IF l_job_status = JOB_STATUS_SUSPENDED THEN raise_application_error(MGMT_GLOBAL.EXEC_RESUME_JOB_SUSPENDED_ERR, 'Cannot resume execution: job currently suspended'); ELSIF l_job_status = JOB_STATUS_REASSIGNED THEN raise_application_error(MGMT_GLOBAL.EXEC_RESUME_JOB_SUSPENDED_ERR, 'Cannot resume execution: job currently reassigned'); END IF; END IF; -- Figure out whether to set the execution to SCHEDULED -- or EXECUTING or SKIPPED SELECT COUNT(*) INTO l_count FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND step_type = STEPTYPE_STEP AND step_status != p_status AND step_status != SCHEDULED_STATUS; IF l_count > 0 THEN l_execution_status := EXECUTING_STATUS; ELSIF p_start_grace_period = NO_START_GRACE THEN -- If there are subsequent skipped executions skip this as well SELECT COUNT(*) INTO l_count FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=l_job_id AND target_list_index=l_target_list_index AND expected_start_time > l_expected_start_time AND status=SKIPPED_STATUS; IF l_count > 0 THEN l_execution_status := SKIPPED_STATUS; ELSE l_execution_status := SCHEDULED_STATUS; END IF; ELSE l_execution_status := SCHEDULED_STATUS; END IF; IF l_source_exec_id=p_execution_id THEN l_source_exec_id := null; ELSE SELECT step_id, decode(original_step_id, -1, step_id, original_step_id) INTO l_source_step_id, l_original_step_id FROM MGMT_JOB_HISTORY WHERE execution_id=l_source_exec_id AND parent_step_id=-1; END IF; -- Account for any events that may cause it to suspend -- Note that this may cause the execution to be in one -- of the suspended states again l_execution_status := get_execution_status(l_job_id, p_execution_id, l_source_exec_id, l_target_list_index, SYSDATE_UTC(), l_execution_status, p_status); IF l_execution_status NOT IN (EXECUTING_STATUS, SCHEDULED_STATUS) THEN l_suspend_time := SYSDATE_UTC(); END IF; -- For the steps in the job, the status is either SCHEDULED (if the -- execution state is one of SCHEDULED or EXECUTING) or one of -- the suspended states, if that is what the status of the execution -- ended up being IF l_execution_status=EXECUTING_STATUS THEN l_step_status := SCHEDULED_STATUS; ELSE l_step_status := l_execution_status; END IF; UPDATE MGMT_JOB_EXEC_SUMMARY SET status=l_execution_status, suspend_event=null, suspend_time=l_suspend_time, suspend_timeout=0 WHERE execution_id=p_execution_id; DELETE FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=p_execution_id; IF l_execution_status=SKIPPED_STATUS THEN skip_exec_and_schedule_next(l_job_id, l_target_list_index, SYSDATE_UTC(), p_execution_id, SKIPPED_EXPIRY, true); -- assign step count 0 so that the callbacks are not run l_step_count := 0; ELSIF l_step_status=SCHEDULED_STATUS THEN IF l_status_detail IN (START_WAITING_STATUS, GRACE_WAITING_STATUS) THEN -- This is a job which was in suspended state with -- wait steps scheduled. We need to schedule the -- real steps on resumption. cleanup_execution(l_job_id, p_execution_id, true); insert_execution_params(l_job_id, p_execution_id, l_source_exec_id, SCHEDULED_STATUS); schedule_nested_job(l_job_id, l_job_id, p_execution_id, -1, l_source_step_id, l_original_step_id, RESTART_MODE_FAILURE, NULL, -1, l_expected_start_time, l_tzregion, l_step_status); ELSE -- Setting the status of a step back to scheduled may -- involve re-executing some parameter sources SELECT step_id, step_type BULK COLLECT INTO l_step_ids, l_step_types FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND step_status=p_status; -- Re-execute submit-time credential sources if -- the execution was resumed, and no step has executed yet IF p_status=SUSPENDED_CREDS_STATUS THEN SELECT COUNT(*) INTO l_completed_steps FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND step_type=STEPTYPE_STEP AND step_status != SCHEDULED_STATUS; IF l_completed_steps=0 THEN IF compute_cred_info(l_job_id, p_execution_id, null, 1,1) = 0 THEN -- Reexecution of submit-time sources -- failed! This should normally not happen IF l_step_ids IS NOT NULL THEN l_step_ids.DELETE; END IF; END IF; END IF; END IF; IF l_step_ids IS NOT NULL AND l_step_ids.COUNT > 0 THEN FOR i IN 1..l_step_ids.COUNT LOOP IF p_status = SUSPENDED_EVENT_STATUS AND l_step_types(i) = STEPTYPE_STEP AND p_suspend_timeout > 0 THEN -- write to the output for this step because -- error output would be lost (bug 5840734) -- since this step will be started again write_step_output(l_step_ids(i), ' Suspend step timeout exceeded. (' || p_suspend_timeout || ' minutes) ============================== '); END IF; update_step_status_nolock(l_step_ids(i), SCHEDULED_STATUS, 0); END LOOP; UPDATE MGMT_JOB_EXECUTION SET step_status=l_step_status WHERE execution_id=p_execution_id AND step_status=p_status; l_step_count := l_step_ids.COUNT; END IF; END IF; ELSE UPDATE MGMT_JOB_EXECUTION SET step_status=l_step_status WHERE execution_id=p_execution_id AND step_status=p_status; l_step_count := SQL%ROWCOUNT; END IF; IF l_step_count > 0 THEN -- Call all resume callbacks, only if the execution is -- not resuspended IF l_execution_status IN (EXECUTING_STATUS, SCHEDULED_STATUS) THEN SELECT callback_name BULK COLLECT INTO l_resume_callbacks FROM MGMT_JOB_CALLBACKS cb, MGMT_JOB_EXEC_SUMMARY e WHERE e.execution_id=p_execution_id AND cb.job_type_id=e.job_type_id AND callback_type=EXECUTION_RESUMED_CALLBACK; IF l_resume_callbacks IS NOT NULL AND l_resume_callbacks.count > 0 THEN FOR i IN 1..l_resume_callbacks.COUNT LOOP BEGIN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.qualified_sql_name(l_resume_callbacks(i)) || '(:1,:2,:3,:4); END;' USING EXECUTION_RESUMED_CALLBACK, p_status, l_job_id, p_execution_id; EXCEPTION WHEN OTHERS THEN log_error(p_execution_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception when invoking resume callback ' || l_resume_callbacks(i) || ':' || SQLERRM); END; END LOOP; END IF; END IF; END IF; END; -- Suspend an execution: external method PROCEDURE suspend_job_execution(p_execution_id IN RAW) IS BEGIN IF NOT is_suspendable(get_job_id(p_execution_id)) THEN raise_application_error(MGMT_GLOBAL.JOBTYPE_NON_SUSPEND_ERR, 'Cannot suspend execution: job type marked as non-suspendable'); END IF; suspend_job_execution(p_execution_id, SUSPENDED_STATUS, 0); END; -- Resume a execution (external) PROCEDURE resume_job_execution(p_execution_id IN RAW) IS l_job_id MGMT_JOB.job_id%TYPE; l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; l_start_grace_period := get_start_grace_period(l_job_id); resume_job_execution(p_execution_id, SUSPENDED_STATUS, l_start_grace_period); END; -- Returns TRUE if a job has active executions, else FALSE FUNCTION has_active_executions(p_job_id IN RAW) RETURN BOOLEAN IS l_active_execs NUMBER := 0; BEGIN SELECT COUNT(1) INTO l_active_execs FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id = p_job_id AND status NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS, SKIPPED_STATUS, DELETE_PENDING_STATUS) AND ROWNUM = 1; RETURN (l_active_execs = 1); END; -- Throw an exception if the specified job has active executions PROCEDURE check_active_executions(p_job_id IN RAW) IS BEGIN IF has_active_executions(p_job_id) THEN raise_application_error(MGMT_GLOBAL.ACTIVE_EXECUTIONS_EXIST_ERR, 'The specified job has active executions.'); END IF; END; --Return number of executions of a specified job -- FUNCTION get_num_executions(p_job_id IN RAW) RETURN NUMBER IS l_num_executions NUMBER; BEGIN SELECT count(execution_id) INTO l_num_executions FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id; RETURN l_num_executions; END; -- Throw an exception if the current user does not possess the -- specified privilege. If p_job_guid is null, then the privilege -- is assumed to be a system privilege. Otherwise, the privilege -- is assumed to be a job privilege on the specified job. -- p_allow_superuser determines whether the operation we're -- checking for is one that superusers could perform (in which -- case it is set to true) or can only be performed by a user -- that has FULL privilge on the job -- PROCEDURE check_priv(p_priv_name VARCHAR2, p_current_user VARCHAR2 DEFAULT NULL, p_job_id RAW DEFAULT NULL, p_allow_superuser BOOLEAN DEFAULT TRUE) IS l_current_user VARCHAR2(256); l_job_id MGMT_JOB.job_id%TYPE; BEGIN IF p_current_user IS NOT NULL THEN l_current_user := p_current_user; ELSE l_current_user := mgmt_user.get_current_em_user(); IF l_current_user IS NULL THEN l_current_user := USER; END IF; END IF; IF p_job_id IS NULL THEN l_job_id := MGMT_USER.NO_GUID; ELSE l_job_id := p_job_id; END IF; -- If superusers can perform this operation, then check if the -- current user is a superuser IF p_allow_superuser THEN IF MGMT_USER.has_priv(l_current_user, MGMT_USER.SUPER_USER) = MGMT_USER.USER_HAS_PRIV THEN RETURN; END IF; -- If the request privilege was SUPER_USER, we've already -- established that the user doesn't have it IF p_priv_name=MGMT_USER.SUPER_USER THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'The current user (' || l_current_user || ') does not have sufficient privileges to perform this operation'); END IF; END IF; -- If we're here, the user is not a superuser. Check that the user -- actually has the specified privilege IF MGMT_USER.has_priv(l_current_user, p_priv_name, l_job_id)=1 THEN RETURN; ELSE raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'The current user (' || l_current_user || ') does not have sufficient privileges to perform this operation'); END IF; END; -- Check that the caller has enough privileges to view -- the specified job PROCEDURE check_view_job(p_job_id RAW) IS l_job_id MGMT_JOB.job_id%TYPE; BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified job does not exist or the current user cannot view it'); END; -- Throw an exception if the current user cannot modify the specified -- CA PROCEDURE check_modify_ca(p_job_id RAW, p_owner VARCHAR2, p_current_user VARCHAR2, p_allow_superuser BOOLEAN DEFAULT TRUE) IS l_ca_scope MGMT_CORRECTIVE_ACTION.ca_scope%TYPE; l_ca_target_guid MGMT_CORRECTIVE_ACTION.ca_target_guid%TYPE; l_ca_template_guid MGMT_CORRECTIVE_ACTION.ca_template_guid%TYPE; l_current_user MGMT_CREATED_USERS.user_name%TYPE := MGMT_USER.get_current_em_user; l_job_id MGMT_JOB.job_id%TYPE := p_job_id; l_nested MGMT_JOB.nested%TYPE; l_num_rows NUMBER; BEGIN SELECT nested INTO l_nested FROM MGMT_JOB WHERE job_id=p_job_id; -- For nested jobs, check privs using the parent (outer) job id IF l_nested=1 THEN -- Check whether the nested job is currently being inserted SELECT COUNT(*) INTO l_num_rows FROM MGMT_JOB_HISTORY WHERE job_id=l_job_id; IF l_num_rows=0 THEN -- The job is currently being inserted RETURN; END IF; SELECT e.job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB_HISTORY h WHERE e.execution_id=h.execution_id AND h.job_id=p_job_id AND ROWNUM=1; END IF; SELECT ca_scope, ca_target_guid, ca_template_guid INTO l_ca_scope, l_ca_target_guid, l_ca_template_guid FROM MGMT_CORRECTIVE_ACTION WHERE job_id=l_job_id; -- Template copy CA cannot be modified, period IF l_ca_template_guid=CA_SCOPE_TEMPLATE_COPY THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Template copy CAs cannot be modified'); END IF; IF p_allow_superuser THEN IF MGMT_USER.has_priv(p_current_user, MGMT_USER.SUPER_USER) = MGMT_USER.USER_HAS_PRIV THEN RETURN; END IF; ELSIF l_ca_scope=CA_SCOPE_USER THEN -- Only the user can modify library CAs IF p_current_user != p_owner THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'Only the owner can modify a user-scoped CA'); END IF; ELSIF l_ca_scope=CA_SCOPE_TARGET THEN -- Any user with operator on the target can modify the CA IF MGMT_USER.has_priv(p_current_user, MGMT_USER.OPERATOR_TARGET, l_ca_target_guid)=0 THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'User ' || p_current_user || ' does not have operator on target'); END IF; ELSIF l_ca_scope=CA_SCOPE_TEMPLATE THEN BEGIN --become sysman SETEMUSERCONTEXT(MGMT_USER.GET_REPOSITORY_OWNER, MGMT_USER.OP_SET_IDENTIFIER); -- Check is the template exists. Account for the fact that -- the template may be non-existent at template CA creation time BEGIN SELECT template_guid INTO l_ca_template_guid FROM MGMT_TEMPLATES WHERE template_guid=l_ca_template_guid; EXCEPTION WHEN NO_DATA_FOUND THEN l_ca_template_guid := null; END; SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); -- Any user with FULL On the template can modify the CA IF l_ca_template_guid IS NOT NULL AND MGMT_USER.has_priv(p_current_user, MGMT_USER.FULL_TEMPLATE, l_ca_template_guid)=0 THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'User ' || p_current_user || ' does not have full on template'); END IF; EXCEPTION WHEN OTHERS THEN SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); RAISE; END; ELSIF l_ca_scope=CA_SCOPE_TARGET_TYPE THEN -- Only superusers can modify these CAs IF MGMT_USER.has_priv(p_current_user, MGMT_USER.SUPER_USER)=0 THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'User ' || p_current_user || ' is not a superuser'); END IF; ELSE -- Shouldn't happen unless there's a bug log_error(p_job_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Unexpected value for ca_scope for CA ' || p_job_id); END IF; END; -- Throw an exception if the current user does not have privileges to -- perform the current operation. -- If p_allow_superuser is TRUE, then superusers will be able to -- perform the operation. If set to FALSE, then the user will -- be able to perform the operation only if the user is the -- owner of the job. -- PROCEDURE check_modify_job(p_job_id RAW, p_allow_superuser BOOLEAN DEFAULT TRUE) IS l_owner VARCHAR2(256); l_current_user VARCHAR2(256) := mgmt_user.get_current_em_user(); l_nested NUMBER; l_job_id RAW(16) := p_job_id; l_is_ca MGMT_JOB.is_corrective_action%TYPE; l_num_rows NUMBER; BEGIN SELECT job_owner, nested, is_corrective_action INTO l_owner, l_nested, l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id AND job_status != JOB_STATUS_DELETE_PENDING; IF l_is_ca=1 THEN check_modify_ca(p_job_id, l_owner, l_current_user, p_allow_superuser); RETURN; END IF; IF l_current_user=l_owner THEN RETURN; END IF; -- For nested jobs, check privs using the parent (outer) job id IF l_nested=1 THEN -- Check whether the nested job is currently being inserted SELECT COUNT(*) INTO l_num_rows FROM MGMT_JOB_HISTORY WHERE job_id=l_job_id; IF l_num_rows=0 THEN -- The job is currently being inserted RETURN; END IF; SELECT e.job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB_HISTORY h WHERE e.execution_id=h.execution_id AND h.job_id=p_job_id AND ROWNUM=1; END IF; check_priv(MGMT_USER.FULL_JOB, l_current_user, l_job_id, p_allow_superuser); EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified job/execution does not exist'); END; -- Check security, given a step id PROCEDURE check_modify_step(p_step_id NUMBER, p_allow_superuser BOOLEAN DEFAULT TRUE) IS l_job_id RAW(16); BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=(SELECT execution_id FROM MGMT_JOB_HISTORY WHERE step_id=p_step_id); check_modify_job(l_job_id, p_allow_superuser); EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_EXECUTION_ERR, 'Specified execution does not exist'); END; PROCEDURE check_modify_execution(p_execution_id RAW, p_allow_superuser BOOLEAN DEFAULT TRUE) IS l_job_id RAW(16); BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; check_modify_job(l_job_id, p_allow_superuser); EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_EXECUTION_ERR, 'Specified execution does not exist'); END; -- Delete all nested jobs associated with this execution PROCEDURE delete_execution_nested_jobs(p_parent_job_id RAW, p_execution_id RAW) IS BEGIN FOR crec in (SELECT DISTINCT job_id FROM MGMT_JOB_HISTORY WHERE execution_id=p_execution_id) LOOP IF crec.job_id != p_parent_job_id THEN DELETE FROM MGMT_JOB WHERE job_id=crec.job_id; DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=crec.job_id AND execution_id=p_execution_id; DELETE FROM MGMT_JOB_TARGET WHERE job_id=crec.job_id AND execution_id=p_execution_id; DELETE FROM MGMT_JOB_EXT_TARGETS WHERE job_id=crec.job_id AND execution_id=p_execution_id; -- Inform the user model that this job was deleted MGMT_USER.nested_job_deleted(crec.job_id); END IF; END LOOP; END; -- Delete a job execution. Perform a security check if p_check_security -- is set to true. Internal method only PROCEDURE delete_job_execution(p_execution_id RAW, p_check_security BOOLEAN, p_delete_job BOOLEAN, p_lock_job BOOLEAN DEFAULT TRUE) IS l_job_id RAW(16); l_status NUMBER; l_count NUMBER; l_deletion_callbacks SMP_EMD_STRING_ARRAY; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN IF p_check_security THEN check_modify_execution(p_execution_id); -- Check if the execution is a source execution for -- any other execution SELECT count(execution_id) INTO l_count FROM MGMT_JOB_EXEC_SUMMARY WHERE source_execution_id=p_execution_id AND execution_id != p_execution_id; -- If we're here, this execution is the source execution -- for one or more executions IF l_count > 0 THEN raise_application_error(MGMT_GLOBAL.INVALID_EXECUTION_ERR, 'The specified execution is a source execution for one or more executions'); END IF; END IF; l_status := lock_executions(p_execution_id); BEGIN SELECT status, job_id, job_type_id INTO l_status, l_job_id, l_job_type_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; IF l_status != COMPLETED_STATUS AND l_status != FAILED_STATUS AND l_status != ABORTED_STATUS AND l_status != STOPPED_STATUS AND l_status != SKIPPED_STATUS AND l_status != DELETE_PENDING_STATUS THEN raise_application_error(MGMT_GLOBAL.ACTIVE_EXECUTIONS_EXIST_ERR, 'The specified execution is an active execution'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_EXECUTION_ERR, 'The specified execution does not exist'); END; -- Delete all nested jobs delete_execution_nested_jobs(l_job_id, p_execution_id); DELETE FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id; DELETE FROM MGMT_JOB_HISTORY WHERE execution_id=p_execution_id; DELETE FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=p_execution_id; DELETE FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; DELETE FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id = l_job_id AND execution_id=p_execution_id; DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=l_job_id AND execution_id=p_execution_id; DELETE FROM MGMT_JOB_TARGET WHERE job_id=l_job_id AND execution_id=p_execution_id; DELETE FROM MGMT_JOB_EXT_TARGETS WHERE job_id=l_job_id AND execution_id=p_execution_id; -- Call all deletion callbacks SELECT callback_name BULK COLLECT INTO l_deletion_callbacks FROM MGMT_JOB_CALLBACKS cb WHERE cb.job_type_id=l_job_type_id AND callback_type=EXECUTION_DELETED_CALLBACK; IF l_deletion_callbacks IS NOT NULL AND l_deletion_callbacks.count > 0 THEN FOR i IN 1..l_deletion_callbacks.COUNT LOOP BEGIN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.qualified_sql_name(l_deletion_callbacks(i)) || '(:1,:2,:3,:4); END;' USING EXECUTION_DELETED_CALLBACK, -1, l_job_id, p_execution_id; EXCEPTION WHEN OTHERS THEN log_error(p_execution_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception when invoking execDeleted callback ' || l_deletion_callbacks(i) || ':' || SQLERRM); END; END LOOP; END IF; IF p_delete_job THEN -- If there are no more executions, then delete the job itself SELECT COUNT(*) INTO l_count FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=l_job_id; IF l_count = 0 THEN delete_job(p_job_id => l_job_id, p_commit => 0, p_lock_job => p_lock_job); END IF; END IF; END; -- Delete a job run PROCEDURE delete_job_run(p_job_id RAW, p_scheduled_time DATE) IS l_run_ids MGMT_USER_GUID_ARRAY; BEGIN check_modify_job(p_job_id); SELECT execution_id BULK COLLECT INTO l_run_ids FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND scheduled_time=p_scheduled_time ORDER BY start_time desc, execution_id; -- order by to a) prevent deadlocks -- b) insure retried execs are deleted in the correct sequence IF l_run_ids IS NULL OR l_run_ids.COUNT=0 THEN RETURN; END IF; FOR i IN 2..l_run_ids.COUNT LOOP delete_job_execution(l_run_ids(i), false, false); END LOOP; -- if this is the last run, delete the job as well delete_job_execution(l_run_ids(1), false, true); END; -- External method PROCEDURE delete_job_execution(p_execution_id RAW) IS BEGIN delete_job_execution(p_execution_id, true, true); END; --Internal method --Submits a job to delete the jobs in deleting pending status --Marks the job and executions in Delete Pending status PROCEDURE do_async_delete_job(p_job_id IN RAW) IS l_current_user VARCHAR2(256) := MGMT_USER.GET_CURRENT_EM_USER; l_delete_job_name MGMT_JOB.job_name%TYPE; l_job_id MGMT_JOB.job_id%TYPE; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_schedule MGMT_JOB_SCHEDULE_RECORD; l_job_targets MGMT_JOB_TARGET_LIST := MGMT_JOB_TARGET_LIST(); BEGIN IF EMDW_LOG.p_is_info_set THEN emdw_log.info('submitting job to delete job id ' || p_job_id, MODULE_NAME); END IF; -- submit a job to delete the job asynchronously BEGIN --become sysman SETEMUSERCONTEXT(MGMT_USER.GET_REPOSITORY_OWNER, MGMT_USER.OP_SET_IDENTIFIER); --submit job with immediate schedule l_schedule := get_immediate_schedule_record; l_delete_job_name := 'DELJOB_JOB_' || SYS_GUID(); MGMT_JOBS.submit_job( l_delete_job_name, 'This is a job to delete other jobs', 'DeleteJob', l_job_targets, null, l_schedule, l_job_id, l_execution_id, null, SYSTEM_JOB_RETRY); IF EMDW_LOG.p_is_info_set THEN emdw_log.info('submitted deletion job: job_id ' || l_job_id || ': execution_id ' || l_execution_id, MODULE_NAME); END IF; --become normal SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); EXCEPTION WHEN OTHERS THEN IF l_current_user IS NOT NULL THEN SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); END IF; RAISE; END; --update job status UPDATE MGMT_JOB SET job_status = JOB_STATUS_DELETE_PENDING WHERE job_id = p_job_id; --update execution status UPDATE MGMT_JOB_EXEC_SUMMARY SET status = DELETE_PENDING_STATUS WHERE job_id = p_job_id; END; --private procedure to delete job -- PROCEDURE do_delete_job(p_job_id IN RAW, p_commit INTEGER DEFAULT 0) IS l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_is_library MGMT_JOB.is_library%TYPE; BEGIN process_delete_job(p_job_id, p_commit); -- Delete all overridden credentials for the job MGMT_CREDENTIAL.delete_job_credentials(p_job_id); -- Notify the User Model so privileges can be cleaned up MGMT_USER.JOB_DELETED(p_job_id); -- Now delete the job itself DELETE FROM MGMT_JOB where job_id=p_job_id; END; --private procedure to delete CA -- PROCEDURE do_delete_ca(p_job_id IN RAW, p_commit INTEGER DEFAULT 0) IS BEGIN process_delete_job(p_job_id, p_commit); -- Delete all references from policy association EM_POLICY.handle_delete_ca(p_job_id); -- Delete all overridden credentials for the job MGMT_CREDENTIAL.DELETE_CA_CREDENTIALS(p_job_id); -- Notify the User Model so privileges can be cleaned up MGMT_USER.JOB_DELETED(p_job_id); -- Now delete the job itself DELETE FROM MGMT_JOB where job_id=p_job_id; --Delete it from Corrective_action table DELETE FROM MGMT_CORRECTIVE_ACTION WHERE job_id=p_job_id; END; -- Private method.... PROCEDURE process_delete_job(p_job_id RAW, p_commit INTEGER DEFAULT 0) IS l_num_active_executions NUMBER; l_deletion_callbacks SMP_EMD_STRING_ARRAY; l_null_guid MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE := null; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_type MGMT_JOB_TYPE_INFO.job_type%TYPE; l_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; BEGIN -- Deleting a job amounts to deleting all the executions of the job FOR crec in (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id ORDER BY start_time) LOOP delete_job_execution(crec.execution_id, false, false); IF p_commit=1 THEN COMMIT; END IF; END LOOP; l_job_type_id := get_job_type_id(p_job_id); SELECT job_type, job_type_category INTO l_job_type, l_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; -- Cleanup the job type for hidden jobs IF l_category=JOBTYPE_CATEGORY_HIDDEN THEN DELETE FROM MGMT_JOB_TYPE_INFO WHERE job_type=l_job_type; END IF; -- Delete schedule if no other job is using it DELETE FROM MGMT_JOB_SCHEDULE WHERE schedule_id = ( SELECT schedule_id FROM MGMT_JOB j WHERE job_id = p_job_id ); -- Note: if we ever allow jobs to share schedules, we need to add -- a not exists clause here. -- We would also need to index MGMT_JOB.schedule_id -- AND NOT EXISTS -- ( SELECT job_id -- FROM MGMT_JOB -- WHERE schedule_id = j.schedule_id -- AND job_id != p_job_id ) ); -- Delete credential info DELETE FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id=p_job_id; -- Call all deletion callbacks SELECT callback_name BULK COLLECT INTO l_deletion_callbacks FROM MGMT_JOB_CALLBACKS cb WHERE cb.job_type_id=l_job_type_id AND callback_type=JOB_DELETED_CALLBACK; IF l_deletion_callbacks IS NOT NULL AND l_deletion_callbacks.count > 0 THEN FOR i IN 1..l_deletion_callbacks.COUNT LOOP BEGIN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.qualified_sql_name(l_deletion_callbacks(i)) || '(:1,:2,:3,:4); END;' USING JOB_DELETED_CALLBACK, -1, p_job_id, l_null_guid; EXCEPTION WHEN OTHERS THEN log_error(p_job_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception when invoking jobDeleted callback ' || l_deletion_callbacks(i) || ':' || SQLERRM, false); END; END LOOP; END IF; END; PROCEDURE delete_job(p_job_id IN RAW, p_commit INTEGER DEFAULT 0, p_lock_job BOOLEAN DEFAULT TRUE) IS l_ignore NUMBER; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_is_library MGMT_JOB.is_library%TYPE; l_num_executions NUMBER; --Audit delete_job l_job_type MGMT_JOB.job_type%TYPE; l_audit_level NUMBER; BEGIN IF EMDW_LOG.p_is_info_set THEN emdw_log.info('Deleting job id ' || p_job_id, MODULE_NAME); END IF; check_modify_job(p_job_id); IF p_lock_job THEN l_ignore := lock_job(p_job_id); END IF; --check if there are any active executions check_active_executions(p_job_id); --get number of executions of this job l_num_executions := get_num_executions(p_job_id); -- get the audit level, and store advance information -- about the job if auditing is turned on mgmt_audit_admin.audit_level(l_audit_level); IF (l_audit_level = mgmt_audit_admin.AUDIT_LEVEL_ALL OR l_audit_level = mgmt_audit_admin.AUDIT_LEVEL_SELECTED) THEN SELECT job_name, job_owner, is_library, job_type INTO l_job_name, l_job_owner, l_is_library, l_job_type FROM MGMT_JOB WHERE job_id=p_job_id; END IF; IF l_num_executions > DELETE_JOB_UPPER_BOUND THEN do_async_delete_job(p_job_id); ELSE --delete the job synchronously do_delete_job(p_job_id, p_commit); END IF; IF p_commit=1 THEN COMMIT; END IF; --Audit delete_job IF (l_audit_level = mgmt_audit_admin.AUDIT_LEVEL_ALL OR l_audit_level = mgmt_audit_admin.AUDIT_LEVEL_SELECTED) THEN mgmt_audit_log.audit_log(mgmt_audit_log.DELETE_JOB, upper(l_job_name), upper(l_job_type), upper(l_job_owner)); END IF; END; PROCEDURE delete_ca(p_job_id IN RAW, p_commit INTEGER DEFAULT 0) IS l_ignore NUMBER; l_num_executions NUMBER; BEGIN IF EMDW_LOG.p_is_info_set THEN emdw_log.info('deleting ca id ' || p_job_id, MODULE_NAME); END IF; check_modify_job(p_job_id); l_ignore := lock_job(p_job_id); --check if there are any active executions check_active_executions(p_job_id); --get number of executions of this job l_num_executions := get_num_executions(p_job_id); IF l_num_executions > DELETE_JOB_UPPER_BOUND THEN do_async_delete_job(p_job_id); ELSE --delete the ca synchronously do_delete_ca(p_job_id, p_commit); END IF; IF p_commit=1 THEN COMMIT; END IF; END; PROCEDURE delete_job(p_job_name IN VARCHAR2, p_job_owner IN VARCHAR2, p_commit NUMBER DEFAULT 0, p_is_library NUMBER DEFAULT 0) IS l_num_active_executions NUMBER; l_job_id RAW(16); BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB WHERE job_name=upper(p_job_name) AND job_owner=upper(p_job_owner) AND is_library=p_is_library AND is_corrective_action=0 AND nested=0; delete_job(p_job_id=>l_job_id, p_commit=>p_commit); EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified job does not exist'); END; -- Gets called by DeleteJob job type. -- Deletes the jobs and CAs that are in status JOB_STATUS_DELETE_PENDING -- PROCEDURE delete_job_complete IS l_job_ids MGMT_JOB_GUID_ARRAY; l_is_corrective_action MGMT_JOB_INT_ARRAY; l_dummy NUMBER; l_lock_acquired NUMBER; l_failure_count NUMBER := 0; BEGIN IF EMDW_LOG.p_is_info_set THEN emdw_log.info('attempting to delete jobs in delete pending state' , MODULE_NAME); END IF; -- select all jobs that are marked as delete pending. -- order by job_id to avoid deadlock. SELECT job_id, is_corrective_action BULK COLLECT INTO l_job_ids, l_is_corrective_action FROM MGMT_JOB WHERE job_status=JOB_STATUS_DELETE_PENDING AND nested=0 ORDER BY job_id; IF l_job_ids IS NOT NULL THEN FOR i in 1..l_job_ids.COUNT LOOP IF EMDW_LOG.p_is_info_set THEN emdw_log.info('deleting job id ' || l_job_ids(i), MODULE_NAME); END IF; BEGIN l_dummy := lock_job(l_job_ids(i)); l_lock_acquired := 1; EXCEPTION WHEN NO_DATA_FOUND THEN --this means that someone else removed the entry between --now and previous select l_lock_acquired := 0; END; IF l_lock_acquired = 1 THEN BEGIN IF l_is_corrective_action(i) = 0 THEN do_delete_job(l_job_ids(i)); ELSE do_delete_ca(l_job_ids(i)); END IF; --commit after every job; COMMIT; EXCEPTION WHEN OTHERS THEN log_error(l_job_ids(i), MGMT_GLOBAL.DELETE_JOB_COMPLETE_ERR, 'Exception when deleting a job using DeleteJob jobtype:' || SQLERRM, false); l_failure_count := l_failure_count + 1; --rollback changes for this job ROLLBACK; END; END IF; END LOOP; END IF; --if deletion failed for any reason, raise an exception so that the job --can retry IF l_failure_count > 0 THEN raise_application_error(MGMT_GLOBAL.DELETE_JOB_COMPLETE_ERR, 'Failed to delete ' || l_failure_count || ' job(s).'); END IF; END; PROCEDURE delete_job_executions(p_execution_ids IN MGMT_JOB_GUID_ARRAY, p_commit NUMBER DEFAULT 0) IS l_current_status NUMBER; BEGIN FOR i in 1..p_execution_ids.count LOOP delete_job_execution(p_execution_ids(i), true, true); IF p_commit=1 THEN COMMIT; END IF; END LOOP; END; -- Delete all executions of a job: internal method PROCEDURE delete_all_executions_id(p_job_id RAW, p_commit NUMBER DEFAULT 0) IS BEGIN FOR crec IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status IN (COMPLETED_STATUS, FAILED_STATUS, STOPPED_STATUS, ABORTED_STATUS, SKIPPED_STATUS)) LOOP delete_job_execution(crec.execution_id, false, true); IF p_commit=1 THEN COMMIT; END IF; END LOOP; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified job does not exist'); END; -- External method PROCEDURE delete_all_executions(p_job_name VARCHAR2, p_job_owner VARCHAR2, p_commit NUMBER DEFAULT 0, p_is_library NUMBER DEFAULT 0) IS l_job_id MGMT_JOB.job_id%TYPE; BEGIN l_job_id := get_job_id(p_job_name, p_job_owner, p_is_library); check_modify_job(l_job_id); delete_all_executions_id(l_job_id, p_commit); END; -- Move a stopped execution to history, call the appropriate -- callbacks PROCEDURE move_stopped_exec_to_history(p_execution_id RAW, p_target_list_index NUMBER, p_start_time DATE, p_end_time DATE, p_schedule_next BOOLEAN DEFAULT false) IS l_stop_callbacks SMP_EMD_STRING_ARRAY; l_job_id RAW(16); BEGIN -- ensure we have the top level job id SELECT job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; -- For single-target jobs, update the flat list to indicate -- that the target is no longer active IF is_single_target_job(l_job_id) AND NOT p_schedule_next THEN UPDATE MGMT_JOB_FLAT_TARGETS SET active=0 WHERE job_id=l_job_id AND target_list_index=p_target_list_index; END IF; move_execution_to_history(l_job_id, p_execution_id, STOPPED_STATUS, p_start_time, p_end_time, p_schedule_next); -- Call all stop callbacks SELECT callback_name BULK COLLECT INTO l_stop_callbacks FROM MGMT_JOB_CALLBACKS cb, MGMT_JOB_EXEC_SUMMARY e WHERE e.job_type_id=cb.job_type_id AND e.execution_id=p_execution_id AND callback_type=EXECUTION_STOPPED_CALLBACK; IF l_stop_callbacks IS NOT NULL AND l_stop_callbacks.count > 0 THEN FOR i IN 1..l_stop_callbacks.COUNT LOOP BEGIN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.qualified_sql_name(l_stop_callbacks(i)) || '(:1,:2,:3,:4); END;' USING EXECUTION_STOPPED_CALLBACK, STOPPED_STATUS, l_job_id, p_execution_id; EXCEPTION WHEN OTHERS THEN log_error(p_execution_id, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Exception when invoking stopped callback ' || l_stop_callbacks(i) || ':' || SQLERRM); END; END LOOP; END IF; END; -- Stop the specified execution. If p_check_security is true, a -- security check is performed. Note: this is an internal method -- only PROCEDURE stop_execution(p_execution_id IN RAW, p_check_security IN BOOLEAN, p_schedule_next IN BOOLEAN DEFAULT FALSE, p_force_stop IN BOOLEAN DEFAULT FALSE, p_stop_waiting IN BOOLEAN DEFAULT FALSE) IS l_job_id MGMT_JOB.job_id%TYPE; l_is_corrective_action MGMT_JOB.is_corrective_action%TYPE; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_end_time DATE := SYSDATE_UTC(); l_start_time DATE; l_stop_status NUMBER := STOPPED_STATUS; l_curr_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_step_ids SMP_EMD_INTEGER_ARRAY := NULL; l_count NUMBER := 0; l_update_count NUMBER := 0; l_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('stop_execution:enter,exec=' || p_execution_id, MODULE_NAME); END IF; -- Lock execution prior to operating on it l_status := lock_executions(p_execution_id); IF l_status < 0 THEN raise_application_error(MGMT_GLOBAL.INVALID_EXECUTION_ERR, 'The specified execution does not exist'); END IF; IF p_check_security THEN check_modify_execution(p_execution_id); END IF; SELECT e.status, e.job_id, e.target_list_index, e.start_time, j.is_corrective_action INTO l_curr_status, l_job_id, l_target_list_index, l_start_time, l_is_corrective_action FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE e.execution_id=p_execution_id AND e.job_id = j.job_id AND e.status != DELETE_PENDING_STATUS; IF l_curr_status = WAITING_STATUS AND NOT p_stop_waiting THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Waiting executions cannot be stopped'); END IF; IF (not p_force_stop) THEN -- Figure out the status to set the job to (stopped/stop pending) SELECT step_id BULK COLLECT INTO l_step_ids FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND step_type=STEPTYPE_STEP AND step_status=EXECUTING_STATUS; IF l_step_ids IS NULL THEN l_count := 0; ELSE l_count := l_step_ids.COUNT; END IF; IF l_count > 0 THEN l_stop_status := STOP_PENDING_STATUS; END IF; UPDATE MGMT_JOB_EXECUTION SET step_status=l_stop_status, end_time=l_end_time WHERE execution_id=p_execution_id AND step_id NOT IN (SELECT * FROM TABLE(CAST(l_step_ids AS SMP_EMD_INTEGER_ARRAY))) AND step_status NOT IN (COMPLETED_STATUS, FAILED_STATUS, STOPPED_STATUS, ABORTED_STATUS, SKIPPED_STATUS); l_update_count := SQL%ROWCOUNT; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('stop_execution:not a forced stop,exec=' || p_execution_id || ',running count=' || l_count || ',update count=' || l_update_count, MODULE_NAME); END IF; IF (l_update_count = 0) AND (l_count = 0) THEN RETURN; END IF; ELSE -- Force stop the job execution UPDATE MGMT_JOB_EXECUTION SET step_status=STOPPED_STATUS, end_time=l_end_time WHERE execution_id=p_execution_id AND step_status NOT IN (COMPLETED_STATUS, FAILED_STATUS, STOPPED_STATUS, ABORTED_STATUS, SKIPPED_STATUS) ; l_update_count := SQL%ROWCOUNT; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('stop_execution:forced stop,exec=' || p_execution_id || ',update count=' || l_update_count, MODULE_NAME); END IF; IF l_update_count = 0 THEN RETURN; END IF; UPDATE MGMT_JOB_EXEC_SUMMARY SET status=STOPPED_STATUS WHERE execution_id=p_execution_id; END IF; -- If the status is stop pending, do not move it to history yet IF (l_stop_status=STOP_PENDING_STATUS) AND ( not p_force_stop) THEN UPDATE MGMT_JOB_EXEC_SUMMARY SET status=STOP_PENDING_STATUS WHERE execution_id=p_execution_id; --update status of STEPTYPE_JOB UPDATE MGMT_JOB_EXECUTION SET step_status=STOP_PENDING_STATUS WHERE execution_id=p_execution_id AND step_type=STEPTYPE_JOB; l_update_count := SQL%ROWCOUNT; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('stop_execution:stop pending with no force,exec=' || p_execution_id || ',update to pending count=' || l_update_count, MODULE_NAME); END IF; ELSE IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('stop_execution:moving exec to history,exec=' || p_execution_id, MODULE_NAME); END IF; move_stopped_exec_to_history(p_execution_id, l_target_list_index, l_start_time, l_end_time, p_schedule_next); END IF; -- Update the status of the job, if required IF ( ( NOT has_active_executions(l_job_id) ) AND l_is_corrective_action = 0) THEN UPDATE MGMT_JOB SET job_status=JOB_STATUS_STOPPED, expired=1 WHERE job_id=l_job_id; END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('stop_execution:exit,exec=' || p_execution_id, MODULE_NAME); END IF; END; -- External method.... PROCEDURE stop_execution(p_execution_id RAW) IS BEGIN stop_execution(p_execution_id, true, true,false); END; -- Stop the specified set of executions. PROCEDURE stop_executions(p_execution_ids MGMT_JOB_GUID_ARRAY) IS BEGIN FOR i IN 1..p_execution_ids.count LOOP stop_execution(p_execution_ids(i), true, true, false); END LOOP; END; -- stop a job run PROCEDURE stop_job_run(p_job_id RAW, p_scheduled_time DATE) IS l_run_ids MGMT_JOB_GUID_ARRAY; l_statuses MGMT_JOB_INT_ARRAY; BEGIN check_modify_job(p_job_id); SELECT execution_id, status BULK COLLECT INTO l_run_ids, l_statuses FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND scheduled_time=p_scheduled_time ORDER BY execution_id; -- order by to prevent deadlocks IF l_run_ids IS NULL OR l_run_ids.COUNT=0 THEN RETURN; END IF; FOR i IN 1..l_run_ids.count LOOP stop_execution(l_run_ids(i), true, true,false,true); -- Delete all waiting executions immediately IF l_statuses(i) = WAITING_STATUS THEN BEGIN delete_job_execution(l_run_ids(i), false, false); EXCEPTION -- For system jobs, this could fail WHEN OTHERS THEN NULL; END; END IF; END LOOP; END; -- Stop all active executions in the specified job PROCEDURE stop_all_executions_with_id(p_job_id RAW, p_force_stop in BOOLEAN DEFAULT FALSE) IS l_curr_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; BEGIN check_modify_job(p_job_id); -- Note: All waiting executions need to be stopped here FOR crec IN (SELECT execution_id, status FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status NOT IN (STOPPED_STATUS, ABORTED_STATUS, COMPLETED_STATUS, FAILED_STATUS, SKIPPED_STATUS, DELETE_PENDING_STATUS) ORDER BY execution_id) LOOP l_curr_status := crec.status; BEGIN stop_execution(crec.execution_id, false,false,p_force_stop,true); EXCEPTION WHEN OTHERS THEN IF l_curr_status=WAITING_STATUS THEN -- Waiting executions could already have been -- removed by the stopping of other executions NULL; ELSE RAISE; END IF; END; -- Do not keep waiting executions in the system, remove them -- immediately IF l_curr_status=WAITING_STATUS THEN BEGIN delete_job_execution(crec.execution_id, false, false); EXCEPTION -- For system jobs, this could fail WHEN OTHERS THEN NULL; END; END IF; END LOOP; -- Set the job to stopped as well. UPDATE MGMT_JOB SET job_status=JOB_STATUS_STOPPED, expired=1 WHERE job_id=p_job_id; END; -- Stop all active executions in the specified job PROCEDURE stop_all_executions(p_job_name VARCHAR2, p_job_owner VARCHAR2, p_force_stop IN BOOLEAN DEFAULT FALSE) IS l_job_id MGMT_JOB.job_id%TYPE; BEGIN l_job_id := get_job_id(p_job_name, p_job_owner, 0); stop_all_executions_with_id(l_job_id,p_force_stop); END; -- Stop all active executions in the specified job -- -- This procedure works around problems with calling stop_all_executions -- from java, due to the BOOLEAN parameter. -- PROCEDURE force_stop_all_executions(p_job_name VARCHAR2, p_job_owner VARCHAR2) IS l_job_id MGMT_JOB.job_id%TYPE; BEGIN l_job_id := get_job_id(p_job_name, p_job_owner, 0); stop_all_executions_with_id(l_job_id,TRUE); END; -- Schedule a nested job PROCEDURE schedule_nested_job(p_job_id RAW, p_parent_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER, p_insert_wait_step BOOLEAN DEFAULT FALSE) IS initial_stepname VARCHAR2(64); initial_steptype NUMBER(1); l_job_name VARCHAR2(64); l_job_step_id INTEGER; l_current_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_update_status NUMBER := p_update_status; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_creds_set NUMBER; BEGIN IF p_insert_wait_step THEN l_job_step_id := insert_scheduled_entry( p_parent_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, 'start_wait_step', STEPTYPE_START_WAIT_STEP, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, l_update_status, WAIT_COMMAND); RETURN; END IF; l_job_type_id := get_job_type_id(p_job_id, p_execution_id); SELECT job_name INTO l_job_name FROM MGMT_JOB WHERE job_id=p_job_id; SELECT status INTO l_current_status FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; -- If the incoming status (p_update_status) is a suspended -- status, then that overrides any -- status we obtained from the serialization info. IF is_system_suspended_status(l_update_status) AND NOT is_system_suspended_status(l_current_status) THEN suspend_job_execution(p_execution_id, l_update_status, 0); END IF; -- Insert an entry for this job. The entry for the job is at the -- same nesting level as the parent. It's steps are at a higher -- nesting level. Also, note that the entry for the job has the -- parent's job id l_job_step_id := insert_scheduled_entry(p_parent_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, l_job_name, STEPTYPE_JOB, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, l_update_status); -- Figure out the first stepset to schedule SELECT step_name, step_type INTO initial_stepname, initial_steptype FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND incoming_edge_type = -1; -- The child steps of a nested job must always be SCHEDULED.. IF l_update_status=EXECUTING_STATUS THEN l_update_status := SCHEDULED_STATUS; END IF; -- Execute submission-time parameter sources if this is a nested -- job IF p_parent_step_id IS NOT NULL AND p_parent_step_id>0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('Trying to run param sources for nested job ' || p_job_id, MODULE_NAME); END IF; IF p_source_step_id > 0 THEN EMDW_LOG.debug('Running retry sources only', MODULE_NAME); -- This is a retried nested job, only re-execute -- param sources marked evaluateOnRetry l_creds_set := fetch_job_parameters(p_job_id, p_execution_id, null, null, 1, STEPTYPE_PARAMSRC_RETRY_EXEC); ELSE EMDW_LOG.debug('Running all submisson sources', MODULE_NAME); -- Newly scheduled nested job, execute all param sources l_creds_set := fetch_job_parameters(p_job_id, p_execution_id, null, null, 1); END IF; IF l_creds_set=0 THEN l_update_status := SUSPENDED_CREDS_STATUS; END IF; END IF; schedule(p_job_id, p_execution_id, l_job_step_id, p_source_step_id, p_original_step_id, p_restart_mode, l_job_type_id, initial_stepname, initial_steptype, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, l_update_status); END; -- "Adjust" the incoming start time, which is in *local* time, -- as follows: -- If the start time is less than current time, set it to be -- equal to the current time and return it -- Else, return the start time -- Also adjust the execution hours and minutes of the schedule. -- This function returns a time that is in UTC, using the tzregion -- specified FUNCTION adjust_local_start_time(p_schedule MGMT_JOB_SCHEDULE_RECORD, p_next_start_time DATE, p_tzregion VARCHAR2) RETURN DATE IS l_next_start_time DATE := p_next_start_time; BEGIN -- If the schedule has a start time, then apply it IF p_schedule.frequency_code != IMMEDIATE_FREQUENCY_CODE AND p_schedule.frequency_code != ONE_TIME_FREQUENCY_CODE AND p_schedule.frequency_code != INTERVAL_FREQUENCY_CODE THEN l_next_start_time := TRUNC(l_next_start_time, 'DD') + (p_schedule.execution_hours+p_schedule.execution_minutes/60)/24; /************** l_next_start_time := -- We're really doing the equivalent of -- trunc(l_next_start_time) here. Unfortunately, trunc -- does not work with timestamps.... local_date_to_local_time(trunc(CAST(l_next_start_time AS DATE), 'DD'), p_tzoffset) + numtodsinterval(p_schedule.execution_hours*60+p_schedule.execution_minutes, 'MINUTE'); ************/ END IF; IF p_schedule.end_time IS NULL THEN RETURN MGMT_GLOBAL.to_utc(l_next_start_time, p_tzregion); ELSIF l_next_start_time <= p_schedule.end_time THEN RETURN MGMT_GLOBAL.to_utc(l_next_start_time, p_tzregion); ELSE RETURN null; END IF; END; -- Daylight savings related fix -- Oracle throws ORA-01878 when we try to create -- a TIMESTAMP which falls between the time jump -- we catch the exception and adjust the time to -- next valid time -- we do that by incrementing the TIMESTAMP by -- 15 min iteratively -- we also check if the schedule of the job has expired -- as we increment the timestamp FUNCTION daylight_adjusted_local_time(p_schedule MGMT_JOB_SCHEDULE_RECORD, p_next_start_time DATE, p_tzregion VARCHAR2, p_move_ahead BOOLEAN) RETURN DATE IS l_next_start_time DATE := p_next_start_time; BEGIN LOOP BEGIN IF p_schedule.end_time IS NULL THEN RETURN MGMT_GLOBAL.to_utc(l_next_start_time, p_tzregion); ELSIF l_next_start_time <= p_schedule.end_time THEN RETURN MGMT_GLOBAL.to_utc(l_next_start_time, p_tzregion); ELSE RETURN null; END IF; EXCEPTION WHEN OTHERS THEN IF SQLCODE = -01878 THEN IF p_move_ahead = true THEN l_next_start_time := l_next_start_time + 1/96 ; -- 15 min ELSE l_next_start_time := l_next_start_time - 1/96 ; -- 15 min END IF; ELSE RAISE; END IF; END; END LOOP; END; -- "Adjust" the incoming start time, which is in UTC time, -- as follows: -- If the start time is less than current time, set it to be -- equal to the current time and return it -- Else, return the start time -- Also adjust the execution hours and minutes of the schedule. -- This function returns a time that is in UTC, using the tzregion -- specified FUNCTION adjust_utc_start_time(p_schedule MGMT_JOB_SCHEDULE_RECORD, p_next_start_time DATE, p_tzregion VARCHAR2) RETURN DATE IS l_next_start_time DATE := p_next_start_time; BEGIN -- If the schedule has a start time, then apply it IF p_schedule.frequency_code != IMMEDIATE_FREQUENCY_CODE AND p_schedule.frequency_code != ONE_TIME_FREQUENCY_CODE AND p_schedule.frequency_code != INTERVAL_FREQUENCY_CODE THEN l_next_start_time := TRUNC(l_next_start_time, 'DD') + (p_schedule.execution_hours+p_schedule.execution_minutes/60)/24; END IF; IF p_schedule.end_time IS NULL THEN RETURN l_next_start_time; ELSIF l_next_start_time <= MGMT_GLOBAL.to_utc(p_schedule.end_time, p_tzregion) THEN RETURN l_next_start_time; ELSE RETURN null; END IF; END; -- Return the Day of the week (Bug 5034533) -- Use to_char(date,'DAY') instead of to_char(date,'D'); -- -- 1 SUNDAY -- 2 MONDAY -- 3 TUESDAY -- 4 WEDNESDAY -- 5 THURSDAY -- 6 FRIDAY -- 7 SATURDAY -- FUNCTION day_of_week(p_date DATE) RETURN NUMBER IS l_well_known_sunday DATE := to_date('20060101','yyyymmdd'); l_sunday NUMBER := to_char(l_well_known_sunday, 'D'); l_day NUMBER := to_char(p_date,'D'); l_day_of_week NUMBER := mod(7 + l_day - l_sunday, 7) + 1; BEGIN return l_day_of_week; END; -- Validate the specified schedule; usually done at job submission time PROCEDURE validate_schedule(p_schedule IN OUT NOCOPY MGMT_JOB_SCHEDULE_RECORD) IS l_max_interval NUMBER; BEGIN IF p_schedule.frequency_code=IMMEDIATE_FREQUENCY_CODE THEN IF p_schedule.end_time IS NOT NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The start time must be null for immediate schedules'); END IF; IF p_schedule.end_time IS NOT NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The end time must be null for immediate schedules'); END IF; IF p_schedule.timezone_info != TIMEZONE_REPOSITORY THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The timezone type must be TIMEZONE_REPOSITORY for immediate schedules'); END IF; RETURN; END IF; IF p_schedule.start_time IS NULL THEN IF p_schedule.timezone_info=TIMEZONE_REPOSITORY THEN p_schedule.start_time := SYSDATE; ELSIF p_schedule.timezone_info=TIMEZONE_SPECIFIED THEN p_schedule.start_time := SYSDATE_TZRGN(to_region(p_schedule.timezone_offset)); ELSIF p_schedule.timezone_info=TIMEZONE_RGN_SPECIFIED THEN p_schedule.start_time := SYSDATE_TZRGN(p_schedule.timezone_region); ELSE raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Start time must be specified'); END IF; END IF; -- The end time cannot be null IF p_schedule.end_time IS NOT NULL AND p_schedule.end_time < p_schedule.start_time THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Start time is later than end time'); END IF; IF p_schedule.frequency_code=ONE_TIME_FREQUENCY_CODE THEN IF p_schedule.end_time IS NOT NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The end time must be null for one-time schedules'); END IF; -- The start grace period has greater than or equal to 10 minutes -- and less than 1 day IF (p_schedule.start_grace_period != -1) AND ((p_schedule.start_grace_period < 10) OR (p_schedule.start_grace_period > 1439)) THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The start grace period should be greater than or equal to 10 minutes and less than a day.'); END IF; RETURN; ELSIF p_schedule.end_time IS NOT NULL AND p_schedule.end_time < p_schedule.start_time THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The end time cannot be sooner than the start time of the job'); END IF; IF p_schedule.frequency_code=INTERVAL_FREQUENCY_CODE THEN -- Note: we will allow an interval of 0. This means the next -- execution will be scheduled as soon as the previous one ends IF p_schedule.interval < 0 THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Invalid value for interval: ' || p_schedule.interval); END IF; -- The start grace period has to be less than the max of the interval or 1 day IF p_schedule.interval > 1439 THEN l_max_interval := 1439; ELSE l_max_interval := p_schedule.interval-1; END IF; IF (p_schedule.start_grace_period != -1) AND ((p_schedule.start_grace_period < 1) OR (p_schedule.start_grace_period > l_max_interval)) THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The start grace period should be greater than 0 minutes and less than the greater of the interval specified or a day.'); END IF; RETURN; END IF; -- The start grace period has to be greater than or equal to 10 minutes and less than a day IF (p_schedule.start_grace_period != -1) AND ((p_schedule.start_grace_period < 10) OR (p_schedule.start_grace_period > 1439)) THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The start grace period should be greater than or equal to 10 minutes and less than a day.'); END IF; -- Check the validity of the execution_hours and execution_minutes IF p_schedule.execution_hours < 0 OR p_schedule.execution_hours > 23 THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'execution_hours must be between 0 and 23'); END IF; IF p_schedule.execution_minutes < 0 OR p_schedule.execution_hours > 59 THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'execution_minutes must be between 0 and 59'); END IF; -- If we're here, we're done validating daily schedules IF p_schedule.frequency_code=DAILY_FREQUENCY_CODE THEN -- for backward compatiblity convert invalid interval to 1 IF p_schedule.interval IS NULL OR p_schedule.interval < 1 THEN p_schedule.interval := 1; END IF; RETURN; END IF; IF p_schedule.days IS NULL OR p_schedule.days.count=0 THEN -- All other schedule types require days to be specified raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The days array cannot be empty for this type of schedule'); END IF; IF p_schedule.frequency_code=WEEK_FREQUENCY_CODE THEN FOR i IN 1..p_schedule.days.count LOOP IF p_schedule.days(i) < 1 OR p_schedule.days(i) > 7 THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Invalid value (' || p_schedule.days(i) || ') for day of week, must be between 1 and 7'); END IF; END LOOP; RETURN; ELSIF p_schedule.frequency_code=MONTH_FREQUENCY_CODE OR p_schedule.frequency_code=YEAR_FREQUENCY_CODE THEN FOR i IN 1..p_schedule.days.count LOOP IF p_schedule.days(i) != -1 AND (p_schedule.days(i) < 1 OR p_schedule.days(i) > 31) THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Invalid value (' || p_schedule.days(i) || ') for day of month, must be between 1 and 31'); END IF; END LOOP; IF p_schedule.frequency_code=MONTH_FREQUENCY_CODE THEN RETURN; END IF; END IF; IF p_schedule.frequency_code=YEAR_FREQUENCY_CODE THEN IF p_schedule.months IS NULL OR p_schedule.months.count=0 THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The months array cannot be empty for day-of-year schedules'); ELSIF p_schedule.months.count != p_schedule.days.count THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'The days and months arrays must be of equal size for day-of-year schedules'); END IF; -- Validate the months FOR i IN 1..p_schedule.months.count LOOP IF p_schedule.months(i) < 1 OR p_schedule.months(i) > 12 THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Invalid value (' || p_schedule.months(i) || ') for month in day-of-year schedule'); END IF; END LOOP; ELSE raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Invalid schedule type'); END IF; END; /** * Calculate the next start time for a weekly schedule. All times must be * specified in the same timezone. * @param p_scheduled_days Array with days the job should run. 1=> Sunday, * 7=> Saturday * @param p_first_time Whether this calculation is for the first execution * @param p_current_time Current time (basis for determining the next time) * @param p_last_start_time Last expected start time of the same schedule. This * is assumed to be within a week of the current time * @param p_grace_interval The grace interval (fraction in days) for this schedule */ FUNCTION get_next_week_start_time(p_scheduled_days MGMT_JOB_INT_ARRAY, p_first_time BOOLEAN, p_current_time DATE, p_last_start_time DATE, p_grace_interval NUMBER) RETURN DATE IS -- The day of the week that the last execution ran on l_last_start_dow INTEGER := day_of_week(p_last_start_time); l_max_days_in_week CONSTANT INTEGER := 7; -- The possible start time holds the start time on the current schedule day that -- is within the same week as the last start time (week beginning on Sunday) -- For instance, if the schedule has days Sun, Fri and Wed, and the job last ran -- on Wed (10th), the possible start time would initially contain Sun (7th), Fri -- (12th) and Wed (10th) while looping across the scheduled days. l_possible_start_time DATE; l_next_start_time DATE; BEGIN -- Loop over the scheduled days and find the nearest time we may schedule -- the execution. FOR i in 1..p_scheduled_days.count LOOP l_possible_start_time := p_last_start_time - l_last_start_dow + p_scheduled_days(i); -- If the possible start time is past, we advance it to the next -- week. Similarly, if this is not the first schedule and we calculated -- the possible start time to be same as the current time, we move it -- ahead by a week. IF (p_current_time > (l_possible_start_time + p_grace_interval)) OR (NOT p_first_time AND (l_possible_start_time = p_current_time OR l_possible_start_time = p_last_start_time)) THEN l_possible_start_time := l_possible_start_time + l_max_days_in_week; END IF; -- Pick the earliest start time among the ones calculated IF l_next_start_time IS NULL THEN l_next_start_time := l_possible_start_time; ELSE l_next_start_time := LEAST(l_next_start_time, l_possible_start_time); END IF; END LOOP; RETURN l_next_start_time; END; /** * Calculate the next start time for a monthly schedule. All times must be * specified in the same timezone. * @param p_scheduled_days Array with days of the month the job should run. * @param p_first_time Whether this calculation is for the first execution * @param p_current_time Current time (basis for determining the next time) * @param p_last_start_time Last expected start time of the same schedule. This * is assumed to be within a month of the current time * @param p_grace_interval The grace interval (fraction in days) for this schedule */ FUNCTION get_next_month_start_time(p_scheduled_days MGMT_JOB_INT_ARRAY, p_first_time BOOLEAN, p_current_time DATE, p_last_start_time DATE, p_grace_interval NUMBER) RETURN DATE IS -- The day of the month that the last execution ran on l_last_start_dom INTEGER := EXTRACT(DAY FROM p_last_start_time); l_last_day_of_last_month INTEGER := EXTRACT(DAY FROM LAST_DAY(p_last_start_time)); l_scheduled_day INTEGER; l_possible_start_time DATE; l_next_start_time DATE; BEGIN -- The logic is similar to that of weekly schedule except we move by -- months. Additionally, we have to check for last-day-of-month schedules -- (indicated by a -1) and move to the end of the month FOR i in 1..p_scheduled_days.count LOOP IF p_scheduled_days(i) = -1 THEN l_scheduled_day := 32; -- max days in month + 1 ELSE l_scheduled_day := p_scheduled_days(i); END IF; l_possible_start_time := p_last_start_time - l_last_start_dom + LEAST(l_scheduled_day, l_last_day_of_last_month); IF (p_current_time > (l_possible_start_time + p_grace_interval)) OR (NOT p_first_time AND (l_possible_start_time = p_current_time OR l_possible_start_time = p_last_start_time)) THEN -- move ahead a month -- First, move to last day of next month l_possible_start_time := LAST_DAY(ADD_MONTHS(l_possible_start_time, 1)); -- Calculate the day, now that we are in the correct month. Remove -- number of days in the month and add the scheduled day. While -- adding, ensure we remain in the same month l_possible_start_time := l_possible_start_time - EXTRACT(DAY FROM l_possible_start_time) + LEAST(l_scheduled_day, EXTRACT(DAY FROM l_possible_start_time)); END IF; IF l_next_start_time IS NULL THEN l_next_start_time := l_possible_start_time; ELSE l_next_start_time := LEAST(l_next_start_time, l_possible_start_time); END IF; END LOOP; RETURN l_next_start_time; END; /** * Calculate the next start time for a yearly schedule. All times must be * specified in the same timezone. * @param p_scheduled_months Array with months the job should run (1->Jan, 12->Dec). * @param p_scheduled_days Array with days of the month the job should run. * @param p_first_time Whether this calculation is for the first execution * @param p_current_time Current time (basis for determining the next time) * @param p_last_start_time Last expected start time of the same schedule. This * is assumed to be within a month of the current time * @param p_grace_interval The grace interval (fraction in days) for this schedule */ FUNCTION get_next_year_start_time(p_scheduled_months MGMT_JOB_INT_ARRAY, p_scheduled_days MGMT_JOB_INT_ARRAY, p_first_time BOOLEAN, p_current_time DATE, p_last_start_time DATE, p_grace_interval NUMBER) RETURN DATE IS l_months_in_year CONSTANT INTEGER := 12; l_epoch DATE := p_last_start_time; l_scheduled_day INTEGER; l_scheduled_month INTEGER; l_possible_start_time DATE; l_next_start_time DATE; BEGIN -- move to beginning of month of last start time -> 01-MON-YYYY l_epoch := l_epoch + 1 - EXTRACT(DAY FROM l_epoch); -- move to beginning of year -> 01-JAN-YYYY l_epoch := ADD_MONTHS(l_epoch, 1 - EXTRACT(MONTH FROM l_epoch)); FOR i in 1..p_scheduled_months.count LOOP IF p_scheduled_days(i) = -1 THEN l_scheduled_day := 32; -- max days in month + 1 ELSE l_scheduled_day := p_scheduled_days(i); END IF; -- scheduled month must be between 1 and 12 l_scheduled_month := LEAST(GREATEST(p_scheduled_months(i), 1), l_months_in_year); -- epoch is already 01 JAN, so add one less month and one less day l_possible_start_time := ADD_MONTHS(l_epoch, l_scheduled_month - 1); l_possible_start_time := l_possible_start_time - 1 + LEAST(l_scheduled_day, EXTRACT(DAY FROM LAST_DAY(l_possible_start_time))); IF (p_current_time > (l_possible_start_time + p_grace_interval)) OR (NOT p_first_time AND (l_possible_start_time = p_current_time OR l_possible_start_time = p_last_start_time)) THEN -- move ahead a year -- re-calculate to account for leap year and similar quirks l_possible_start_time := ADD_MONTHS(l_epoch, l_months_in_year + l_scheduled_month - 1); l_possible_start_time := l_possible_start_time - 1 + LEAST(l_scheduled_day, EXTRACT(DAY FROM LAST_DAY(l_possible_start_time))); END IF; IF l_next_start_time IS NULL THEN l_next_start_time := l_possible_start_time; ELSE l_next_start_time := LEAST(l_next_start_time, l_possible_start_time); END IF; END LOOP; RETURN l_next_start_time; END; -- Return the next execution time in utc. Return null -- if the job does not have any further executions. -- p_last_start_time represents the last start time of the execution, -- and is in utc. FUNCTION get_next_execution_time(p_schedule MGMT_JOB_SCHEDULE_RECORD, p_last_start_time DATE, p_tzregion VARCHAR2, p_current_time DATE DEFAULT NULL) RETURN DATE IS l_interval NUMBER; l_current_time DATE := NVL(p_current_time, SYSDATE_UTC()); -- Convert the value into a DATE object in the repository time zone l_last_start_time DATE := p_last_start_time; l_first_time BOOLEAN := false; -- Note: Since the schedule has been specified in local -- times, we need to use local times in most of our -- calculations l_last_start_time_local DATE; l_current_time_local DATE; l_next_start_time_local DATE; l_grace_time_interval NUMBER := mins_to_interval(GREATEST(NVL(p_schedule.start_grace_period, 0), 0)); BEGIN -- BUG 5895148, when we assign start_time to l_next_start_time_local we can -- get SQlException SQLCODE = -01878 in MGMT_GLOBAL.to_utc if start_time is -- between the DST time jump. This exception is caught and processed by -- daylight_adjusted_local_time() which processes l_next_start_time_local. -- NOTE: This default may not apply correctly for some of the schedule -- types, so it is better to re-initialize this value for those schedules. l_next_start_time_local := p_schedule.start_time; IF l_last_start_time IS NULL THEN l_first_time := true; IF p_schedule.frequency_code = IMMEDIATE_FREQUENCY_CODE THEN RETURN l_current_time; ELSIF p_schedule.frequency_code = ONE_TIME_FREQUENCY_CODE THEN RETURN MGMT_GLOBAL.to_utc(l_next_start_time_local, p_tzregion); END IF; l_last_start_time := MGMT_GLOBAL.to_utc(l_next_start_time_local, p_tzregion); END IF; -- If we had an execution scheduled in the future and it was stopped and -- we got here, we want to set the current time to the last exec start time. l_current_time := GREATEST(l_last_start_time, l_current_time); l_last_start_time_local := MGMT_GLOBAL.from_utc(l_last_start_time, p_tzregion); l_current_time_local := MGMT_GLOBAL.from_utc(l_current_time, p_tzregion); -- Interval, where applicable, must be at least 0 l_interval := GREATEST(NVL(p_schedule.interval, 0), 0); IF p_schedule.frequency_code = IMMEDIATE_FREQUENCY_CODE OR p_schedule.frequency_code = ONE_TIME_FREQUENCY_CODE THEN -- If we're here, this is the second or subsequent execution -- For one-time schedules, always return null -- on the second call RETURN null; ELSIF p_schedule.frequency_code = INTERVAL_FREQUENCY_CODE THEN -- NOTE: Only UTC calculations for interval frequency IF l_first_time THEN RETURN l_last_start_time; ELSE -- Use UTC here RETURN adjust_utc_start_time(p_schedule, l_last_start_time+mins_to_interval(l_interval), p_tzregion); END IF; -- For the rest of the schedule types, we need to calculate the local time. -- We store the calculated time in l_next_start_time_local and invoke -- adjust_local_start_time on it. In case of any errors in DST conversion, -- daylight_adjusted_local_time will calculate the correct local time based -- on the value of l_next_start_time_local. ELSIF p_schedule.frequency_code=DAILY_FREQUENCY_CODE THEN IF l_first_time THEN -- Schedule it on the day of the start time, at the specified -- start time l_next_start_time_local := l_last_start_time_local; ELSE -- Simply add the days to the last start time! -- NOTE: Minimum interval is 1 day l_next_start_time_local := l_last_start_time_local+GREATEST(l_interval, 1); END IF; ELSIF p_schedule.frequency_code=WEEK_FREQUENCY_CODE THEN l_next_start_time_local := get_next_week_start_time(p_schedule.days, l_first_time, l_current_time_local, l_last_start_time_local, l_grace_time_interval); ELSIF p_schedule.frequency_code=MONTH_FREQUENCY_CODE THEN l_next_start_time_local := get_next_month_start_time(p_schedule.days, l_first_time, l_current_time_local, l_last_start_time_local, l_grace_time_interval); ELSIF p_schedule.frequency_code=YEAR_FREQUENCY_CODE THEN l_next_start_time_local := get_next_year_start_time(p_schedule.months, p_schedule.days, l_first_time, l_current_time_local, l_last_start_time_local, l_grace_time_interval); ELSE -- Shouldn't happen, since the schedule must have already -- been validated raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Invalid schedule type ' || p_schedule.frequency_code); END IF; RETURN adjust_local_start_time(p_schedule, l_next_start_time_local, p_tzregion); EXCEPTION WHEN OTHERS THEN BEGIN IF SQLCODE = -01878 THEN -- Daylight savings related fix -- Oracle throws ORA-01878 when we try to create -- a TIMESTAMP which falls between the time jump -- we catch the exception and adjust the time to -- next valid time -- we do that by incrementing the TIMESTAMP by -- 15 min iteratively RETURN daylight_adjusted_local_time(p_schedule, l_next_start_time_local, p_tzregion, true); ELSE RAISE; END IF; END; END; -- Generate a lock guid that is unique for the specified -- lock name and type. FUNCTION generate_lock_guid(p_lock_name VARCHAR2, p_lock_type NUMBER) RETURN RAW IS l_lock_name VARCHAR2(64); BEGIN IF p_lock_name IS NULL THEN l_lock_name := ''; ELSE l_lock_name := p_lock_name; END IF; RETURN DBMS_OBFUSCATION_TOOLKIT.md5( input => UTL_RAW.cast_to_raw(p_lock_type || ';'|| l_lock_name)); END; -- Acquire a lock for the specified execution, with the -- given name, type and mode FUNCTION acquire_execution_lock(p_execution_id RAW, p_job_id RAW, p_job_type_id RAW, p_lock_type NUMBER, p_lock_guid RAW, p_target_guid RAW, p_lock_mode_requested NUMBER, p_lock_index NUMBER, p_current_holder_out OUT RAW) RETURN BOOLEAN IS -- The execution that currently holds the requested lock l_current_holder MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; -- And its job type l_current_holder_jobtype MGMT_JOB.job_type%TYPE; l_current_holder_jobtype_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_current_lock_mode NUMBER; l_current_time DATE := SYSDATE_UTC(); l_job_type MGMT_JOB_TYPE_INFO.job_type%TYPE; BEGIN SELECT job_type INTO l_job_type FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=p_job_type_id; -- See if the lock is already held by someone else BEGIN IF p_lock_type=LOCKTYPE_GLOBAL THEN SELECT execution_id, el.job_type_id, el.lock_mode INTO l_current_holder, l_current_holder_jobtype_id, l_current_lock_mode FROM MGMT_JOB_EXEC_LOCKS el WHERE el.lock_guid=p_lock_guid AND el.lock_status=LOCKSTATUS_ACQUIRED AND ROWNUM=1; ELSIF p_lock_type=LOCKTYPE_TARGET_EXCLUSIVE THEN -- Note: I cannot acquire a target exclusive lock if -- any lock on that target is being held SELECT execution_id, el.job_type_id, el.lock_mode INTO l_current_holder, l_current_holder_jobtype_id, l_current_lock_mode FROM MGMT_JOB_EXEC_LOCKS el WHERE el.target_guid=p_target_guid AND el.lock_status=LOCKSTATUS_ACQUIRED AND ROWNUM=1; ELSIF p_lock_type=LOCKTYPE_TARGET_NAMED THEN -- Note: I cannot acquire a named target lock if there -- is either an exclusive or named lock being held SELECT execution_id, el.job_type_id, el.lock_mode INTO l_current_holder, l_current_holder_jobtype_id, l_current_lock_mode FROM MGMT_JOB_EXEC_LOCKS el, MGMT_JOB_LOCK_INFO li WHERE li.lock_guid=el.lock_guid AND (li.lock_guid=p_lock_guid OR li.lock_type=LOCKTYPE_TARGET_EXCLUSIVE) AND el.target_guid=p_target_guid AND el.lock_status=LOCKSTATUS_ACQUIRED AND ROWNUM=1; END IF; SELECT job_type INTO l_current_holder_jobtype FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_current_holder_jobtype_id; IF l_current_holder=p_execution_id THEN -- If the current execution holds the lock, we're done RETURN TRUE; END IF; p_current_holder_out := l_current_holder; IF l_current_lock_mode=LOCKMODE_SHARED_EXCLUSIVE AND p_lock_mode_requested=LOCKMODE_SHARED_EXCLUSIVE AND l_job_type=l_current_holder_jobtype THEN NULL; ELSE -- We cannot acquire this lock RETURN FALSE; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- It is OK to acquire the lock l_current_holder := null; END; -- If we are here, an existing entry for the lock was not found; -- go ahead and put one in INSERT INTO MGMT_JOB_EXEC_LOCKS(execution_id, job_id, job_type_id, lock_guid, target_guid, lock_mode, lock_status, lock_request_time, lock_acquired_time, lock_index) VALUES (p_execution_id, p_job_id, p_job_type_id, p_lock_guid, p_target_guid, p_lock_mode_requested, LOCKSTATUS_ACQUIRED, l_current_time, l_current_time, p_lock_index); RETURN TRUE; END; -- Throw an exception stating that the job system was not -- able to obtain a lock, with an appropriate message PROCEDURE raise_lock_exception(p_lock_name VARCHAR2, p_lock_type VARCHAR2, p_target_name VARCHAR2, p_target_type VARCHAR2, p_current_holder RAW) IS BEGIN IF p_target_name IS NULL THEN raise_application_error(MGMT_GLOBAL.EXEC_LOCK_ERR, 'Unable to acquire global lock ' || p_lock_name || ':' || 'current holder is ' || p_current_holder); ELSIF p_lock_name IS NULL THEN raise_application_error(MGMT_GLOBAL.EXEC_LOCK_ERR, 'Unable to acquire exclusive target lock on target ' || p_target_name || ':' || p_target_type || ':' || 'current holder is ' || p_current_holder); ELSE raise_application_error(MGMT_GLOBAL.EXEC_LOCK_ERR, 'Unable to acquire lock ' || p_lock_name || ' on target ' || p_target_name || ':' || p_target_type || ':' || 'current holder is ' || p_current_holder); END IF; END; -- Put in a request for all locks held by the execution PROCEDURE request_locks(p_execution_id RAW, p_job_id RAW, p_job_type_id RAW, p_locks MGMT_JOB_LOCK_ARRAY) IS l_current_time DATE := SYSDATE_UTC(); l_job_type MGMT_JOB_TYPE_INFO.job_type%TYPE; BEGIN FOR i IN 1..p_locks.COUNT LOOP INSERT INTO MGMT_JOB_EXEC_LOCKS(execution_id, job_id, job_type_id, lock_guid, target_guid, lock_mode, lock_status, lock_request_time, lock_acquired_time, lock_index) VALUES (p_execution_id, p_job_id, p_job_type_id, p_locks(i).lock_guid, p_locks(i).target_guid, p_locks(i).lock_mode, LOCKSTATUS_WAITING, l_current_time, null, i); END LOOP; END; -- Cleanup by releasing all locks currently held by the execution PROCEDURE cleanup_locks(p_execution_id RAW) IS BEGIN DELETE FROM MGMT_JOB_EXEC_LOCKS WHERE execution_id=p_execution_id AND lock_status=LOCKSTATUS_ACQUIRED; END; -- Retrieve lock information from requests FUNCTION get_lock_info_from_requests(p_job_id RAW, p_execution_id RAW, p_job_type_id_out OUT RAW, p_lock_action_out OUT NUMBER) RETURN MGMT_JOB_LOCK_ARRAY IS CURSOR get_requested_locks (p_execution_id RAW) IS SELECT MGMT_JOB_LOCK_RECORD(el.lock_guid, li.lock_name, li.lock_type, el.lock_mode, target_name, target_type, el.target_guid) FROM MGMT_JOB_EXEC_LOCKS el, MGMT_JOB_LOCK_INFO li, MGMT_TARGETS t WHERE el.execution_id=p_execution_id AND el.lock_guid=li.lock_guid AND el.job_type_id=li.job_type_id AND el.target_guid=t.target_guid (+) AND el.lock_status=LOCKSTATUS_WAITING ORDER BY el.lock_index; l_locks MGMT_JOB_LOCK_ARRAY; BEGIN OPEN get_requested_locks(p_execution_id); FETCH get_requested_locks BULK COLLECT INTO l_locks; p_job_type_id_out := get_job_type_id(p_job_id, p_execution_id); p_lock_action_out := LOCK_SUSPEND_ACTION; RETURN l_locks; END; -- Retrieve lock information from metadata FUNCTION get_lock_info_from_metadata(p_job_id RAW, p_execution_id RAW, p_job_type_id_out OUT RAW, p_lock_action_out OUT NUMBER) RETURN MGMT_JOB_LOCK_ARRAY IS l_lock_action NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_lock_names SMP_EMD_STRING_ARRAY; l_lock_types SMP_EMD_INTEGER_ARRAY; l_lock_modes SMP_EMD_INTEGER_ARRAY; l_lock_guids MGMT_USER_GUID_ARRAY; l_target_names_params SMP_EMD_STRING_ARRAY; l_target_types_params SMP_EMD_STRING_ARRAY; l_fail_target_guid RAW(16); l_locks MGMT_JOB_LOCK_ARRAY; N INTEGER := 1; l_targets SMP_EMD_NVPAIR_ARRAY; l_target_names MGMT_JOB_VECTOR_PARAMS; l_target_types MGMT_JOB_VECTOR_PARAMS; l_target_guids MGMT_USER_GUID_ARRAY; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; BEGIN get_job_name_and_owner(p_job_id, l_job_name, l_job_owner); l_job_type_id := get_job_type_id(p_job_id, p_execution_id); SELECT lock_action INTO l_lock_action FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; p_job_type_id_out := l_job_type_id; p_lock_action_out := l_lock_action; SELECT lock_name, lock_type, lock_mode, lock_guid, target_names_param, target_types_param BULK COLLECT INTO l_lock_names, l_lock_types, l_lock_modes, l_lock_guids, l_target_names_params, l_target_types_params FROM MGMT_JOB_LOCK_INFO WHERE job_type_id=l_job_type_id; IF l_lock_types IS NULL OR l_lock_types.count=0 THEN RETURN null; END IF; l_locks := MGMT_JOB_LOCK_ARRAY(); -- Loop thru each lock and collect information about it FOR i IN 1..l_lock_types.COUNT LOOP IF l_lock_types(i)=LOCKTYPE_GLOBAL THEN l_locks.extend(1); l_locks(N) := MGMT_JOB_LOCK_RECORD(l_lock_guids(i), l_lock_names(i), l_lock_types(i), l_lock_modes(i), null, null, null); N := N+1; ELSE -- Construct a list of target guids from the specified -- target names and types SELECT target_name, target_type BULK COLLECT INTO l_target_names, l_target_types FROM MGMT_JOB_LOCK_TARGETS WHERE lock_guid=l_lock_guids(i) AND job_type_id=l_job_type_id ORDER BY target_index; handle_target_params(l_target_names_params(i), l_target_types_params(i), l_target_names, l_target_types, p_job_id, p_execution_id, null, l_job_name, l_job_owner, false, MGMT_GLOBAL.INVALID_PARAMS_IN_LOCK_ERR); reset_params(); l_target_guids := MGMT_USER_GUID_ARRAY(); l_target_guids.extend(l_target_names.COUNT); FOR j IN 1..l_target_names.count LOOP BEGIN SELECT target_guid INTO l_target_guids(j) FROM MGMT_TARGETS WHERE target_name=l_target_names(j) AND target_type=l_target_types(j); l_locks.extend(1); l_locks(N) := MGMT_JOB_LOCK_RECORD(l_lock_guids(i), l_lock_names(i), l_lock_types(i), l_lock_modes(i), l_target_names(j), l_target_types(j), l_target_guids(j)); N := N+1; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'Could not acquire lock ' || l_lock_names(i) || ': Invalid target ' || l_target_names(j) || ':' || l_target_types(j)); END; END LOOP; END IF; END LOOP; RETURN l_locks; -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Obtain locks needed by the execution. If the locks cannot be -- obtained, perform the appropriate action (suspend/abort the -- execution) p_retry is true if this execution is already suspended -- and is retrying the locks. -- This function returns the new status of the execution -- (ABORTED/SUSPENDED/EXECUTING) FUNCTION get_execution_locks(p_job_id RAW, p_execution_id RAW, p_retry BOOLEAN DEFAULT FALSE) RETURN NUMBER IS l_locks MGMT_JOB_LOCK_ARRAY; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_lock_action NUMBER; l_current_holder RAW(16); l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN IF p_retry THEN l_locks := get_lock_info_from_requests(p_job_id, p_execution_id, l_job_type_id, l_lock_action); ELSE l_locks := get_lock_info_from_metadata(p_job_id, p_execution_id, l_job_type_id, l_lock_action); END IF; IF l_locks IS NULL OR l_locks.COUNT=0 THEN RETURN EXECUTING_STATUS; END IF; LOCK TABLE MGMT_JOB_EXEC_LOCKS IN EXCLUSIVE MODE; -- Now we have information about all locks needed by the execution. -- Go ahead and start trying to acquire the locks FOR i IN 1..l_locks.COUNT LOOP IF NOT acquire_execution_lock(p_execution_id, p_job_id, l_job_type_id, l_locks(i).lock_type, l_locks(i).lock_guid, l_locks(i).target_guid, l_locks(i).lock_mode, i, l_current_holder) THEN -- Release all locks obtained so far... cleanup_locks(p_execution_id); IF l_lock_action = LOCK_SUSPEND_ACTION THEN -- Put in requests for each lock, if not already present IF NOT p_retry THEN request_locks(p_execution_id, p_job_id, l_job_type_id, l_locks); END IF; suspend_job_execution(p_execution_id, SUSPENDED_LOCK_STATUS, 0); RETURN SUSPENDED_LOCK_STATUS; ELSE -- Raise an exception; this will automatically cause -- the calling layer to abort the execution raise_lock_exception(l_locks(i).lock_name, l_locks(i).lock_type, l_locks(i).target_name, l_locks(i).target_type, l_current_holder); END IF; END IF; END LOOP; -- If we were able to obtain all requested locks, then -- remove all requests and resume the execution if required DELETE FROM MGMT_JOB_EXEC_LOCKS WHERE execution_id=p_execution_id AND lock_status=LOCKSTATUS_WAITING; l_start_grace_period := get_start_grace_period(p_job_id); IF p_retry THEN -- This execution is currently suspended; resume it resume_job_execution(p_execution_id, SUSPENDED_LOCK_STATUS, l_start_grace_period); END IF; RETURN EXECUTING_STATUS; END; -- Release all the locks obtained by the execution PROCEDURE release_execution_locks(p_execution_id RAW, p_job_type_id RAW) IS CURSOR get_waiters(p_execution_id RAW) IS SELECT DISTINCT execution_id, job_id FROM (SELECT el1.execution_id, el1.job_id FROM MGMT_JOB_EXEC_LOCKS el1, MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB_LOCK_INFO li1 WHERE el1.execution_id=e.execution_id AND el1.lock_guid=li1.lock_guid AND el1.job_type_id=li1.job_type_id AND lock_status=LOCKSTATUS_WAITING AND EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_LOCKS el2, MGMT_JOB_LOCK_INFO li2 WHERE el2.lock_guid=li2.lock_guid AND el2.job_type_id=li2.job_type_id AND el2.execution_id=p_execution_id AND (el2.lock_guid=el1.lock_guid OR li2.lock_type=LOCKTYPE_TARGET_EXCLUSIVE OR li1.lock_type=LOCKTYPE_TARGET_EXCLUSIVE) AND ( (el2.target_guid IS NULL AND el1.target_guid IS NULL) OR (el1.target_guid=el2.target_guid) ) ) ORDER BY lock_request_time); l_lock_guid MGMT_JOB_LOCK_INFO.lock_guid%TYPE; l_target_guid MGMT_TARGETS.target_guid%TYPE; l_lock_mode NUMBER; l_refcount NUMBER; l_lock_type NUMBER; l_waiter_eids MGMT_USER_GUID_ARRAY; l_waiter_jids MGMT_USER_GUID_ARRAY; l_lock_count NUMBER; l_status NUMBER; BEGIN -- See if this execution is holding any locks at all SELECT COUNT(*) INTO l_lock_count FROM MGMT_JOB_EXEC_LOCKS WHERE execution_id=p_execution_id; IF l_lock_count=0 THEN RETURN; END IF; LOCK TABLE MGMT_JOB_EXEC_LOCKS IN EXCLUSIVE MODE; -- First get a list of waiting executions OPEN get_waiters(p_execution_id); FETCH get_waiters BULK COLLECT INTO l_waiter_eids, l_waiter_jids; -- Give up (delete) all locks held by this execution cleanup_locks(p_execution_id); -- Clean up all requested locks as well. This could happen if -- an execution is currently suspended on a lock and is stopped DELETE FROM MGMT_JOB_EXEC_LOCKS WHERE execution_id=p_execution_id AND lock_status=LOCKSTATUS_WAITING; -- Loop over each waiting execution and try to acquire its locks FOR i IN 1..l_waiter_eids.COUNT LOOP -- Note: all waiters should have their lock action set -- to suspend. So this method should never raise any -- exceptions. l_status := get_execution_locks(l_waiter_jids(i), l_waiter_eids(i), true); END LOOP; END; -- Copy over parameters from the source ca to the destination ca. -- Should be used only when making copies of CAs PROCEDURE copy_ca_params(p_source_ca_id RAW, p_dest_ca_id RAW) IS BEGIN -- First, copy all the scalar and vector parameters INSERT INTO MGMT_JOB_PARAMETER ( job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit) SELECT p_dest_ca_id, NO_EXECUTION, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit FROM MGMT_JOB_PARAMETER WHERE job_id=p_source_ca_id AND execution_id=NO_EXECUTION AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); -- Make deep copies of large parameters copy_large_params(p_dest_ca_id, p_source_ca_id); END; -- Copy over parameters at the job level to the specified execution PROCEDURE copy_job_params(p_source_job_id RAW, p_source_execution_id RAW, p_dest_job_id RAW, p_dest_execution_id RAW) IS BEGIN INSERT INTO MGMT_JOB_PARAMETER ( job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit) SELECT p_dest_job_id, p_dest_execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit FROM MGMT_JOB_PARAMETER WHERE job_id=p_source_job_id AND execution_id=p_source_execution_id; END; -- Insert execution params, run param sources as required PROCEDURE insert_execution_params(p_job_id RAW, p_execution_id RAW, p_source_exec_id RAW, p_status NUMBER) IS l_status NUMBER := p_status; l_source_job_id MGMT_JOB.job_id%TYPE; l_creds_set NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_is_ca MGMT_JOB.is_corrective_action%TYPE; BEGIN -- Insert the targets for this execution IF p_source_exec_id IS NOT NULL THEN l_status := EXECUTING_STATUS; SELECT job_id INTO l_source_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_source_exec_id; -- Copy parameters over from the source execution copy_job_params(p_job_id, p_source_exec_id, p_job_id, p_execution_id); ELSE -- Copy parameters over from the job definition copy_job_params(p_job_id, NO_EXECUTION, p_job_id, p_execution_id); END IF; -- Invoke any submission-time parameter sources that have been defined IF p_source_exec_id IS NULL THEN l_creds_set := fetch_job_parameters(p_job_id, p_execution_id, null, null, 1); ELSE l_creds_set := fetch_job_parameters(p_job_id, p_execution_id, null, null, 1, STEPTYPE_PARAMSRC_RETRY_EXEC); END IF; -- For multitask jobs, compute multitask credential info l_job_type_id := get_job_type_id(p_job_id, p_execution_id); SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; SELECT is_corrective_action INTO l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id; IF l_is_ca=0 THEN IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN compute_mtjob_cred_info(p_job_id, p_execution_id, l_job_type_id); ELSE -- Account for execution time parameter sources that -- require credentials. Do not actually insert the -- parameters, but suspend execution if required l_creds_set := compute_cred_info(p_job_id, p_execution_id, null, 0, 0); END IF; END IF; -- Check security. This must be done for restart as well.... check_security_info(p_job_id, p_execution_id, 1); -- For restarts, do the runtime security checks here as well IF p_source_exec_id IS NOT NULL THEN check_security_info(p_job_id, p_execution_id, 0); -- Obtain locks l_status := get_execution_locks(p_job_id, p_execution_id); END IF; END; -- Insert one execution. p_source_exec_id is non-null if this is a -- restart execution. Parameter sources are not invoked for -- restart executions. Returns the execution id of the job FUNCTION insert_execution(p_job_id RAW, p_target_list_index INTEGER, p_target_guid RAW, p_source_exec_id RAW, p_expected_start_time DATE DEFAULT NULL, p_start_time DATE DEFAULT NULL, p_end_time DATE DEFAULT NULL, p_status NUMBER DEFAULT SCHEDULED_STATUS, p_status_detail NUMBER DEFAULT 0, p_triggering_severity RAW DEFAULT NULL, p_schedule MGMT_JOB_SCHEDULE_RECORD DEFAULT NULL) RETURN RAW IS l_execution_id RAW(16); l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_triggering_severity MGMT_JOB_EXEC_SUMMARY.triggering_severity%TYPE; l_source_job_id MGMT_JOB.job_id%TYPE; BEGIN -- Get a new execution id l_execution_id := SYS_GUID(); -- Figure out the job type id to use IF p_source_exec_id IS NOT NULL THEN -- For retries, always use the same job type id as the source SELECT job_type_id, triggering_severity INTO l_job_type_id, l_triggering_severity FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_source_exec_id; ELSE -- Use the most recent minor version l_job_type_id := get_job_type_id(p_job_id); l_triggering_severity := p_triggering_severity; END IF; -- Insert a row in the execution summary table for this execution INSERT INTO MGMT_JOB_EXEC_SUMMARY(job_id, job_type_id, execution_id, expected_start_time, start_time, end_time, source_execution_id, target_list_index, status, status_detail, triggering_severity) VALUES (p_job_id, l_job_type_id, l_execution_id, p_expected_start_time, p_start_time, p_end_time, decode(p_source_exec_id, null, l_execution_id, p_source_exec_id), p_target_list_index, p_status, p_status_detail, l_triggering_severity); -- Populate targets IF p_source_exec_id IS NOT NULL THEN -- Copy targets over from the source execution SELECT job_id INTO l_source_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_source_exec_id; INSERT INTO MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) SELECT job_id, l_execution_id, target_list_index, target_guid, target_index FROM MGMT_JOB_TARGET WHERE job_id=l_source_job_id AND execution_id=p_source_exec_id; ELSE IF p_target_guid IS NOT NULL THEN -- Single target job INSERT INTO MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) VALUES (p_job_id, l_execution_id, p_target_list_index, p_target_guid, 1); ELSE -- Copy targets over from the job definition INSERT INTO MGMT_JOB_TARGET(job_id, execution_id, target_list_index, target_guid, target_index) SELECT job_id, l_execution_id, target_list_index, target_guid, target_index FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND target_list_index=p_target_list_index; END IF; END IF; -- If this is a WAITING execution, do NOT insert parameters. -- They will be inserted later, when the waiting execution is converted -- to a scheduled execution IF p_status NOT IN (WAITING_STATUS, SKIPPED_STATUS) THEN insert_execution_params(p_job_id, l_execution_id, p_source_exec_id, p_status); END IF; RETURN l_execution_id; END; -- Insert a new execution at the expected start time specified -- if one does not already exist. If one exists, update it. -- Note. This routine assumes that the execution is locked -- before it is called. FUNCTION insert_or_update_execution(p_job_id RAW, p_source_exec_id RAW, p_target_list_index INTEGER, p_target_guid RAW, p_expected_start_time DATE, p_start_time DATE DEFAULT NULL, p_end_time DATE DEFAULT NULL, p_status NUMBER DEFAULT SCHEDULED_STATUS, p_status_detail NUMBER DEFAULT 0, p_triggering_severity RAW DEFAULT NULL, p_schedule MGMT_JOB_SCHEDULE_RECORD DEFAULT NULL, p_current_time DATE DEFAULT NULL) RETURN RAW IS l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_current_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_status_detail MGMT_JOB_EXEC_SUMMARY.status_detail%TYPE; l_start_time MGMT_JOB_EXEC_SUMMARY.start_time%TYPE; l_existing_exec boolean := true; l_timezone_region MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; BEGIN BEGIN -- Is there is an execution at the expected start time specified? OR -- is there an existing waiting execution when we are trying to add one? -- As a specific target list index may have at most one waiting -- execution, look for existing waiting executions before inserting one. SELECT execution_id, status, status_detail INTO l_execution_id, l_current_status, l_status_detail FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id = p_job_id AND target_list_index = p_target_list_index AND ((p_status = WAITING_STATUS AND status = WAITING_STATUS) OR expected_start_time = p_expected_start_time); EXCEPTION WHEN NO_DATA_FOUND THEN l_existing_exec := false; -- This can happen only in the CA case when there are -- multiple violations for the same metric/policy WHEN TOO_MANY_ROWS THEN l_existing_exec := false; END; IF l_existing_exec AND p_source_exec_id IS NULL AND p_triggering_severity IS NULL THEN -- For waiting execs, we may need to modify the expected start. Otherwise, -- the execution is fine as-is. -- TBD. should we update the end time? IF p_status != WAITING_STATUS AND l_current_status = p_status THEN return l_execution_id; END IF; IF l_status_detail = GRACE_WAITING_STATUS THEN l_start_time := p_current_time; ELSE l_start_time := p_start_time; END IF; -- delete the existing steps cleanup_execution(p_job_id, l_execution_id, true); UPDATE MGMT_JOB_EXEC_SUMMARY SET start_time = l_start_time, end_time = p_end_time, expected_start_time = p_expected_start_time, status = p_status, status_detail = p_status_detail, job_type_id=MGMT_JOB_ENGINE.get_job_type_id(p_job_id) WHERE execution_id = l_execution_id; -- Insert parameters IF p_status NOT IN (WAITING_STATUS, SKIPPED_STATUS) THEN insert_execution_params(p_job_id, l_execution_id, p_source_exec_id, p_status); END IF; ELSE l_execution_id := insert_execution(p_job_id, p_target_list_index, p_target_guid, p_source_exec_id, p_expected_start_time, l_start_time, p_end_time, p_status, p_status_detail, p_triggering_severity, p_schedule); l_timezone_region := compute_timezone_region(p_job_id, p_target_guid, p_target_list_index, p_schedule); IF p_status=SKIPPED_STATUS THEN UPDATE MGMT_JOB_EXEC_SUMMARY SET scheduled_time=MGMT_GLOBAL.from_utc(p_expected_start_time, l_timezone_region), start_time=p_expected_start_time, end_time=p_expected_start_time, timezone_region=l_timezone_region WHERE execution_id=l_execution_id; END IF; END IF; RETURN l_execution_id; END; -- Convert the specified timezone offset into a region -- that is recognized by the repository FUNCTION to_region(p_offset NUMBER) RETURN VARCHAR2 IS l_hours NUMBER; l_minutes NUMBER; BEGIN l_hours := TRUNC(p_offset /60); l_minutes := MOD(p_offset, 60); IF l_minutes < 0 THEN l_minutes := -l_minutes; END IF; RETURN l_hours || ':' || l_minutes; END; -- Return the timezone region from the schedule FUNCTION compute_timezone_region(p_job_id RAW, p_target_guid RAW, p_target_list_index NUMBER, p_schedule MGMT_JOB_SCHEDULE_RECORD) RETURN VARCHAR2 IS l_target_index NUMBER; l_tzregion MGMT_TARGETS.timezone_region%TYPE; BEGIN IF p_schedule.timezone_info = TIMEZONE_REPOSITORY THEN -- Use the timezone region of the repository. -- Depending on whether an actual timezone is set, -- we will get either a region name or an offset. RETURN GET_REPOSITORY_TIMEZONE; ELSIF p_schedule.timezone_info = TIMEZONE_TARGET THEN BEGIN -- Timezone target....fetch the target's timezone region l_target_index := p_schedule.timezone_target_index; -- Fetch the tz region of the target designated by the -- timezone_target_index IF p_target_guid IS NOT NULL THEN -- Single-target job SELECT timezone_region INTO l_tzregion FROM MGMT_TARGETS t, MGMT_JOB_FLAT_TARGETS ft WHERE job_id=p_job_id AND ft.target_guid=p_target_guid AND ft.target_guid=t.target_guid; ELSE SELECT timezone_region INTO l_tzregion FROM MGMT_TARGETS t, MGMT_JOB_TARGET jt WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND target_list_index=p_target_list_index AND t.target_guid=jt.target_guid AND target_index=l_target_index; END IF; RETURN l_tzregion; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'timezone_target_index in the schedule is invalid or references an invalid target'); END; ELSIF p_schedule.timezone_info = TIMEZONE_SPECIFIED THEN RETURN to_region(p_schedule.timezone_offset); ELSIF p_schedule.timezone_info = TIMEZONE_RGN_SPECIFIED THEN RETURN p_schedule.timezone_region; END IF; END; -- Lock the specified job, return current job status FUNCTION lock_job(p_job_id RAW) RETURN NUMBER IS l_status MGMT_JOB.job_status%TYPE; BEGIN SELECT job_status INTO l_status FROM MGMT_JOB WHERE job_id=p_job_id FOR UPDATE; RETURN l_status; END; -- Return the status of an execution, accounting for all -- the events that may cause the execution to be in one -- of the suspended states. p_status_in is the current status of -- the execution, not accounting for the suspended states FUNCTION get_execution_status(p_job_id RAW, p_execution_id RAW, p_source_exec_id RAW, p_target_list_index NUMBER, p_start_time DATE, p_status_in NUMBER, p_current_status NUMBER) RETURN NUMBER IS l_count NUMBER := 0; l_job_status MGMT_JOB.job_status%TYPE; l_agent_bound MGMT_JOB_TYPE_INFO.agent_bound%TYPE; l_blk_started_count NUMBER := 0; l_system_job MGMT_JOB.SYSTEM_JOB%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_creds_not_set_count NUMBER; BEGIN -- If the execution is in waiting status, return immediately IF p_status_in = WAITING_STATUS THEN RETURN p_status_in; END IF; -- Return suspended on no credentials if the execution -- is currently missing credentials SELECT COUNT(1) INTO l_creds_not_set_count FROM MGMT_JOB_EXEC_CRED_INFO WHERE execution_id=p_execution_id AND credentials_set=0; IF l_creds_not_set_count >0 THEN RETURN SUSPENDED_CREDS_STATUS; END IF; l_job_type_id := get_job_type_id(p_job_id, p_execution_id); -- Check if the job has been suspended. If so, the execution starts -- off suspended SELECT job_status, agent_bound, system_job INTO l_job_status, l_agent_bound, l_system_job FROM MGMT_JOB j, MGMT_JOB_TYPE_INFO i WHERE j.job_id=p_job_id AND i.job_type_id=l_job_type_id; -- For system jobs associated with a session ("interactive" jobs) -- agent-bound is always 0, whatever the job type says IF l_system_job=SYSTEM_JOB_SESSION THEN l_agent_bound := 0; END IF; IF l_job_status=JOB_STATUS_SUSPENDED THEN RETURN SUSPENDED_STATUS; END IF; IF l_job_status=JOB_STATUS_REASSIGNED THEN RETURN REASSIGNED_STATUS; END IF; -- Check if the execution has at least one associated blackout in STARTED state. -- If so, bypass the blackout check in order to allow the job to be restarted -- when it fails. SELECT COUNT(1) INTO l_blk_started_count FROM MGMT_JOB_BLACKOUT_ASSOC jba, MGMT_BLACKOUTS b WHERE jba.blackout_guid = b.blackout_guid AND jba.execution_id = p_execution_id AND b.blackout_status = MGMT_BLACKOUT.BLK_STATE_STARTED; IF l_blk_started_count = 0 AND l_system_job=0 THEN -- The blackout status takes precedence over everything. -- If the specified execution falls in a blackout window, the -- initial status is SUSPENDED:BLACKED_OUT SELECT COUNT(1) INTO l_count FROM MGMT_BLACKOUT_WINDOWS bw, MGMT_BLACKOUTS b, MGMT_JOB_EXT_TARGETS et WHERE et.job_id=p_job_id AND et.execution_id=p_execution_id AND et.target_list_index=p_target_list_index AND bw.blackout_guid=b.blackout_guid AND bw.target_guid=et.target_guid AND b.job_flag=0 AND utc_start_time <= p_start_time AND (utc_end_time IS NULL OR p_start_time <= utc_end_time); IF l_count > 0 THEN RETURN SUSPENDED_BLACKOUT_STATUS; END IF; END IF; -- No need to perform the agent down check if the execution -- is already in agentdown status IF l_agent_bound=1 AND p_current_status != AGENTDOWN_STATUS THEN -- Check whether one of the targets is unreacheable SELECT COUNT(1) INTO l_count FROM MGMT_JOB_EXT_TARGETS et, MGMT_CURRENT_AVAILABILITY a WHERE et.job_id=p_job_id AND et.execution_id=p_execution_id AND et.target_list_index=p_target_list_index AND et.target_guid=a.target_guid AND a.current_status=MGMT_GLOBAL.G_STATUS_UNREACHABLE; IF l_count > 0 THEN RETURN AGENTDOWN_STATUS; END IF; END IF; IF p_source_exec_id IS NOT NULL THEN RETURN EXECUTING_STATUS; END IF; RETURN p_status_in; END; -- Return the start time of the execution given the previous -- start time. This skips over periods in the past FUNCTION get_adjusted_start_time(p_job_id RAW, p_target_list_index INTEGER, p_target_guid RAW, p_source_exec_id RAW, p_start_time DATE, p_last_start_time DATE, p_current_time DATE, p_schedule MGMT_JOB_SCHEDULE_RECORD) RETURN DATE IS l_current_time DATE := NVL(p_current_time, SYSDATE_UTC); l_start_time DATE := p_start_time; l_last_start_time DATE := p_last_start_time; l_grace_interval NUMBER := 0; l_need_grace_period boolean := false; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_num_skipped_exec NUMBER(30); l_skip_count NUMBER := 0; BEGIN IF NVL(p_schedule.start_grace_period, NO_START_GRACE) != NO_START_GRACE THEN l_grace_interval := mins_to_interval(p_schedule.start_grace_period); END IF; IF p_schedule.frequency_code=IMMEDIATE_FREQUENCY_CODE THEN RETURN l_current_time; END IF; IF p_schedule.frequency_code=ONE_TIME_FREQUENCY_CODE THEN RETURN l_start_time; END IF; -- bug 4301435 when the job is submited in the past IF p_last_start_time IS NULL AND l_start_time < l_current_time THEN IF p_schedule.frequency_code = INTERVAL_FREQUENCY_CODE THEN -- we move to the last execution before current time. -- and then use the while loop to find the next execution time l_num_skipped_exec := floor((l_current_time - l_start_time) / mins_to_interval(p_schedule.interval)); l_last_start_time := l_start_time + (l_num_skipped_exec) * mins_to_interval(p_schedule.interval); ELSIF p_schedule.frequency_code = DAILY_FREQUENCY_CODE THEN -- bug 5890373 we move starttime to last execution before current time -- and then use the while loop to find the next execution time l_num_skipped_exec := floor((l_current_time - l_start_time) / p_schedule.interval); l_last_start_time := l_start_time + l_num_skipped_exec * p_schedule.interval; ELSIF p_schedule.frequency_code IN (WEEK_FREQUENCY_CODE, MONTH_FREQUENCY_CODE, YEAR_FREQUENCY_CODE) THEN -- we move l_last_start_time till the last day ahead of l_current_time -- and then use the while loop to find the next execution time l_last_start_time := l_start_time + floor(l_current_time - l_start_time) ; END IF; END IF; -- We can re-adjust the current time to be earlier by the grace interval, so -- the calculation for grace interval need not happen every iteration -- So the while condition is really (start_time + grace < current_time) l_current_time := l_current_time - l_grace_interval; -- Ensure that we skip executions in the past WHILE l_start_time < l_current_time LOOP l_tzregion := compute_timezone_region(p_job_id, p_target_guid, p_target_list_index, p_schedule); l_start_time := get_next_execution_time(p_schedule, l_last_start_time, l_tzregion, l_last_start_time); -- For jobs with repeating schedules, the execution time might -- be null if the schedule has expired IF l_start_time IS NULL THEN RETURN NULL; END IF; l_last_start_time := l_start_time; -- Do not loop for one-time schedules IF p_schedule.frequency_code=ONE_TIME_FREQUENCY_CODE THEN EXIT; END IF; -- Do not insert SKIPPED executions for one-time schedules -- and the first time we are scheduling an execution -- only insert the execution on the first skip. All subsequent skip -- due to l_start_time < l_current_time would be ignored IF p_schedule.frequency_code != ONE_TIME_FREQUENCY_CODE AND p_last_start_time IS NOT NULL AND l_skip_count = 0 THEN l_execution_id := insert_or_update_execution(p_job_id, null, p_target_list_index, p_target_guid, l_last_start_time, l_last_start_time, l_last_start_time + l_grace_interval, SKIPPED_STATUS, SKIPPED_SYSTEM, NULL, p_schedule, p_current_time); END IF; l_skip_count := l_skip_count + 1; END LOOP; RETURN l_start_time; END; -- Schedule an execution at the specified target list index. -- If p_execution_id is non-null, the execution is already -- inserted. If p_execution_id is null, this method attempts -- to insert an execution. In all cases, it returns the -- new (or old) execution id. -- p_queue_name, if non-null, is the name of the job queue -- to which the execution needs to be inserted -- If p_next_start_time is not null, then it is used as the -- start time of the execution. -- If p_target_guid is not null, it is used as the target -- for the execution (single-target jobs). -- Otherwise, the targets inserted at job level for the target -- list specified by p_target_list_index will be copied over -- If p_adjust_start_time is true, then adjust the -- p_next_start_time parameter with respect to the current -- time. If not, don't do the adjustment FUNCTION schedule_execution(p_job_id RAW, p_queue_name VARCHAR2, p_execution_id RAW, p_source_exec_id RAW, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_target_list_index NUMBER, p_target_guid RAW, p_last_start_time DATE DEFAULT NULL, p_last_end_time DATE DEFAULT NULL, p_next_start_time DATE DEFAULT NULL, p_current_time DATE DEFAULT NULL, p_status INTEGER DEFAULT SCHEDULED_STATUS, p_status_detail INTEGER DEFAULT 0, p_triggering_severity RAW DEFAULT NULL, p_restart_params MGMT_JOB_PARAM_LIST DEFAULT NULL) RETURN RAW IS l_source_step_id INTEGER; l_original_step_id INTEGER; l_initial_status INTEGER := p_status; l_scheduled_time DATE; l_start_time DATE; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE := p_execution_id; l_next_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_next_start_time DATE; l_schedule MGMT_JOB_SCHEDULE_RECORD := p_schedule; l_waiting_exec_needed BOOLEAN := true; l_wait_step_needed BOOLEAN := false; l_status_detail MGMT_JOB_EXEC_SUMMARY.status_detail%TYPE := 0; l_current_time DATE := p_current_time; l_blk_count NUMBER; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_ignore NUMBER; l_scheduled_time_utc DATE; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('schedule_execution() for job ' || p_job_id || ': current time is ' || to_char(l_current_time, 'mm/dd/yy hh:mi:ss pm'), MODULE_NAME); END IF; IF l_current_time IS NULL THEN l_current_time := SYSDATE_UTC(); END IF; l_ignore := lock_job(p_job_id); -- Compute the next start time IF l_schedule IS NULL THEN -- Assume it is an IMMEDIATE schedule l_schedule := get_immediate_schedule_record; END IF; l_tzregion := compute_timezone_region(p_job_id, p_target_guid, p_target_list_index, l_schedule); IF p_next_start_time IS NULL THEN l_start_time := get_next_execution_time(l_schedule, p_last_start_time, l_tzregion, p_last_end_time); l_scheduled_time_utc := l_start_time; ELSE l_start_time := p_next_start_time; l_scheduled_time_utc := p_next_start_time; END IF; IF p_status != WAITING_STATUS AND l_start_time IS NOT NULL THEN l_start_time := get_adjusted_start_time(p_job_id, p_target_list_index, p_target_guid, p_source_exec_id, l_start_time, p_last_start_time, l_current_time, l_schedule); l_scheduled_time_utc := l_start_time; ELSE l_wait_step_needed := true; END IF; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('schedule_execution() for job ' || p_job_id || ': adjusted start time is ' || to_char(l_start_time, 'mm/dd/yy hh:mi:ss pm'), MODULE_NAME); EMDW_LOG.info('schedule_execution() for job ' || p_job_id || ': scheduled time utc is ' || to_char(l_scheduled_time_utc, 'mm/dd/yy hh:mi:ss pm'), MODULE_NAME); END IF; IF l_start_time IS NULL THEN -- For single-target jobs, expire the execution IF p_target_guid IS NOT NULL THEN UPDATE MGMT_JOB_FLAT_TARGETS SET active=0 WHERE job_id=p_job_id AND target_guid=p_target_guid; END IF; -- Set the job to expired if needed, so we don't allow -- editing and other operations on the job IF (NOT has_active_executions(p_job_id)) THEN UPDATE MGMT_JOB SET expired=1 WHERE job_id=p_job_id; END IF; RETURN null; END IF; IF l_execution_id IS NULL THEN l_execution_id := insert_or_update_execution(p_job_id, p_source_exec_id, p_target_list_index, p_target_guid, l_start_time, l_start_time, null, p_status, p_status_detail, p_triggering_severity, l_schedule,l_current_time); END IF; --update the job parameters while retrying the job if p_retry_params is not null IF p_restart_params IS NOT NULL THEN update_job_parameters(p_job_id, l_execution_id, p_restart_params,false, false,true,1); END IF; IF p_source_exec_id IS NOT NULL THEN SELECT step_id, decode(original_step_id, -1, step_id, original_step_id) INTO l_source_step_id, l_original_step_id FROM MGMT_JOB_HISTORY WHERE execution_id=p_source_exec_id AND parent_step_id=-1; END IF; -- Note the scheduled time is really in local time! -- For restarted job executions set the scheduled time -- to be the scheduled time of the original execution. IF p_source_exec_id IS NULL THEN l_scheduled_time := MGMT_GLOBAL.FROM_UTC(l_scheduled_time_utc, l_tzregion); ELSE SELECT scheduled_time INTO l_scheduled_time FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_source_exec_id; END IF; l_initial_status := get_execution_status(p_job_id, l_execution_id, p_source_exec_id, p_target_list_index, l_start_time, p_status, p_status); -- If the execution is starting in suspended/agentdown status -- and if there is a start grace period defined this should -- be scheduled as a waiting execution istead. IF (is_system_suspended_status(l_initial_status)) AND (l_schedule.start_grace_period != NO_START_GRACE) THEN l_status_detail := START_WAITING_STATUS; l_waiting_exec_needed := false; l_wait_step_needed := true; END IF; UPDATE MGMT_JOB_EXEC_SUMMARY SET status=l_initial_status, status_detail=l_status_detail, scheduled_time=l_scheduled_time, start_time=l_start_time, expected_start_time=l_start_time, timezone_region=l_tzregion WHERE execution_id=l_execution_id; -- Only set status to QUEUED for new executions IF p_queue_name IS NOT NULL AND p_execution_id IS NULL THEN l_initial_status := QUEUED_STATUS; END IF; schedule_nested_job(p_job_id, p_job_id, l_execution_id, -1, l_source_step_id, l_original_step_id, RESTART_MODE_FAILURE, NULL, -1, l_start_time, l_tzregion, l_initial_status, l_wait_step_needed); -- If the execution is being inserted into a queue, -- process the queue insert (for new executions only) IF p_queue_name IS NOT NULL AND p_execution_id IS NULL THEN insert_execution_into_queue(p_queue_name, l_execution_id); END IF; -- Insert a waiting execution IF p_status != WAITING_STATUS AND l_waiting_exec_needed THEN l_next_start_time := get_next_execution_time(l_schedule, l_start_time, l_tzregion, l_start_time); IF l_next_start_time IS NOT NULL THEN l_next_exec_id := schedule_execution(p_job_id, p_queue_name, null, null, l_schedule, p_target_list_index, p_target_guid, l_start_time, p_last_end_time, l_next_start_time, l_start_time, WAITING_STATUS, START_WAITING_STATUS); END IF; END IF; RETURN l_execution_id; END; -- Reschedule an execution that has already been scheduled -- If p_schedule_only is true, then there were only schedule -- changes. p_job_id is assumed to be the parent job id. PROCEDURE reschedule_execution(p_job_id RAW, p_execution_id RAW, p_job_type_id RAW, p_job_type_category VARCHAR2, p_target_list_index NUMBER, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_schedule_only BOOLEAN, p_current_time DATE, p_use_last_scheduled_time BOOLEAN) IS l_next_start_time DATE := null; l_source_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; dummy MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_timezone_region MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_active NUMBER; l_queue_id MGMT_JOB_QUEUES.queue_id%TYPE; l_queue_name MGMT_JOB_QUEUES.queue_name%TYPE; l_schedule MGMT_JOB_SCHEDULE_RECORD; l_creds_set NUMBER; BEGIN SELECT timezone_region, scheduled_time, queue_id, source_execution_id INTO l_timezone_region, l_next_start_time, l_queue_id, l_source_execution_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; IF l_queue_id IS NOT NULL THEN SELECT queue_name INTO l_queue_name FROM MGMT_JOB_QUEUES WHERE queue_id=l_queue_id; END IF; -- Delete all nested jobs delete_execution_nested_jobs(p_job_id, p_execution_id); -- Delete the current execution DELETE FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id; DELETE FROM MGMT_JOB_HISTORY WHERE execution_id=p_execution_id; IF NOT p_schedule_only THEN -- Delete parameters that were not specified by the user DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND created_at_submit=0; DELETE FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id=p_job_id AND execution_id=p_execution_id; -- Run submit-time parameter sources again l_creds_set := fetch_job_parameters(p_job_id, p_execution_id, null, null, 1); END IF; IF p_schedule IS NOT NULL THEN -- The schedule has changed. Do not use the old start time, -- unless specified IF p_use_last_scheduled_time THEN l_next_start_time := MGMT_GLOBAL.to_utc(l_next_start_time, l_timezone_region); ELSE l_next_start_time := null; END IF; ELSE l_next_start_time := MGMT_GLOBAL.to_utc(l_next_start_time, l_timezone_region); END IF; IF l_source_execution_id=p_execution_id THEN l_source_execution_id := null; END IF; IF p_schedule IS NULL THEN BEGIN l_schedule := get_current_schedule(p_job_id); EXCEPTION WHEN NO_DATA_FOUND THEN -- we may get a NO_DATA_FOUND if this is a corrective action -- or a queued execution l_schedule := null; END; ELSE l_schedule := p_schedule; END IF; -- delete any waiting executions. new waiting -- executions will be scheduled by schedule_execution. DELETE FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status = WAITING_STATUS; -- reprocess it dummy := schedule_execution(p_job_id, l_queue_name, p_execution_id, l_source_execution_id, l_schedule, p_target_list_index, get_single_target_guid(p_job_id, p_target_list_index, l_active), null, null, l_next_start_time, p_current_time); -- If the execution id is null, the user specified an invalid schedule. IF dummy IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_SCHEDULE_ERR, 'Could not schedule any executions between start time and end time'); END IF; -- Reeval multitask creds information if needed IF p_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN compute_mtjob_cred_info(p_job_id, p_execution_id, p_job_type_id); ELSE l_creds_set := compute_cred_info(p_job_id, p_execution_id, null, 0, 0); END IF; END; -- Reschedule all scheduled executions when a new version of -- the job type is created PROCEDURE reschedule_on_new_jobtype_ver(p_new_job_type_id RAW) IS l_job_type MGMT_JOB_TYPE_INFO.job_type%TYPE; l_major_version MGMT_JOB_TYPE_INFO.major_version%TYPE; l_status NUMBER; l_count NUMBER; l_target_count NUMBER; l_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_single_target MGMT_JOB_TYPE_INFO.single_target%TYPE; l_prev_single_target MGMT_JOB_TYPE_INFO.single_target%TYPE; BEGIN IF EMDW_LOG.p_is_info_set THEN emdw_log.info('Processing id ' || p_new_job_type_id, MODULE_NAME); END IF; -- special flag for avoiding use of EM Key during db upgrade: -- If the flag is set, avoid rescheduling the job execution, thus avoiding -- the need for EM key usage. The caveat is that the currently active job -- executions would continue to use the older job type definition. IF NOT EMD_MAINTENANCE.should_reschd_on_new_jobtype THEN RETURN; END IF; SELECT mv.job_type, mv.major_version, job_type_category, single_target INTO l_job_type, l_major_version, l_category, l_single_target FROM MGMT_JOB_TYPE_MAX_VERSIONS mv, MGMT_JOB_TYPE_INFO i WHERE mv.job_type_id=p_new_job_type_id AND mv.job_type_id=i.job_type_id; -- Reschedule all scheduled and suspended executions using the -- old job type version to use the new job type version. -- Make sure any restarted executions are not considered. FOR crec IN (SELECT e.job_id, e.job_type_id, execution_id, target_list_index FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE e.job_id=j.job_id AND j.job_type=l_job_type AND j.job_type_major_version=l_major_version AND job_type_id != p_new_job_type_id AND status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, REASSIGNED_STATUS, QUEUED_STATUS, SUSPENDED_CREDS_STATUS) AND source_execution_id = execution_id ORDER BY e.job_id, execution_id) LOOP l_status := lock_executions(crec.execution_id); IF EMDW_LOG.p_is_info_set THEN emdw_log.info('Processing execution ' || crec.execution_id, MODULE_NAME); END IF; IF l_status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, REASSIGNED_STATUS, QUEUED_STATUS, SUSPENDED_CREDS_STATUS) THEN -- Check whether there are any steps that -- already executed SELECT COUNT(1) INTO l_count FROM MGMT_JOB_EXECUTION WHERE execution_id=crec.execution_id AND step_type=STEPTYPE_STEP AND step_status NOT IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, REASSIGNED_STATUS, QUEUED_STATUS, SUSPENDED_CREDS_STATUS); IF l_count=0 THEN IF EMDW_LOG.p_is_info_set THEN emdw_log.info('Updating execution with new job type', MODULE_NAME); END IF; UPDATE MGMT_JOB_EXEC_SUMMARY SET job_type_id=p_new_job_type_id WHERE execution_id=crec.execution_id; SELECT single_target INTO l_prev_single_target FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = crec.job_type_id; -- if we changed from non single target to single target -- we need to insert the target into the flat targets table IF l_prev_single_target = 0 AND l_single_target = 1 THEN SELECT count(*) INTO l_target_count FROM MGMT_JOB_TARGET WHERE job_id = crec.job_id AND execution_id = crec.execution_id AND target_list_index = crec.target_list_index; IF l_target_count > 1 THEN force_abort_execution(crec.execution_id, 'Could not upgrade execution to new version of job type ' || l_job_type || ', found multiple targets when converting to single target'); RETURN; ELSIF l_target_count = 1 THEN INSERT INTO MGMT_JOB_FLAT_TARGETS( job_id, target_guid, target_list_index, active) SELECT job_id, target_guid, target_list_index, 1 FROM MGMT_JOB_TARGET WHERE job_id = crec.job_id AND execution_id = crec.execution_id AND target_list_index = crec.target_list_index; END IF; END IF; -- For multi-task jobs, we allow changes that are actually -- major version changes (eg., in single-execution MT jobs -- changing task names leads to changes in parameter names). -- So calling reschedule_execution here is incorrect, since -- the new job type definition is not compatible with -- the existing parameters. When editing MT jobs, the job -- type creation is actually followed by a call to edit_job() -- which will correctly reschedule the execution, taking -- the new parameters into account. IF l_category != JOBTYPE_CATEGORY_HIDDEN THEN BEGIN reschedule_execution(crec.job_id, crec.execution_id, p_new_job_type_id, l_category, crec.target_list_index, null, false, SYSDATE_UTC(), true); EXCEPTION WHEN OTHERS THEN force_abort_execution(crec.execution_id, 'Could not upgrade execution to new version of job type ' || l_job_type || ', error: ' || SQLERRM); END; END IF; END IF; END IF; END LOOP; -- Make sure that the suspended-on-creds status of any executions -- we modified is correct following the edit resume_cred_execs; suspend_cred_execs; END; -- Return the "current" schedule for the single-target job FUNCTION get_current_schedule(p_job_id RAW) RETURN MGMT_JOB_SCHEDULE_RECORD IS l_schedule MGMT_JOB_SCHEDULE_RECORD; l_nested MGMT_JOB.nested%TYPE; l_job_id MGMT_JOB.job_id%TYPE; BEGIN SELECT nested INTO l_nested FROM MGMT_JOB WHERE job_id=p_job_id; IF l_nested=1 THEN SELECT e.job_id INTO l_job_id FROM MGMT_JOB_HISTORY h, MGMT_JOB_EXEC_SUMMARY e WHERE h.job_id=p_job_id AND h.execution_id=e.execution_id AND ROWNUM=1; ELSE l_job_id := p_job_id; END IF; SELECT MGMT_JOB_SCHEDULE_RECORD(frequency_code, start_time, end_time, start_grace_period, execution_hours, execution_minutes, interval, months, days, timezone_info, timezone_target_index, timezone_offset, timezone_region) INTO l_schedule FROM MGMT_JOB_SCHEDULE s, MGMT_JOB j WHERE s.schedule_id=j.schedule_id AND j.job_id=l_job_id; RETURN l_schedule; END; -- Return the "current" schedule FUNCTION get_start_grace_period(p_job_id RAW) RETURN MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE IS l_current_schedule MGMT_JOB_SCHEDULE_RECORD; l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN BEGIN l_current_schedule := get_current_schedule(p_job_id); l_start_grace_period := l_current_schedule.start_grace_period; EXCEPTION WHEN NO_DATA_FOUND THEN -- we may get a NO_DATA_FOUND if this is a corrective action -- or a queued execution l_start_grace_period := NO_START_GRACE; END; RETURN l_start_grace_period; END; -- Upsert a target to the flat target list of the job. Return the -- new execution id, if any FUNCTION upsert_flat_target(p_job_id RAW, p_target_guid RAW, p_max_target_index IN OUT NUMBER, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_queue_name VARCHAR2, p_current_time DATE) RETURN RAW IS l_is_active NUMBER; l_target_list_index NUMBER; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE := null; l_schedule MGMT_JOB_SCHEDULE_RECORD := p_schedule; BEGIN IF l_schedule IS NULL AND p_queue_name IS NULL THEN BEGIN l_schedule := get_current_schedule(p_job_id); EXCEPTION WHEN NO_DATA_FOUND THEN l_schedule := null; END; END IF; INSERT INTO MGMT_JOB_FLAT_TARGETS(job_id, target_guid, target_list_index, active) VALUES(p_job_id, p_target_guid, p_max_target_index+1, 1); -- Schedule a new execution for this target l_execution_id := schedule_execution(p_job_id, p_queue_name, null, null, l_schedule, p_max_target_index+1, p_target_guid, null, null, null, p_current_time); p_max_target_index := p_max_target_index+1; RETURN l_execution_id; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN SELECT target_list_index, active INTO l_target_list_index, l_is_active FROM MGMT_JOB_FLAT_TARGETS WHERE job_id=p_job_id AND target_guid=p_target_guid; IF l_is_active=0 THEN -- The target was once added, then removed, and -- is now added back again UPDATE MGMT_JOB_FLAT_TARGETS SET active=1 WHERE job_id=p_job_id AND target_guid=p_target_guid; l_execution_id := schedule_execution(p_job_id, p_queue_name, null, null, l_schedule, l_target_list_index, p_target_guid, null, null, null, p_current_time); END IF; RETURN l_execution_id; END; -- Remove a target from the flat list of the job PROCEDURE remove_flat_target(p_job_id RAW, p_target_guid RAW) IS l_is_active NUMBER; l_target_list_index NUMBER; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; BEGIN SELECT target_list_index, active INTO l_target_list_index, l_is_active FROM MGMT_JOB_FLAT_TARGETS WHERE job_id=p_job_id AND target_guid=p_target_guid; IF l_is_active=1 THEN BEGIN -- There must be exactly one execution in active status SELECT execution_id INTO l_execution_id FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND target_list_index=l_target_list_index AND status NOT IN (COMPLETED_STATUS, ABORTED_STATUS, STOPPED_STATUS, FAILED_STATUS, SKIPPED_STATUS, WAITING_STATUS, DELETE_PENDING_STATUS); l_status := lock_executions(l_execution_id); -- Remove the execution IF l_status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, REASSIGNED_STATUS, QUEUED_STATUS, SUSPENDED_CREDS_STATUS) THEN stop_execution(l_execution_id, true, false,false); BEGIN delete_job_execution(l_execution_id); EXCEPTION -- Could fail for system jobs WHEN OTHERS THEN NULL; END; ELSE -- The execution is currently active. Set the -- target status to be inactive so that when the -- current execution finishes, we don't schedule -- subsequent ones UPDATE MGMT_JOB_FLAT_TARGETS SET active=0 WHERE job_id=p_job_id AND target_list_index=l_target_list_index; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN UPDATE MGMT_JOB_FLAT_TARGETS SET active=0 WHERE job_id=p_job_id AND target_list_index=l_target_list_index; END; END IF; END; -- Upsert the flat target list of the job, and add and stop executions -- as necessary. Returns all the new executions that were added FUNCTION upsert_single_target_list(p_job_id RAW, p_flat_target_guids MGMT_JOB_GUID_ARRAY, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_queue_names SMP_EMD_STRING_ARRAY, p_current_time DATE) RETURN MGMT_JOB_GUID_ARRAY IS l_new_execs MGMT_JOB_GUID_ARRAY := MGMT_JOB_GUID_ARRAY(); l_deleted_targets MGMT_JOB_GUID_ARRAY; l_max_index NUMBER; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_curr_exec INTEGER := 1; l_queue_name MGMT_JOB_QUEUES.queue_name%TYPE; BEGIN SELECT NVL(MAX(target_list_index), 0) INTO l_max_index FROM MGMT_JOB_FLAT_TARGETS WHERE job_id=p_job_id; -- Upsert each target IF p_flat_target_guids IS NOT NULL AND p_flat_target_guids.COUNT > 0 THEN FOR i IN 1..p_flat_target_guids.COUNT LOOP IF p_queue_names IS NULL THEN l_queue_name := null; ELSE l_queue_name := p_queue_names(i); END IF; l_execution_id := upsert_flat_target(p_job_id, p_flat_target_guids(i), l_max_index, p_schedule, l_queue_name, p_current_time); IF l_execution_id IS NOT NULL THEN l_new_execs.extend(1); l_new_execs(l_curr_exec) := l_execution_id; l_curr_exec := l_curr_exec+1; END IF; END LOOP; END IF; -- Now deal with deleted targets. These are targets that -- are still marked active, and not in the current list SELECT target_guid BULK COLLECT INTO l_deleted_targets FROM MGMT_JOB_FLAT_TARGETS WHERE job_id=p_job_id AND active=1 AND target_guid NOT IN ( SELECT * FROM TABLE(CAST(p_flat_target_guids AS MGMT_JOB_GUID_ARRAY))); IF l_deleted_targets IS NOT NULL AND l_deleted_targets.COUNT > 0 THEN FOR i IN 1..l_deleted_targets.COUNT LOOP remove_flat_target(p_job_id, l_deleted_targets(i)); END LOOP; END IF; RETURN l_new_execs; END; -- flatten the list of targets for this job -- note. this can be used for nested jobs as well. PROCEDURE flatten_target_list(p_target_guids MGMT_JOB_GUID_LIST, p_target_type VARCHAR2, p_flat_target_guids OUT MGMT_JOB_GUID_ARRAY, p_flat_target_names OUT MGMT_JOB_VECTOR_PARAMS, p_flat_target_types OUT MGMT_JOB_VECTOR_PARAMS) IS l_cluster_target_type MGMT_TARGETS.target_type%TYPE; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('flatten_target_list:enter,targetType=' || p_target_type, MODULE_NAME); END IF; -- Note: The asumption here is that there will only be one -- cluster type for a given target type BEGIN SELECT target_type INTO l_cluster_target_type FROM MGMT_TYPE_PROPERTIES WHERE property_name=MGMT_GLOBAL.G_CLUSTER_MEMBER_TYPE_PROP AND property_value=p_target_type; EXCEPTION WHEN NO_DATA_FOUND THEN l_cluster_target_type := null; END; -- Compute the final flat target list of the job. Explode all groups, -- as well as all composites IF l_cluster_target_type IS NULL THEN SELECT * BULK COLLECT INTO p_flat_target_guids, p_flat_target_names, p_flat_target_types FROM ( SELECT t.target_guid, t.target_name, t.target_type FROM MGMT_TARGETS t, TABLE(CAST(p_target_guids AS MGMT_JOB_GUID_LIST)) jt WHERE jt.target_guid=t.target_guid AND t.target_type=p_target_type UNION SELECT assoc.assoc_target_guid, assoc_target.target_name, assoc_target.target_type FROM MGMT_FLAT_TARGET_ASSOC assoc, MGMT_TARGETS assoc_target WHERE assoc_target.target_type=p_target_type AND assoc.assoc_target_guid = assoc_target.target_guid AND assoc.is_membership = 1 AND assoc.source_target_guid IN (SELECT t.target_guid FROM MGMT_TARGETS t, TABLE(CAST(p_target_guids AS MGMT_JOB_GUID_LIST)) jt, MGMT_TYPE_PROPERTIES tp WHERE jt.target_guid=t.target_guid AND t.target_type=tp.target_type AND tp.property_name=MGMT_GLOBAL.G_IS_AGGREGATE_PROP AND tp.property_value='1') ); ELSE SELECT * BULK COLLECT INTO p_flat_target_guids, p_flat_target_names, p_flat_target_types FROM ( SELECT t.target_guid, t.target_name, t.target_type FROM MGMT_TARGETS t, TABLE(CAST(p_target_guids AS MGMT_JOB_GUID_LIST)) jt WHERE jt.target_guid=t.target_guid AND t.target_type IN (p_target_type, l_cluster_target_type) UNION SELECT assoc.assoc_target_guid, assoc_target.target_name, assoc_target.target_type FROM MGMT_FLAT_TARGET_ASSOC assoc, MGMT_TARGETS assoc_target WHERE assoc_target.target_type IN (p_target_type, l_cluster_target_type) AND assoc.assoc_target_guid = assoc_target.target_guid AND assoc.is_membership = 1 AND assoc.source_target_guid IN (SELECT t.target_guid FROM MGMT_TARGETS t, TABLE(CAST(p_target_guids AS MGMT_JOB_GUID_LIST)) jt, MGMT_TYPE_PROPERTIES tp WHERE jt.target_guid=t.target_guid AND t.target_type=tp.target_type AND tp.property_name=MGMT_GLOBAL.G_IS_AGGREGATE_PROP AND tp.property_value='1' AND NOT EXISTS (SELECT 1 FROM MGMT_TYPE_PROPERTIES tp2 WHERE tp2.target_type=tp.target_type AND tp2.property_name=MGMT_GLOBAL.G_IS_CLUSTER_PROP AND tp2.property_value='1') ) ); END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('flatten_target_list:exit,targetType=' || p_target_type, MODULE_NAME); END IF; END; -- Schedule the executions for a single-target job. There is one -- execution per flat target -- return NULL if the flat list of targets is empty -- return empty list if the schedule is invalid -- otherwise return valid execution list FUNCTION schedule_single_target_execs(p_job_id RAW, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_queue_names SMP_EMD_STRING_ARRAY, p_current_time DATE) RETURN MGMT_JOB_GUID_ARRAY IS l_exec_ids MGMT_JOB_GUID_ARRAY := MGMT_JOB_GUID_ARRAY(); l_target_guids MGMT_JOB_GUID_LIST := MGMT_JOB_GUID_LIST(); l_job_target_type MGMT_JOB.target_type%TYPE; l_flat_target_guids MGMT_JOB_GUID_ARRAY := MGMT_JOB_GUID_ARRAY(); l_flat_target_names MGMT_JOB_VECTOR_PARAMS := MGMT_JOB_VECTOR_PARAMS(); l_flat_target_types MGMT_JOB_VECTOR_PARAMS := MGMT_JOB_VECTOR_PARAMS(); l_queue_names SMP_EMD_STRING_ARRAY := p_queue_names; l_new_execs MGMT_JOB_GUID_ARRAY; BEGIN SELECT MGMT_JOB_TARGET_TYPE(target_guid) BULK COLLECT INTO l_target_guids FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=NO_EXECUTION; SELECT target_type INTO l_job_target_type FROM MGMT_JOB WHERE job_id=p_job_id; flatten_target_list(l_target_guids, l_job_target_type, l_flat_target_guids, l_flat_target_names, l_flat_target_types); IF l_queue_names IS NOT NULL AND l_flat_target_guids IS NOT NULL AND l_flat_target_guids.COUNT != l_queue_names.COUNT THEN l_queue_names := null; END IF; -- Note: even if the flat list returned is null, we will still -- need to go and remove all the executions that are currently -- scheduled...(bug 5572753) l_new_execs := upsert_single_target_list(p_job_id, l_flat_target_guids, p_schedule, l_queue_names, p_current_time); IF l_flat_target_guids IS NULL AND l_flat_target_guids.COUNT > 0 THEN RETURN NULL; ELSE RETURN l_new_execs; END IF; END; -- upsert the target lists of the job, schedule executions as -- necessary. Returns the guids of all newly scheduled executions FUNCTION upsert_job_target_lists(p_job_id RAW, p_is_library NUMBER, p_job_targets MGMT_JOB_TARGET_LIST_ARRAY, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_job_target_type VARCHAR2, p_edit BOOLEAN, p_queue_names SMP_EMD_STRING_ARRAY DEFAULT NULL) RETURN MGMT_JOB_GUID_ARRAY IS l_target_list MGMT_JOB_TARGET_LIST; l_execution_ids MGMT_JOB_GUID_ARRAY := null; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_single_target_job BOOLEAN; l_schedule MGMT_JOB_SCHEDULE_RECORD := p_schedule; l_current_time DATE := SYSDATE_UTC(); l_queue_name MGMT_JOB_QUEUES.queue_name%TYPE; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('upsert_job_target_lists() for job ' || p_job_id || ': current time is ' || to_char(l_current_time, 'mm/dd/yy hh:mi:ss pm'), MODULE_NAME); END IF; l_single_target_job := is_single_target_job(p_job_id); -- For single-target jobs, there is one target list, and -- one execution per target in the list. -- For jobs that are not single-target, there is one execution -- per target list. IF NOT l_single_target_job THEN l_execution_ids := MGMT_JOB_GUID_ARRAY(); l_execution_ids.extend(p_job_targets.COUNT); END IF; FOR i in 1..p_job_targets.count LOOP l_target_list := p_job_targets(i); -- Insert targets at the job level update_job_targets(p_job_id, i, l_target_list, p_job_target_type); -- Schedule the executions IF p_is_library=0 THEN IF l_single_target_job THEN -- Compute the flat list and schedule executions as -- neccessary l_execution_ids := MGMT_JOB_ENGINE.schedule_single_target_execs(p_job_id, p_schedule, p_queue_names, l_current_time); ELSE -- There is one execution for this target list. See if -- it already has been scheduled BEGIN SELECT execution_id, status INTO l_execution_id, l_status FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND target_list_index=i AND status NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS, SKIPPED_STATUS, WAITING_STATUS, DELETE_PENDING_STATUS); -- There already is an execution. Just update the -- target list for it IF l_status=SCHEDULED_STATUS THEN l_status := lock_executions(l_execution_id); IF l_status=SCHEDULED_STATUS THEN update_job_exec_targets(p_job_id, l_execution_id); END IF; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN IF p_queue_names IS NULL THEN l_queue_name := null; ELSE l_queue_name := p_queue_names(i); END IF; -- There is no existing execution; schedule one l_execution_ids(i) := schedule_execution(p_job_id, l_queue_name, null, null, p_schedule, i, null, null, null, null, l_current_time); END; END IF; -- Check to see that we have at least one execution, -- if we're submitting the job IF NOT p_edit THEN IF l_single_target_job THEN -- bug 4333348: throw either NO_TARGET_SPECIFIED_ERR -- or START_END_TIME_INVALID_ERR IF l_execution_ids IS NULL THEN -- no targets raise_application_error( MGMT_GLOBAL.NO_TARGET_SPECIFIED_ERR, MGMT_GLOBAL.NO_TARGET_SPECIFIED_ERR_M); ELSIF l_execution_ids.COUNT=0 THEN -- targets found, must be a schedule issue raise_application_error( MGMT_GLOBAL.START_END_TIME_INVALID_ERR, MGMT_GLOBAL.START_END_TIME_INVALID_ERR_M); END IF; -- not l_single_target_job ELSIF l_execution_ids(i) IS NULL THEN raise_application_error( MGMT_GLOBAL.START_END_TIME_INVALID_ERR, MGMT_GLOBAL.START_END_TIME_INVALID_ERR_M); END IF; END IF; END IF; END LOOP; RETURN l_execution_ids; END; -- Utility routine which inserts a vector parameter -- if it does not exist, or updates it if it exists. PROCEDURE upsert_vector_parameter(p_job_id RAW, p_execution_id RAW, p_param_name VARCHAR2, p_param_value MGMT_JOB_VECTOR_PARAMS) IS l_old_param_value MGMT_JOB_VECTOR_PARAMS; BEGIN BEGIN SELECT vector_value INTO l_old_param_value FROM MGMT_JOB_PARAMETER WHERE job_id = p_job_id AND execution_id = p_execution_id AND parameter_name = p_param_name AND parameter_type = PARAM_TYPE_VECTOR; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('upsert_vector_parameter:updating param=' || p_param_name || ',job=' || p_job_id || ',exec=' || p_execution_id, MODULE_NAME); END IF; UPDATE MGMT_JOB_PARAMETER SET vector_value = p_param_value WHERE job_id = p_job_id AND execution_id = p_execution_id AND parameter_name = p_param_name AND parameter_type = PARAM_TYPE_VECTOR; EXCEPTION WHEN NO_DATA_FOUND THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('upsert_vector_parameter:inserting param=' || p_param_name || ',job=' || p_job_id || ',exec=' || p_execution_id, MODULE_NAME); END IF; INSERT INTO MGMT_JOB_PARAMETER (job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value) VALUES (p_job_id, p_execution_id, p_param_name, PARAM_TYPE_VECTOR, 0, null, p_param_value); END; END; -- Schedule a flatten target list step FUNCTION schedule_flatten_targets_step(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_step_name VARCHAR2, p_step_type NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) RETURN NUMBER IS l_step_id NUMBER := -1; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('schedule_flatten_targets_step:enter,step_name=' || p_step_name || ',step_type=' || p_step_type, MODULE_NAME); END IF; l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, p_update_status, SYSTEM_COMMAND); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('schedule_flatten_targets_step:exit,step_name=' || p_step_name || ',step_type=' || p_step_type || ', scheduled step_id=' || l_step_id, MODULE_NAME); END IF; RETURN l_step_id; END; -- Execute a flatten target list step PROCEDURE execute_flatten_targets_step(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER, p_job_type_id RAW, p_step_name VARCHAR2, p_step_type NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE) IS l_nested_job_type MGMT_JOB_EXECPLAN.nested_job_type%TYPE; l_nested_job_major_version NUMBER; l_nested_job_type_id RAW(16); l_iterate_param_index MGMT_JOB_EXECUTION.iterate_param_index%TYPE := p_iterate_param_index; l_target_guids MGMT_JOB_GUID_LIST; l_target_type MGMT_JOB.target_type%TYPE; l_flat_target_guids MGMT_JOB_GUID_ARRAY := MGMT_JOB_GUID_ARRAY(); l_flat_target_names MGMT_JOB_VECTOR_PARAMS := MGMT_JOB_VECTOR_PARAMS(); l_flat_target_types MGMT_JOB_VECTOR_PARAMS := MGMT_JOB_VECTOR_PARAMS(); l_iterate_param MGMT_JOB_EXECPLAN.iterate_param%TYPE; l_all_targets MGMT_JOB_EXECPLAN.all_targets%TYPE; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:enter,step_name=' || p_step_name || ',step_type=' || p_step_type, MODULE_NAME); END IF; SELECT iterate_param, nested_job_type, nested_job_target_type, all_targets INTO l_iterate_param, l_nested_job_type, l_target_type, l_all_targets FROM MGMT_JOB_EXECPLAN WHERE job_type_id = p_job_type_id AND step_name = p_step_name AND step_type = p_step_type; IF l_target_type IS NULL THEN get_max_versions(l_nested_job_type, l_nested_job_major_version, l_nested_job_type_id); SELECT default_target_type INTO l_target_type FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_nested_job_type_id; IF l_target_type IS NULL THEN BEGIN SELECT single_target_type INTO l_target_type FROM MGMT_JOB_SINGLE_TARGET_TYPES WHERE job_type_id = l_nested_job_type_id; EXCEPTION WHEN OTHERS THEN EMDW_LOG.error('execute_flatten_targets_step:enter,step_name=' || p_step_name || ',step_type=' || p_step_type || ' could not locate single target type,error=' || SQLERRM, MODULE_NAME); raise_application_error(MGMT_GLOBAL.INVALID_JOB_TYPE_ERR, 'The nested job ' || p_step_name || ' does not have a valid default target type'); END; END IF; END IF; IF l_all_targets = 1 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:step_name=' || p_step_name || ',step_type=' || p_step_type || ', all job targets will be copied', MODULE_NAME); END IF; SELECT MGMT_JOB_TARGET_TYPE(target_guid) BULK COLLECT INTO l_target_guids FROM MGMT_JOB_TARGET WHERE job_id=p_job_id AND execution_id=NO_EXECUTION ORDER BY target_index; ELSE IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:step_name=' || p_step_name || ',step_type=' || p_step_type || ', selected job targets will be copied', MODULE_NAME); END IF; resolve_nested_job_targets(p_job_id, p_execution_id, p_step_id, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, l_target_guids); END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:step_name=' || p_step_name || ',step_type=' || p_step_type || ',found ' || l_target_guids.COUNT || ' original targets', MODULE_NAME); END IF; flatten_target_list(l_target_guids, l_target_type, l_flat_target_guids, l_flat_target_names, l_flat_target_types); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:step_name=' || p_step_name || ',step_type=' || p_step_type || ',found ' || l_flat_target_names.COUNT || ' flat targets', MODULE_NAME); END IF; IF l_iterate_param_index = -1 THEN l_iterate_param_index := 1; END IF; upsert_vector_parameter(p_job_id, p_execution_id, 'target_names_' || l_iterate_param_index || '_' || l_iterate_param, l_flat_target_names); upsert_vector_parameter(p_job_id, p_execution_id, 'target_types_' || l_iterate_param_index || '_' || l_iterate_param, l_flat_target_types); write_step_output(p_step_id, 'Flatten target list step succeeded, found ' || l_target_guids.COUNT || ' original targets and ' || l_flat_target_guids.COUNT || ' flat targets'); update_step_status(p_step_id, COMPLETED_STATUS, 0, STATUS_CATEGORY_APP, false, true); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:exit,step_name=' || p_step_name || ',step_type=' || p_step_type, MODULE_NAME); END IF; EXCEPTION WHEN OTHERS THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('execute_flatten_targets_step:exit exception,step_name=' || p_step_name || ',step_type='|| p_step_type || ',error=' || SQLERRM, MODULE_NAME); -- EMDW_LOG.debug('stack=' || dbms_utility.format_error_stack, MODULE_NAME); END IF; write_step_output(p_step_id, 'Flatten target list step failed due to ' || SQLERRM); update_step_status(p_step_id, ABORTED_STATUS, 0, STATUS_CATEGORY_APP, false, true); END; -- Schedule and possibly execute a flatten target list step PROCEDURE sch_exec_flatten_targets_step(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_step_type NUMBER, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) IS l_step_id NUMBER := -1; l_status NUMBER; l_steps_processed NUMBER := 0; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('sch_exec_flatten_targets_step:enter,step_name=' || p_step_name || ',step_type=' || p_step_type, MODULE_NAME); END IF; BEGIN SELECT COUNT(*) INTO l_steps_processed FROM MGMT_JOB_EXECUTION WHERE execution_id = p_execution_id AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) AND step_status != SCHEDULED_STATUS; EXCEPTION WHEN NO_DATA_FOUND THEN l_steps_processed := 0; END; l_status := lock_executions(p_execution_id); l_step_id := schedule_flatten_targets_step(p_job_id, p_execution_id, p_parent_step_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); IF l_steps_processed > 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('sch_exec_flatten_targets_step:executing flatten step, step_name=' || p_step_name || ',step_type=' || p_step_type, MODULE_NAME); END IF; execute_flatten_targets_step(p_job_id, p_execution_id, l_step_id, p_job_type_id, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, p_start_time); END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('sch_exec_flatten_targets_step:exit,step_name=' || p_step_name || ',step_type=' || p_step_type, MODULE_NAME); END IF; EXCEPTION WHEN OTHERS THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('sch_exec_flatten_targets_step:exit exception,step_name=' || p_step_name || ',step_type='|| p_step_type || ',error=' || SQLERRM, MODULE_NAME); -- EMDW_LOG.debug('stack=' || dbms_utility.format_error_stack, MODULE_NAME); END IF; END; -- Process a flatten targets step PROCEDURE process_flatten_targets_step(p_step_id NUMBER) IS l_status NUMBER; l_cur_exec MGMT_JOB_EXECUTION%ROWTYPE; l_job_type_id MGMT_JOB_EXEC_SUMMARY.job_type_id%TYPE; l_error_msg VARCHAR2(4000); BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('process_flatten_targets_step:enter,step_id=' || p_step_id, MODULE_NAME); END IF; SELECT * INTO l_cur_exec FROM MGMT_JOB_EXECUTION WHERE step_id = p_step_id; -- evaluate the security info and lock info sections BEGIN check_security_info(l_cur_exec.job_id, l_cur_exec.execution_id, 0); l_status := get_execution_locks(l_cur_exec.job_id, l_cur_exec.execution_id); EXCEPTION WHEN OTHERS THEN l_error_msg := 'Security/Lock param source evaluation failed due to ' || SQLERRM; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('process_flatten_targets_step:exit exception, step_id=' || p_step_id || ',error=' || l_error_msg, MODULE_NAME); END IF; write_step_error_message(p_step_id, l_error_msg); update_step_status(p_step_id, ABORTED_STATUS, 1); RETURN; END; SELECT job_type_id INTO l_job_type_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id = l_cur_exec.execution_id; l_status := lock_executions(l_cur_exec.execution_id); execute_flatten_targets_step(l_cur_exec.job_id, l_cur_exec.execution_id, p_step_id, l_job_type_id, l_cur_exec.step_name, l_cur_exec.step_type, l_cur_exec.iterate_param, l_cur_exec.iterate_param_index, l_cur_exec.start_time); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('process_flatten_targets_step:exit,step_id=' || p_step_id, MODULE_NAME); END IF; END; -- Schedule the next execution in a periodic job. All times in utc PROCEDURE schedule_next_job_execution(p_job_id RAW, p_execution_id RAW, p_last_start_time DATE, p_last_end_time DATE, p_status NUMBER) IS l_schedule MGMT_JOB_SCHEDULE_RECORD; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_source_execution_id RAW(16); l_new_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_target_guid MGMT_TARGETS.target_guid%TYPE; l_active NUMBER; l_queue_id MGMT_JOB_EXEC_SUMMARY.queue_id%TYPE; BEGIN SELECT target_list_index, source_execution_id, queue_id INTO l_target_list_index, l_source_execution_id, l_queue_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; IF l_source_execution_id != p_execution_id THEN -- do NOT schedule restarted jobs RETURN; END IF; l_target_guid := get_single_target_guid(p_job_id, l_target_list_index, l_active); -- If the execution is of a single-target job whose target -- has expired, then do not reschedule IF l_target_guid IS NOT NULL AND l_active=0 THEN RETURN; END IF; -- If the execution is a queued execution, it cannot have -- a repeating schedule. IF l_queue_id IS NOT NULL THEN RETURN; END IF; BEGIN l_schedule := get_current_schedule(p_job_id); EXCEPTION WHEN NO_DATA_FOUND THEN -- we may get a NO_DATA_FOUND if this is a corrective action -- or a queued execution RETURN; END; -- Schedule the new execution l_new_execution_id := schedule_execution(p_job_id, null, null, null, l_schedule, l_target_list_index, l_target_guid, p_last_start_time, p_last_end_time, null, null, p_status); EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS OR DUP_VAL_ON_INDEX OR SUBSCRIPT_BEYOND_COUNT OR COLLECTION_IS_NULL OR VALUE_ERROR THEN force_abort_execution(p_execution_id, SQLERRM); WHEN OTHERS THEN IF SQLCODE < -20000 THEN force_abort_execution(p_execution_id, SQLERRM); END IF; END; -- Update the reference count at the row specified by p_output_id -- in the output table. FUNCTION update_refcount(p_output_id RAW) RETURN RAW IS BEGIN IF p_output_id IS NOT NULL THEN UPDATE MGMT_JOB_OUTPUT set reference_count=reference_count+1 WHERE output_id=p_output_id; END IF; RETURN p_output_id; END; -- Copy one step from the source to the destination execution. FUNCTION copy_step(p_source_step_id INTEGER, p_restart_job_id RAW, p_restart_exec_id RAW, p_restart_parent_step_id INTEGER) RETURN INTEGER IS l_new_step_id INTEGER; l_num_children NUMBER; BEGIN -- For some reason, RETURNING INTO does not seem to work with a -- INSERT INTO...SELECT FROM combination... SELECT MGMT_JOB_SEQUENCE.NEXTVAL INTO l_new_step_id FROM DUAL; INSERT INTO MGMT_JOB_EXECUTION(job_id, execution_id, step_id, source_step_id, original_step_id, step_name, step_type, iterate_param, iterate_param_index, parent_step_id, step_status, step_status_code, num_children, num_children_completed, output_id, error_id, start_time, end_time, timezone_region) SELECT p_restart_job_id, p_restart_exec_id, l_new_step_id, p_source_step_id, decode(original_step_id, -1, p_source_step_id, original_step_id), step_name, step_type, iterate_param, iterate_param_index, p_restart_parent_step_id, step_status, step_status_code, num_children, num_children_completed, mgmt_job_engine.update_refcount(output_id), mgmt_job_engine.update_refcount(error_id), start_time, end_time, timezone_region FROM MGMT_JOB_HISTORY WHERE step_id=p_source_step_id; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Copy:New step for source ' || p_source_step_id || ' is ' || l_new_step_id, MODULE_NAME); END IF; -- Copy the command blocks, taking care to up the refcounts INSERT INTO MGMT_JOB_STEP_COMMAND_LOG(step_id, command_block_id, command_block_text_id, command_block_status, command_block_error) SELECT l_new_step_id, MGMT_JOB_SEQUENCE.NEXTVAL, mgmt_job_engine.update_refcount(command_block_text_id), command_block_status, command_block_error FROM MGMT_JOB_STEP_COMMAND_LOG WHERE step_id=p_source_step_id; -- copy step targets INSERT INTO MGMT_JOB_STEP_TARGETS(step_id, target_guid) SELECT l_new_step_id,target_guid FROM MGMT_JOB_STEP_TARGETS WHERE step_id = p_source_step_id; RETURN l_new_step_id; END; -- Copy a set of steps corresponding to the schedulable entity whose -- step is p_source_step_id to the "restart" execution whose -- execution id is p_restart_execution_id. Note that depending on -- the schedulable entity, more than one row may be copied. FUNCTION copy_steps(p_execution_id RAW, p_source_step_id INTEGER, p_source_step_type INTEGER, p_restart_job_id RAW, p_restart_exec_id RAW, p_restart_parent_step_id INTEGER) RETURN INTEGER IS l_job_id RAW(16); l_new_step_id INTEGER; l_source_job_id RAW(16); ignore INTEGER; BEGIN -- Copy one row, for the step itself l_new_step_id := copy_step(p_source_step_id, p_restart_job_id, p_restart_exec_id, p_restart_parent_step_id); IF p_source_step_type=STEPTYPE_STEP THEN -- We're done! RETURN l_new_step_id; ELSIF p_source_step_type=STEPTYPE_JOB THEN -- For a nested job, we need a new job id for the child rows, -- and a new entry for the nested job as well... l_job_id := SYS_GUID(); IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('copy_steps(): new job id is ' || l_job_id, MODULE_NAME); END IF; SELECT job_id INTO l_source_job_id FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_step_id AND rownum=1; copy_job_entry(l_source_job_id, l_job_id, p_execution_id, p_restart_job_id); ELSE -- This is a stepset of some flavour. l_job_id := p_restart_job_id; END IF; -- Walk through the tree recursively, inserting each row. -- Note that I can't insert all the rows in one go because -- the new steps need new step/job ids, and the parent-child relationship -- has to be maintained. FOR crec in (SELECT step_id, step_type FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_step_id) LOOP ignore := copy_steps(p_execution_id, crec.step_id, crec.step_type, l_job_id, p_restart_exec_id, l_new_step_id); END LOOP; RETURN l_new_step_id; EXCEPTION -- This should never happen. This could only happen if the source -- execution had a job that had no steps. WHEN NO_DATA_FOUND THEN RETURN -1; END; -- Restart the specified step from the specified source execution, if -- required. If the step need not be restarted, copy the row(s) -- corresponding to its status from the source execution to the current -- execution, and return the status of the step in the source execution. -- The step id and the exit code of the copied-over step are returned -- through the out params p_restart_step_id_out and -- p_restart_exit_code_out, respectively. -- If the step must be restarted, then this method returns -1; the -- step will subsequently be scheduled by the calling routine. FUNCTION process_restart(p_job_id RAW, p_execution_id RAW, p_job_type_id RAW, p_parent_step_id INTEGER, p_source_parent_step_id INTEGER, p_orig_parent_step_id INTEGER, p_restart_mode IN OUT INTEGER, p_step_name VARCHAR2, p_step_type VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_iterate_index NUMBER, p_source_step_id_out OUT NUMBER, p_original_step_id_out OUT NUMBER, p_restart_step_id_out OUT NUMBER, p_source_status_code_out OUT NUMBER, p_schedule_pramsrc_retry_step OUT NUMBER) RETURN INTEGER IS l_source_step_status INTEGER; l_paramsrc_count INTEGER := 0; l_paramsrc_step_id NUMBER; l_job_restart_mode NUMBER := RESTART_MODE_FAILURE; l_nested_job_name MGMT_JOB_EXECPLAN.step_name%TYPE; l_parent_job_id MGMT_JOB.job_id%TYPE; l_parent_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN -- if the step is of type STEPTYPE_PARAMSRC_RETRY_EXEC -- then it should always be retried IF p_step_type=STEPTYPE_PARAMSRC_RETRY_EXEC THEN p_original_step_id_out := -1; RETURN -1; END IF; -- If the step is an iterative serial stepset and the -- iterate index is greater than 1, return -1 since -- we have already determined that the stepset should -- be rescheduled IF p_step_type=STEPTYPE_ITSERIAL_STEPSET AND p_iterate_param IS NOT NULL AND p_iterate_param_index > 1 THEN p_source_step_id_out := p_source_parent_step_id; SELECT original_step_id INTO p_original_step_id_out FROM MGMT_JOB_HISTORY WHERE step_id=p_source_step_id_out; RETURN -1; END IF; -- Attempt to find the source step for this step. IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Attempting to find source step for parent ' || p_source_parent_step_id || ' name ' || p_step_name || ' type ' || p_step_type, MODULE_NAME); END IF; BEGIN IF p_step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) THEN IF p_iterate_param IS NULL THEN SELECT step_id, original_step_id, step_status, step_status_code INTO p_source_step_id_out, p_original_step_id_out, l_source_step_status, p_source_status_code_out FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_parent_step_id AND step_name=p_step_name AND step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC); ELSE SELECT step_id, original_step_id, step_status, step_status_code INTO p_source_step_id_out, p_original_step_id_out, l_source_step_status, p_source_status_code_out FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_parent_step_id AND step_name=p_step_name AND step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) AND iterate_param=p_iterate_param AND iterate_param_index=p_iterate_param_index; END IF; ELSE IF p_iterate_param IS NULL THEN SELECT step_id, original_step_id, step_status, step_status_code INTO p_source_step_id_out, p_original_step_id_out, l_source_step_status, p_source_status_code_out FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_parent_step_id AND step_name=p_step_name AND step_type=p_step_type; ELSE SELECT step_id, original_step_id, step_status, step_status_code INTO p_source_step_id_out, p_original_step_id_out, l_source_step_status, p_source_status_code_out FROM MGMT_JOB_HISTORY WHERE parent_step_id=p_source_parent_step_id AND step_name=p_step_name AND step_type=p_step_type AND iterate_param=p_iterate_param AND iterate_param_index=p_iterate_param_index; END IF; END IF; EXCEPTION -- We may not find the step if it never executed in the source -- execution WHEN NO_DATA_FOUND THEN p_source_step_id_out := -1; p_original_step_id_out := -1; END; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Found source step: ' || p_source_step_id_out, MODULE_NAME); END IF; -- If the nested job has a restart mode set to "always", -- do not schedule a paramsrc_retry. IF p_restart_mode=RESTART_MODE_ALWAYS AND p_step_type != STEPTYPE_JOB THEN BEGIN -- Try to get the parent job name SELECT step_name INTO l_nested_job_name FROM MGMT_JOB_EXECUTION e1 WHERE step_type=STEPTYPE_JOB AND EXISTS (SELECT 1 FROM MGMT_JOB_EXECUTION e2 WHERE e2.job_id=p_job_id AND e2.parent_step_id=e1.step_id AND e2.job_id!=e1.job_id); -- If we're here, we are within a nested job. -- We will need to get the job type id of the -- job type that nests this job to see what the -- restart mode was SELECT parent_job_id INTO l_parent_job_id FROM MGMT_JOB WHERE job_id=p_job_id; l_parent_job_type_id := get_job_type_id(l_parent_job_id, p_execution_id); SELECT restart_mode INTO l_job_restart_mode FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_parent_job_type_id AND step_type=STEPTYPE_JOB AND step_name=l_nested_job_name; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; END IF; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Restart mode of job is ' || l_job_restart_mode, MODULE_NAME); END IF; -- If the incoming restart mode is "always", then this means -- that the restart mode of a stepset or nested job was ALWAYS. -- This means all steps and stepsets underneath will be always -- restarted. -- For paramsrc steps, we do not check for the restart mode UNLESS -- the restart mode for the job is "always" IF p_step_type NOT IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) OR l_job_restart_mode=RESTART_MODE_ALWAYS THEN -- Check whether the passed in restart mode is "always" IF p_restart_mode = RESTART_MODE_ALWAYS THEN p_original_step_id_out := -1; RETURN -1; END IF; -- Check the restart mode for the specific step BEGIN SELECT restart_mode INTO p_restart_mode FROM MGMT_JOB_EXECPLAN WHERE job_type_id = p_job_type_id AND step_name = p_step_name AND step_type = p_step_type; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; -- Check the restart mode for the step; if it is ALWAYS, then it -- should be rescheduled regardless of its status in the source -- execution. IF p_restart_mode=RESTART_MODE_ALWAYS THEN -- The step must always be restarted p_original_step_id_out := -1; RETURN -1; ELSIF p_original_step_id_out = -1 THEN -- If the step in the source execution was an "original" -- execution, then it becomes the original execution for -- the current step p_original_step_id_out := p_source_step_id_out; END IF; END IF; IF l_source_step_status = COMPLETED_STATUS THEN -- if this is a step of type STEPTYPE_PARAMSRC -- and it has some param sources associated with it -- which have the apply_on_retry value set -- then this step is rescheduled as STEPTYPE_PRAMSRC_RETRY IF p_step_type=STEPTYPE_PARAMSRC THEN SELECT count(1) INTO l_paramsrc_count FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND apply_on_retry=1; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Step name '|| p_step_name ||' type STEPTYPE_PARAMSRC has ' || l_paramsrc_count || ' apply_on_retry pram sources', MODULE_NAME); END IF; IF l_paramsrc_count > 0 THEN p_schedule_pramsrc_retry_step := 1; p_original_step_id_out := -1; RETURN -1; END IF; ELSE -- if it is a non param src step check if -- this step has a param source associated with it -- if so check if the param source has some sources -- which have apply_on_retry set -- if so then we need to schedule a PARAMSRC_RETRY step -- we do so by setting p_schedule_pramsrc_retry_step to '1' -- and returning -1 SELECT count(1) INTO l_paramsrc_count FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=p_step_type AND apply_at_submission=0 AND apply_on_retry = 1; IF l_paramsrc_count > 0 AND p_start_iterate_index=1 THEN BEGIN IF p_iterate_param IS NULL THEN SELECT step_id INTO l_paramsrc_step_id FROM MGMT_JOB_EXECUTION WHERE job_id=p_job_id AND execution_id=p_execution_id AND step_name=p_step_name AND step_type IN (STEPTYPE_PARAMSRC_RETRY_EXEC); ELSE SELECT step_id INTO l_paramsrc_step_id FROM MGMT_JOB_EXECUTION WHERE job_id=p_job_id AND execution_id=p_execution_id AND step_name=p_step_name AND step_type IN (STEPTYPE_PARAMSRC_RETRY_EXEC) AND iterate_param=p_iterate_param AND iterate_param_index=p_iterate_param_index; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- schedule a PARAMSRC_RETRY step p_schedule_pramsrc_retry_step := 1; p_original_step_id_out := -1; RETURN -1; END; END IF; END IF; -- Copy the execution entry(s) for this schedulable entity IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Copying entries for step ' || p_source_step_id_out, MODULE_NAME); END IF; p_restart_step_id_out := copy_steps(p_execution_id, p_source_step_id_out, p_step_type, p_job_id, p_execution_id, p_parent_step_id); RETURN l_source_step_status; ELSE -- This step must be rescheduled for execution. IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Step ' || p_source_step_id_out || ' must be rescheduled', MODULE_NAME); END IF; p_original_step_id_out := -1; RETURN -1; END IF; END; -- Schedule a schedulable entity (step/stepset/job). All parameters -- have their usual meanings. The following is worth noting: -- p_parent_step_id is the step id of the parent step, -1 for the -- top-level step (corresponding to the job) -- p_source_parent_step_id is the step id of the step corresponding -- to the parent step in the source execution during a restart. -- p_orig_parent_step_id is the step id of the step that the parent -- step was copied from during a restart. The source step id and -- the original step id may be different. The source step id always -- refers to the source execution, while the original step id -- may be from any execution in the restart chain from which -- the step was originally copied. -- p_restart_mode is the parent's restart mode. If it is ALWAYS, -- it overrides the restart mode of the current step. -- p_update_status is the status to use when inserting entries for -- the step(set) or job. It could be either SCHEDULED or SUSPENDED. FUNCTION schedule(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_parent_step_id INTEGER, p_orig_parent_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_step_type VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_iterate_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) RETURN INTEGER IS l_new_job_id RAW(16); l_ret INTEGER := 1; l_restart_status INTEGER; l_source_step_id INTEGER := -1; l_original_step_id INTEGER := -1; l_restart_step_id INTEGER; l_restart_status_code INTEGER; l_restart_mode INTEGER := p_restart_mode; l_schedule_pramsrc_retry_step INTEGER := 0; l_paramsrc_step_id NUMBER; l_paramsrc_count NUMBER := 0; l_step_id NUMBER; l_schedule_param_source BOOLEAN := false; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling ' || p_step_name || ',' || p_step_type, MODULE_NAME); END IF; IF p_source_parent_step_id > 0 THEN -- Check whether the step should be restarted, or whether -- the entry(s) from the source execution can be copied over l_restart_status := process_restart(p_job_id, p_execution_id, p_job_type_id, p_parent_step_id, p_source_parent_step_id, p_orig_parent_step_id, l_restart_mode, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, p_start_iterate_index, l_source_step_id, l_original_step_id, l_restart_step_id, l_restart_status_code, l_schedule_pramsrc_retry_step); -- scheduling step of type STEPTYPE_PRAMSRC_RETRY_EXEC IF l_schedule_pramsrc_retry_step > 0 THEN RETURN schedule(p_job_id, p_execution_id, p_parent_step_id, p_source_parent_step_id, p_orig_parent_step_id, l_restart_mode, p_job_type_id, p_step_name, STEPTYPE_PARAMSRC_RETRY_EXEC, p_iterate_param, p_iterate_param_index, p_start_iterate_index, p_start_time, p_tzregion, p_update_status); END IF; IF l_restart_status > 0 THEN -- There is no need to reschedule this step. The preceding -- call to restart_step must have copied over source -- execution entries, if any. update_step_status(l_restart_step_id, l_restart_status, l_restart_status_code, STATUS_CATEGORY_APP, true); RETURN l_ret; END IF; END IF; -- If we're here, we're either a new execution or a restart execution -- where the current step has to be started. -- First, see if there are parameter sources associated with -- the entity if we're about to schedule. If so, schedule them -- Do not re-execute parameter sources on retry -- (p_source_parent_step_id IS NULL OR p_source_parent_step_id <= 0) AND IF p_step_type != STEPTYPE_PARAMSRC AND p_step_type != STEPTYPE_PARAMSRC_RETRY AND p_step_type != STEPTYPE_PARAMSRC_RETRY_EXEC THEN SELECT count(1) INTO l_paramsrc_count FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=p_step_type AND apply_at_submission=0; END IF; -- See if we've already scheduled an entry for the parameter -- source step. For iterative serial stepsets, we need to -- do this check only the first time -- Do not re-execute parameter sources on retry -- (p_source_parent_step_id IS NULL OR p_source_parent_step_id <= 0) AND IF l_paramsrc_count > 0 AND p_start_iterate_index=1 THEN BEGIN IF p_iterate_param IS NULL THEN SELECT step_id INTO l_paramsrc_step_id FROM MGMT_JOB_EXECUTION WHERE job_id=p_job_id AND execution_id=p_execution_id AND step_name=p_step_name AND step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC); ELSE SELECT step_id INTO l_paramsrc_step_id FROM MGMT_JOB_EXECUTION WHERE job_id=p_job_id AND execution_id=p_execution_id AND step_name=p_step_name AND step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) AND iterate_param=p_iterate_param AND iterate_param_index=p_iterate_param_index; END IF; -- If we did find an entry for the param source, then -- we can go ahead and schedule the entity itself EXCEPTION WHEN NO_DATA_FOUND THEN -- Schedule a paramsrc step RETURN schedule(p_job_id, p_execution_id, p_parent_step_id, p_source_parent_step_id, p_orig_parent_step_id, l_restart_mode, p_job_type_id, p_step_name, STEPTYPE_PARAMSRC, p_iterate_param, p_iterate_param_index, p_start_iterate_index, p_start_time, p_tzregion, p_update_status); END; END IF; IF p_step_type = STEPTYPE_SERIAL_STEPSET THEN schedule_serial_stepset(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); ELSIF p_step_type = STEPTYPE_SWITCH_STEPSET THEN schedule_switch_stepset(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); ELSIF p_step_type = STEPTYPE_PARALLEL_STEPSET THEN schedule_parallel_stepset(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); ELSIF p_step_type = STEPTYPE_ITPLL_STEPSET THEN l_ret := schedule_iterative_stepset(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, true, p_start_iterate_index, p_update_status); ELSIF p_step_type = STEPTYPE_ITSERIAL_STEPSET THEN l_ret := schedule_iterative_stepset(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, false, p_start_iterate_index, p_update_status); ELSIF p_step_type = STEPTYPE_STEP THEN schedule_step(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); ELSIF p_step_type = STEPTYPE_JOB THEN -- Insert a new entry in the job tables for a job l_new_job_id := insert_nested_job(p_job_id, p_execution_id, p_job_type_id, p_step_name, p_parent_step_id, p_iterate_param, p_iterate_param_index, l_source_step_id, l_original_step_id); schedule_nested_job(l_new_job_id, p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); ELSIF p_step_type=STEPTYPE_PARAMSRC OR p_step_type=STEPTYPE_PARAMSRC_RETRY OR p_step_type=STEPTYPE_PARAMSRC_RETRY_EXEC THEN schedule_param_src(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status, p_step_type); ELSIF p_step_type = STEPTYPE_FLATTEN_TARGETS_STEP THEN sch_exec_flatten_targets_step(p_job_id, p_execution_id, p_parent_step_id, l_source_step_id, l_original_step_id, l_restart_mode, p_job_type_id, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, p_start_time, p_tzregion, p_update_status); END IF; RETURN l_ret; END; -- The version of schedule called from most places: defaults start -- index for iterative stepsets to 1. PROCEDURE schedule(p_job_id RAW, p_execution_id RAW, p_parent_step_id INTEGER, p_source_parent_step_id INTEGER, p_orig_parent_step_id INTEGER, p_restart_mode INTEGER, p_job_type_id RAW, p_step_name VARCHAR2, p_step_type VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_start_time DATE, p_tzregion VARCHAR2, p_update_status NUMBER) IS ignore INTEGER; BEGIN ignore := schedule(p_job_id, p_execution_id, p_parent_step_id, p_source_parent_step_id, p_orig_parent_step_id, p_restart_mode, p_job_type_id, p_step_name, p_step_type, p_iterate_param, p_iterate_param_index, 1, p_start_time, p_tzregion, p_update_status); END; -- Compute the status of a stepset, assuming all its -- steps have completed FUNCTION compute_step_set_status(p_stepset_id INTEGER, p_job_id RAW, p_execution_id RAW, p_stepset_type INTEGER, p_last_step_status INTEGER, p_step_error_code IN OUT NUMBER, p_step_error_code_category IN OUT NUMBER) RETURN INTEGER IS l_num_completed INTEGER; l_num_failed INTEGER; l_num_aborted INTEGER; l_stepset_status VARCHAR2(64); l_step_status NUMBER; l_max_end_time DATE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id, p_execution_id); -- First determine whether this stepset has a stepsetStatus value. -- Currently, this is a step(set) whose status is used to determine -- the status of the stepset. Note that a job will not have an entry -- in the execplan table... IF p_stepset_type != STEPTYPE_JOB THEN SELECT stepset_status INTO l_stepset_status FROM MGMT_JOB_EXECPLAN WHERE step_name=(SELECT step_name FROM MGMT_JOB_EXECUTION WHERE step_id=p_stepset_id) AND step_type=p_stepset_type AND job_type_id=l_job_type_id; END IF; IF l_stepset_status IS NOT NULL THEN BEGIN -- Obtain the status of the step named in l_stepset_status SELECT step_status, step_status_code, step_status_code_category INTO l_step_status, p_step_error_code, p_step_error_code_category FROM MGMT_JOB_EXECUTION WHERE step_type NOT IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) AND step_name=l_stepset_status AND parent_step_id=p_stepset_id; RETURN l_step_status; EXCEPTION -- It is possible that the step that determines the stepset -- status has not executed. In particular, this could happen -- when a step prior to the step that provides the stepset_status -- aborts. In this case, we just go with the default algorithm -- of computing the status WHEN NO_DATA_FOUND THEN NULL; END; END IF; -- If we're here, then no specific stepsetStatus directive is -- present, and we use the default algorithm to determine the -- status of a stepset. -- The status of an iterative child or serial stepset is the status -- of the last step. The status of iterative parent or parallel stepsets -- is computed based on the status of all the steps. IF p_stepset_type = STEPTYPE_PARALLEL_STEPSET OR p_stepset_type = STEPTYPE_ITPLL_STEPSET OR p_stepset_type = STEPTYPE_ITSERIAL_STEPSET THEN -- This is a parallel/iterative stepset. Such a stepset has -- succeeded only if all of its children succeed SELECT sum(decode(step_status, COMPLETED_STATUS, 1, 0)) "COMPLETED", sum(decode(step_status, ABORTED_STATUS, 1, 0)) "ABORTED", sum(decode(step_status, FAILED_STATUS, 1, 0)) "FAILED" INTO l_num_completed, l_num_aborted, l_num_failed FROM MGMT_JOB_EXECUTION WHERE parent_step_id=p_stepset_id; IF l_num_aborted > 0 OR l_num_failed > 0 THEN SELECT MAX(end_time) INTO l_max_end_time FROM MGMT_JOB_EXECUTION WHERE parent_step_id=p_stepset_id AND step_status IN (ABORTED_STATUS, FAILED_STATUS); -- There could be multiple steps with the same end time SELECT step_status_code, step_status_code_category INTO p_step_error_code, p_step_error_code_category FROM MGMT_JOB_EXECUTION WHERE parent_step_id=p_stepset_id AND step_status IN (ABORTED_STATUS, FAILED_STATUS) AND end_time=l_max_end_time AND ROWNUM=1; END IF; IF l_num_aborted > 0 THEN return ABORTED_STATUS; ELSIF l_num_failed > 0 THEN return FAILED_STATUS; ELSE p_step_error_code := 0; return COMPLETED_STATUS; END IF; ELSE -- The step error code is unchanged RETURN p_last_step_status; END IF; END; -- Compute and update the status of a stepset PROCEDURE update_step_set_status(p_stepset_id INTEGER, p_stepset_name VARCHAR2, p_last_step_status INTEGER, p_error_code NUMBER, p_error_code_category NUMBER) IS l_stepset_status INTEGER; l_stepset_type INTEGER; l_job_id MGMT_JOB.job_id%TYPE; l_execution_id MGMT_JOB_EXECUTION.execution_id%TYPE; l_error_code NUMBER := p_error_code; l_error_code_category NUMBER := p_error_code_category; BEGIN SELECT step_type, job_id, execution_id INTO l_stepset_type, l_job_id, l_execution_id FROM MGMT_JOB_EXECUTION WHERE step_id=p_stepset_id; l_stepset_status := compute_step_set_status(p_stepset_id, l_job_id, l_execution_id, l_stepset_type, p_last_step_status, l_error_code, l_error_code_category); update_step_status_nolock(p_stepset_id, l_stepset_status, l_error_code, l_error_code_category); END; -- Compute and return the status of the specified step, taking into -- account any command blocks that might have executed FUNCTION compute_step_status(p_step_id INTEGER, p_incoming_status INTEGER) RETURN INTEGER IS l_num_completed INTEGER; l_num_failed INTEGER; BEGIN IF p_incoming_status=SCHEDULED_STATUS THEN RETURN SCHEDULED_STATUS; END IF; SELECT SUM(decode(command_block_status, COMPLETED_STATUS, 1, 0)), SUM(decode(command_block_status, FAILED_STATUS, 1, 0)) INTO l_num_completed, l_num_failed FROM MGMT_JOB_STEP_COMMAND_LOG WHERE step_id=p_step_id; IF l_num_failed > 0 THEN IF p_incoming_status = ABORTED_STATUS THEN RETURN ABORTED_STATUS; ELSE RETURN FAILED_STATUS; END IF; ELSE RETURN p_incoming_status; END IF; END; -- Return the status of an iterative serial stepset on failure. -- The job type can specify whether to abort or fail it FUNCTION get_itserial_halt_on_failure(l_job_type_id RAW, l_step_name VARCHAR2) RETURN BOOLEAN IS l_failure_status INTEGER; BEGIN SELECT halt_on_failure INTO l_failure_status FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND step_name=l_step_name AND step_type=STEPTYPE_ITSERIAL_STEPSET; RETURN (l_failure_status=1); END; -- Attempt to grab a lock on all executions with the same -- target list index as the execution that was passed in. -- Return the current status of the execution that was passed in. -- If p_nowait is true, return immediately (with a thrown exception). FUNCTION lock_executions(p_execution_id RAW, p_nowait BOOLEAN DEFAULT FALSE) RETURN NUMBER IS l_job_id MGMT_JOB_EXEC_SUMMARY.job_id%TYPE; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_job_lock_handle VARCHAR2(256); l_wait_time_secs NUMBER; BEGIN SELECT job_id, target_list_index, status INTO l_job_id, l_target_list_index, l_status FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id = p_execution_id; IF p_nowait THEN l_wait_time_secs := 0; ELSE l_wait_time_secs := 300; -- timeout after 5 minutes END IF; l_job_lock_handle := MGMT_LOCK_UTIL.get_exclusive_lock( JOB_TARGET_INDEX_LOCK, l_job_id || ':' || l_target_list_index, l_wait_time_secs, MGMT_GLOBAL.EXEC_LOCK_ERR, 'Unable to acquire exclusive lock for job ' || l_job_id || ' with target index ' || l_target_list_index); RETURN l_status; EXCEPTION -- This could happen if a system job was removed from the repository WHEN NO_DATA_FOUND THEN RETURN -1; END; -- Attempt to grab a lock on all executions with the same -- target list index as the step that was passed in. -- Return the current status of the execution corresponding -- to the step that was passed in. -- If p_nowait is true, return immediately (with a thrown exception). FUNCTION lock_executions(p_step_id NUMBER, p_nowait BOOLEAN DEFAULT false) RETURN NUMBER IS l_execution_id RAW(16); BEGIN SELECT execution_id INTO l_execution_id FROM MGMT_JOB_EXECUTION WHERE step_id = p_step_id; RETURN lock_executions(l_execution_id, p_nowait); EXCEPTION -- This could happen if a system job was removed from the repository WHEN NO_DATA_FOUND THEN RETURN -1; END; PROCEDURE upsert_ca_target_lists(p_job_id RAW, p_job_targets MGMT_JOB_TARGET_LIST_ARRAY, p_job_target_type VARCHAR2) IS l_target_list_index NUMBER:=1; l_target_list MGMT_JOB_TARGET_LIST; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; BEGIN -- We know that there is only one list in the job_target array -- Insert targets at the job level IF p_job_targets IS NOT NULL THEN l_target_list:= p_job_targets(1); update_job_targets(p_job_id, l_target_list_index, l_target_list, p_job_target_type); END IF; -- There is one execution for this target list. See if -- it already has been scheduled BEGIN SELECT execution_id, status INTO l_execution_id, l_status FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS, SKIPPED_STATUS, WAITING_STATUS, DELETE_PENDING_STATUS); -- There already is an execution. Just update the -- target list for it IF l_status=SCHEDULED_STATUS THEN l_status := lock_executions(l_execution_id); IF l_status=SCHEDULED_STATUS THEN update_job_exec_targets(p_job_id, l_execution_id); END IF; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN null; END; END; -- Compare the two integer arrays (assumed to be unsorted) -- Return true if they are "equal" (have the same elements) -- false otherwise FUNCTION compare_int_array(p_arr1 MGMT_JOB_INT_ARRAY, p_arr2 MGMT_JOB_INT_ARRAY) RETURN BOOLEAN IS l_arr1 MGMT_JOB_INT_ARRAY; l_arr2 MGMT_JOB_INT_ARRAY; BEGIN IF p_arr1 IS NULL AND p_arr2 IS NULL THEN RETURN true; END IF; IF p_arr1.count != p_arr2.count THEN RETURN false; END IF; -- Sort each array SELECT * BULK COLLECT INTO l_arr1 FROM TABLE(CAST(p_arr1 as MGMT_JOB_INT_ARRAY)) ORDER BY 1; SELECT * BULK COLLECT INTO l_arr2 FROM TABLE(CAST(p_arr2 as MGMT_JOB_INT_ARRAY)) ORDER BY 1; FOR i IN 1..l_arr1.COUNT LOOP IF l_arr1(i)!= l_arr2(i) THEN RETURN false; END IF; END LOOP; RETURN true; END; -- Compare the two schedules. Return true if they are equal, -- false otherwise. p_sch1 is assumed to be the original -- schedule, and p_sch2 is assumed to be the new schedule. -- If the OUT parameter p_use_last_scheduled_time is true, then -- the stored last_scheduled_time of the job must be used. -- If false, the new scheduled time is computed from the schedule. FUNCTION compare_schedules(p_job_id RAW, p_sch1 MGMT_JOB_SCHEDULE_RECORD, p_sch2 MGMT_JOB_SCHEDULE_RECORD, p_use_last_scheduled_time OUT BOOLEAN) RETURN BOOLEAN IS l_scheduled_executions_count NUMBER; BEGIN IF p_sch1 IS NULL AND p_sch2 IS NULL THEN -- Debatable p_use_last_scheduled_time := true; RETURN true; END IF; IF p_sch1 IS NULL OR p_sch2 IS NULL THEN p_use_last_scheduled_time := false; RETURN false; END IF; -- Check the frequency code IF p_sch1.frequency_code != p_sch2.frequency_code THEN p_use_last_scheduled_time := false; RETURN false; END IF; IF p_sch1.frequency_code=IMMEDIATE_FREQUENCY_CODE THEN -- Fall through NULL; ELSE IF p_sch1.start_time != p_sch2.start_time THEN p_use_last_scheduled_time := false; RETURN false; END IF; IF p_sch1.frequency_code=ONE_TIME_FREQUENCY_CODE THEN -- Fall through NULL; ELSE -- This is one of the repeating frequency codes IF p_sch1.frequency_code = INTERVAL_FREQUENCY_CODE THEN IF p_sch1.interval != p_sch2.interval THEN p_use_last_scheduled_time := false; RETURN false; END IF; ELSE -- This is one of daily, weekly, monthly or yearly -- Check the execution hours and minutes IF NOT (p_sch1.execution_hours=p_sch2.execution_hours AND p_sch1.execution_minutes=p_sch2.execution_minutes) THEN p_use_last_scheduled_time := false; RETURN false; END IF; IF p_sch1.frequency_code = DAILY_FREQUENCY_CODE THEN IF p_sch1.interval != p_sch2.interval THEN p_use_last_scheduled_time := false; RETURN false; END IF; ELSIF p_sch1.frequency_code = WEEK_FREQUENCY_CODE OR p_sch1.frequency_code = MONTH_FREQUENCY_CODE THEN IF NOT compare_int_array(p_sch1.days, p_sch2.days) THEN p_use_last_scheduled_time := false; RETURN false; END IF; ELSIF p_sch1.frequency_code = YEAR_FREQUENCY_CODE THEN IF NOT compare_int_array(p_sch1.days, p_sch2.days) OR NOT compare_int_array(p_sch1.months, p_sch2.months) THEN p_use_last_scheduled_time := false; RETURN false; END IF; END IF; END IF; -- Check the end times IF p_sch1.end_time IS NULL AND p_sch2.end_time IS NULL THEN NULL; ELSE IF p_sch1.end_time IS NULL OR p_sch2.end_time IS NULL OR p_sch1.end_time != p_sch2.end_time THEN -- If the end times don't match, check whether -- there are any scheduled executions after -- the end time. SELECT COUNT(1) INTO l_scheduled_executions_count FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS, DELETE_PENDING_STATUS, WAITING_STATUS, SKIPPED_STATUS) AND scheduled_time > p_sch2.end_time; IF l_scheduled_executions_count = 0 THEN p_use_last_scheduled_time := true; RETURN false; ELSE -- This is an invalid schedule, we will -- not allow the end time to effectively cut the -- current schedule short: they can always stop -- the job if they want to do this raise_application_error(MGMT_GLOBAL.INVAL_END_TIME_SCH_EXECS_ERR, 'There are scheduled executions beyond the end time'); END IF; END IF; END IF; END IF; END IF; -- If we're here, everything else has compared OK -- Compare the grace period IF p_sch1.start_grace_period IS NULL AND p_sch2.start_grace_period IS NULL THEN NULL; ELSIF p_sch1.start_grace_period IS NULL OR p_sch2.start_grace_period IS NULL THEN p_use_last_scheduled_time := true; RETURN false; ELSIF p_sch1.start_grace_period != p_sch2.start_grace_period THEN p_use_last_scheduled_time := true; RETURN false; END IF; -- Compare timezone information IF p_sch1.timezone_info != p_sch2.timezone_info THEN p_use_last_scheduled_time := false; RETURN false; END IF; IF p_sch1.timezone_info = TIMEZONE_TARGET THEN IF p_sch1.timezone_target_index != p_sch2.timezone_target_index THEN p_use_last_scheduled_time := false; RETURN false; END IF; ELSIF p_sch1.timezone_info = TIMEZONE_RGN_SPECIFIED THEN IF p_sch1.timezone_region != p_sch2.timezone_region THEN p_use_last_scheduled_time := false; RETURN false; END IF; ELSIF p_sch1.timezone_info = TIMEZONE_SPECIFIED THEN IF p_sch1.timezone_offset != p_sch2.timezone_offset THEN p_use_last_scheduled_time := false; RETURN false; END IF; END IF; p_use_last_scheduled_time := true; RETURN true; END; -- -- Edit the specified job or CA. Note that changes do not affect currently -- running executions. -- PROCEDURE edit_job(p_job_id RAW, p_description VARCHAR2, p_params MGMT_JOB_PARAM_LIST, p_targets MGMT_JOB_TARGET_LIST_ARRAY, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_overridden_creds MGMT_JOB_CRED_ARRAY DEFAULT NULL, p_job_notify_states SMP_EMD_INTEGER_ARRAY DEFAULT NULL) IS l_execution_ids MGMT_USER_GUID_ARRAY; l_target_list_indexes SMP_EMD_INTEGER_ARRAY; l_status NUMBER; l_frequency_code NUMBER; l_start_time DATE; l_end_time DATE; l_execution_hours NUMBER; l_execution_minutes NUMBER; l_interval NUMBER; l_months MGMT_JOB_INT_ARRAY; l_days MGMT_JOB_INT_ARRAY; l_timezone_info NUMBER; l_timezone_target_index NUMBER; l_timezone_region MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_is_library NUMBER; l_is_ca MGMT_JOB.is_corrective_action%TYPE; l_broken MGMT_JOB.broken%TYPE; l_broken_reason MGMT_JOB.broken_reason%TYPE; -- True if a security check has alreeady been made l_security_checked BOOLEAN := false; l_ignore MGMT_JOB_GUID_ARRAY; l_schedule_only BOOLEAN := true; l_current_schedule MGMT_JOB_SCHEDULE_RECORD; l_schedule MGMT_JOB_SCHEDULE_RECORD := p_schedule; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_job_target_type MGMT_JOB.target_type%TYPE; l_job_status NUMBER; l_current_time DATE; l_is_expired NUMBER; l_use_last_scheduled_time BOOLEAN := false; l_step_count NUMBER; --Audit edit_job l_job_type MGMT_JOB.job_type%TYPE; l_audit_level NUMBER; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%type; l_ca_scope MGMT_CORRECTIVE_ACTION.ca_scope%TYPE; l_creds_set NUMBER; BEGIN l_job_status := lock_job(p_job_id); SELECT is_corrective_action, broken, broken_reason INTO l_is_ca, l_broken, l_broken_reason FROM MGMT_JOB WHERE job_id=p_job_id; -- Update the job description IF p_description IS NOT NULL THEN check_modify_job(p_job_id, true); l_security_checked := true; UPDATE MGMT_JOB SET job_description=p_description WHERE job_id=p_job_id RETURNING job_name, job_owner, job_status, is_library, expired, target_type, job_type INTO l_job_name, l_job_owner, l_job_status, l_is_library, l_is_expired, l_job_target_type, l_job_type; ELSE SELECT job_name, job_owner, job_status, is_library, expired, target_type, job_type INTO l_job_name, l_job_owner, l_job_status, l_is_library, l_is_expired, l_job_target_type, l_job_type FROM MGMT_JOB WHERE job_id=p_job_id; END IF; IF l_is_ca=0 AND (l_is_expired=1 OR l_job_status=JOB_STATUS_STOPPED) THEN -- Ignore parameters, targets and schedule for expired jobs. RETURN; END IF; SELECT ji.job_type_category, ji.job_type_id INTO l_job_type_category, l_job_type_id FROM MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv, MGMT_JOB_TYPE_INFO ji WHERE j.job_id=p_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND mv.job_type_id=ji.job_type_id; -- Update the job schedule IF l_schedule IS NOT NULL THEN IF l_is_ca=1 THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Schedule cannot be specified for Corrective Actions'); ELSE IF NOT l_security_checked THEN check_modify_job(p_job_id, true); l_security_checked := true; END IF; l_current_schedule := get_current_schedule(p_job_id); IF NOT compare_schedules(p_job_id, l_current_schedule, l_schedule, l_use_last_scheduled_time) THEN update_job_schedule(p_job_id, l_schedule); ELSE l_schedule := null; END IF; END IF; END IF; -- Update the job notification preferences IF p_job_notify_states IS NOT NULL THEN check_modify_job(p_job_id, false); l_security_checked := true; DELETE FROM MGMT_JOB_NOTIFY_STATES WHERE job_id=p_job_id; FOR i IN 1..p_job_notify_states.COUNT LOOP INSERT INTO MGMT_JOB_NOTIFY_STATES(job_id, notify_state) VALUES (p_job_id, p_job_notify_states(i)); END LOOP; END IF; -- if the job type is not editable -- Targets cannot be edited --TO-DO: Do not allow parameters other than credentials to be edited -- if job type is not editable. -- Currently we are allowing parameters to be edited since when job is -- reassigned, credentials may change, and pref. creds are modelled as -- parameters. So we've to allow parameters to change. -- In future, we need a better way to identify parameters that map to -- credentials. IF NOT is_editable(p_job_id) THEN IF p_targets IS NOT NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Cannot edit job: job type is not editable'); END IF; END IF; -- Update the parameters at the job level IF p_params IS NOT NULL AND p_params.COUNT > 0 THEN check_modify_job(p_job_id, false); l_security_checked := true; -- Now delete all job level parameters -- that are not large DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND parameter_type != PARAM_TYPE_LARGE; -- Delete all large parameters that are not in the -- specified parameter list DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=NO_EXECUTION AND parameter_type = PARAM_TYPE_LARGE AND parameter_name NOT IN (SELECT param_name FROM TABLE(CAST(p_params AS MGMT_JOB_PARAM_LIST)) ); update_job_parameters(p_job_id, NO_EXECUTION, p_params, false, false, true, 1); l_schedule_only := false; END IF; -- Update the target list at the job level IF p_targets IS NOT NULL THEN IF NOT l_security_checked THEN check_modify_job(p_job_id, true); l_security_checked := true; END IF; IF l_is_ca=1 THEN upsert_ca_target_lists(p_job_id, p_targets, l_job_target_type); ELSE l_ignore := upsert_job_target_lists(p_job_id, l_is_library, p_targets, l_schedule, l_job_target_type, true); END IF; l_schedule_only := false; END IF; -- For CA's, ALWAYS delete the old credentials if a new user is -- modifying the job IF l_is_ca=1 THEN IF MGMT_USER.get_current_em_user != l_job_owner THEN MGMT_CREDENTIAL.delete_job_credentials(p_job_id); END IF; END IF; IF l_is_ca=1 THEN check_modify_job(p_job_id, false); UPDATE MGMT_JOB SET job_owner=MGMT_USER.get_current_em_user WHERE job_id=p_job_id; END IF; -- Update the overridden credentials, if not null. IF p_overridden_creds IS NOT NULL THEN check_modify_job(p_job_id, false); l_security_checked := true; MGMT_CREDENTIAL.delete_job_credentials(p_job_id); IF p_overridden_creds.COUNT > 0 THEN MGMT_CREDENTIAL.set_job_credentials(p_job_id, p_overridden_creds); END IF; l_schedule_only := false; END IF; --if corrective action is broken due to deleted target, un-break it. IF l_is_ca=1 AND l_broken != 0 AND l_broken_reason = BROKEN_TARGET_DELETED THEN IF NOT l_security_checked THEN check_modify_job(p_job_id, true); l_security_checked := true; END IF; UPDATE MGMT_JOB SET broken = 0, broken_reason = NULL WHERE job_id = p_job_id; END IF; -- If the job is a library job, or if it is stopped or expired, we're done IF l_is_expired=1 OR l_is_library=1 OR l_job_status=JOB_STATUS_STOPPED THEN RETURN; END IF; --if editing a reassigned job: -- 1. change job status to active if user can modify the job -- 2. resume all reassigned executions IF l_job_status = JOB_STATUS_REASSIGNED THEN BEGIN IF l_is_ca=0 THEN -- For jobs, superusers CANNOT edit job check_modify_job(p_job_id, false); l_security_checked := true; ELSE -- For CAs, superusers CAN edit job check_modify_job(p_job_id, true); l_security_checked := true; END IF; --Update job status to active UPDATE MGMT_JOB SET job_status=JOB_STATUS_ACTIVE, broken=0, broken_reason=NULL WHERE job_id=p_job_id; l_job_status := JOB_STATUS_ACTIVE; -- Get all reassigned executions SELECT execution_id BULK COLLECT INTO l_execution_ids FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status=REASSIGNED_STATUS; IF l_execution_ids IS NOT NULL AND l_execution_ids.COUNT > 0 THEN l_start_grace_period := get_start_grace_period(p_job_id); FOR i IN 1..l_execution_ids.COUNT LOOP resume_job_execution(l_execution_ids(i), REASSIGNED_STATUS, l_start_grace_period); END LOOP; END IF; EXCEPTION WHEN mgmt_global.insufficient_privileges THEN NULL; END; END IF; -- For corrective actions, update the owner field to be that of the -- current user IF l_is_ca=1 THEN SELECT ca_scope INTO l_ca_scope FROM MGMT_CORRECTIVE_ACTION WHERE job_id=p_job_id; -- For target scoped CAs, we have to make sure that the brokenness -- (or otherwise) of the CA is recomputed IF l_ca_scope=CA_SCOPE_TARGET THEN IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN compute_mtjob_cred_info(p_job_id, NO_EXECUTION, l_job_type_id); ELSE DELETE FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id=p_job_id; l_creds_set := compute_cred_info(p_job_id, NO_EXECUTION, null, 1, 0); l_creds_set := compute_cred_info(p_job_id, NO_EXECUTION, null, 0, 0); END IF; resume_cred_execs; END IF; END IF; -- Propogate changes to existing SCHEDULED/SUSPENDED executions only SELECT execution_id, target_list_index BULK COLLECT INTO l_execution_ids, l_target_list_indexes FROM MGMT_JOB_EXEC_SUMMARY e WHERE job_id=p_job_id AND status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS, AGENTDOWN_STATUS, REASSIGNED_STATUS); l_current_time := SYSDATE_UTC(); -- Loop through each execution, edit the parameters IF l_execution_ids IS NOT NULL AND l_execution_ids.COUNT > 0 THEN FOR i IN 1..l_execution_ids.COUNT LOOP l_status := lock_executions(l_execution_ids(i)); IF l_status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS) THEN -- For suspended executions, we have to determine whether -- the current execution has to be modified. We will -- modify the execution only if no steps -- have executed in the execution IF l_status != SCHEDULED_STATUS THEN SELECT COUNT(*) INTO l_step_count FROM MGMT_JOB_EXECUTION WHERE execution_id=l_execution_ids(i) AND step_type = STEPTYPE_STEP AND step_status IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS); END IF; IF l_step_count > 0 THEN GOTO next_iteration; END IF; -- Update the parameters and the targets IF p_params IS NOT NULL AND p_params.COUNT > 0 THEN DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=l_execution_ids(i) AND parameter_type != PARAM_TYPE_LARGE; DELETE FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=l_execution_ids(i) AND parameter_type = PARAM_TYPE_LARGE AND parameter_name NOT IN (SELECT param_name FROM TABLE(CAST(p_params AS MGMT_JOB_PARAM_LIST))); -- copy over any newly created large job parameters -- NOTE: we should refrence the same CLOB for job as well as exec params INSERT INTO MGMT_JOB_PARAMETER ( job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, vector_value, large_value, created_at_submit) SELECT p1.job_id, l_execution_ids(i) ,p1.parameter_name, p1.parameter_type, p1.encrypted, p1.scalar_value, p1.vector_value, p1.large_value, p1.created_at_submit FROM MGMT_JOB_PARAMETER p1 WHERE p1.parameter_type = PARAM_TYPE_LARGE AND p1.job_id = p_job_id AND p1.execution_id=NO_EXECUTION AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_PARAMETER p2 WHERE p1.job_id = p2.job_id AND p2.execution_id = l_execution_ids(i) AND p1.parameter_name = p2.parameter_name); update_job_parameters(p_job_id, l_execution_ids(i), p_params, false, false, true, 1); END IF; -- Reschedule the execution. This may be necessary since -- the first step(s) of a job may depend on the -- parameter(s) that were edited. IF (p_params IS NOT NULL AND p_params.COUNT > 0) OR p_targets IS NOT NULL OR l_schedule IS NOT NULL THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Rescheduling execution ' || l_execution_ids(i), MODULE_NAME); END IF; reschedule_execution(p_job_id, l_execution_ids(i), l_job_type_id, l_job_type_category, l_target_list_indexes(i), l_schedule, l_schedule_only, l_current_time, l_use_last_scheduled_time); END IF; END IF; <> NULL; END LOOP; END IF; -- Make sure that the suspended-on-creds status of any executions -- we modified is correct following the edit resume_cred_execs; suspend_cred_execs; --Audit edit_job. Currently not done for CAs? IF l_is_ca=0 THEN mgmt_audit_admin.audit_level(l_audit_level); IF (l_audit_level = mgmt_audit_admin.AUDIT_LEVEL_ALL OR l_audit_level = mgmt_audit_admin.AUDIT_LEVEL_SELECTED) THEN mgmt_audit_log.audit_log(mgmt_audit_log.EDIT_JOB, l_job_name, l_job_type, l_job_owner); END IF; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- If we were not able to update anything, then -- the job does not exist raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified job does not exist'); END; FUNCTION get_large_param(p_job_id RAW, p_execution_id RAW, p_nested_job_name VARCHAR2, p_parameter_name VARCHAR2, p_for_update NUMBER DEFAULT 0) RETURN CLOB IS l_param_id MGMT_JOB_LARGE_PARAMS.param_id %TYPE; l_large_value CLOB; l_param_type MGMT_JOB_PARAMETER.parameter_type%TYPE; l_status NUMBER; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_job_type_id MGMT_JOB_EXEC_SUMMARY.job_type_id%TYPE; BEGIN -- Check that the caller has enough privileges to access -- the parameter IF p_for_update=1 THEN check_modify_job(p_job_id, false); ELSE check_view_job(p_job_id); END IF; IF p_execution_id IS NULL THEN l_execution_id := NO_EXECUTION; ELSE l_execution_id := p_execution_id; END IF; IF p_nested_job_name IS NOT NULL THEN IF p_execution_id IS NULL THEN l_job_type_id := get_job_type_id(p_job_id); ELSE l_job_type_id := get_job_type_id(p_job_id, p_execution_id); END IF; SELECT parameter_type, large_value INTO l_param_type, l_param_id FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id = l_job_type_id AND param_name = p_parameter_name AND step_name = p_nested_job_name; ELSE SELECT parameter_type, large_value INTO l_param_type, l_param_id FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=l_execution_id AND parameter_name=p_parameter_name; END IF; IF l_param_type != PARAM_TYPE_LARGE THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAM_ERR, 'Parameter ' || p_parameter_name || ' is not a large parameter'); END IF; IF l_param_id IS NOT NULL THEN IF p_for_update=1 THEN SELECT param_value INTO l_large_value FROM MGMT_JOB_LARGE_PARAMS WHERE param_id=l_param_id FOR UPDATE; ELSE SELECT param_value INTO l_large_value FROM MGMT_JOB_LARGE_PARAMS WHERE param_id=l_param_id; END IF; END IF; RETURN l_large_value; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN null; END; -- -- Return the large paremeter having the specified name -- corresponding to the specified job and execution. -- This will create and return an empty clob if the parameter -- has not yet been initialized, so the caller must commit after -- this call. p_for_update must be set to 1 if the caller intends -- to update the parameter -- FUNCTION get_large_param(p_job_id RAW, p_execution_id RAW, p_parameter_name VARCHAR2, p_for_update NUMBER DEFAULT 0) RETURN CLOB IS BEGIN -- do not make this call positional; it will cause compilation failure RETURN get_large_param(p_job_id => p_job_id, p_execution_id => p_execution_id, p_nested_job_name => NULL, p_parameter_name => p_parameter_name, p_for_update => p_for_update); END; -- -- Return the large paremeter having the specified name -- corresponding to the specified job and execution. -- This will create and return an empty clob if the parameter -- has not yet been initialized, so the caller must commit after -- this call. p_for_update must be set to 1 if the caller intends -- to update the parameter -- FUNCTION get_large_param(p_job_id RAW, p_nested_job_name VARCHAR2, p_parameter_name VARCHAR2, p_for_update NUMBER DEFAULT 0) RETURN CLOB IS BEGIN -- do not make this call positional; it will cause compilation failure RETURN get_large_param(p_job_id => p_job_id, p_execution_id => NULL, p_nested_job_name =>p_nested_job_name, p_parameter_name => p_parameter_name, p_for_update => p_for_update); END; -- Return the large parameter within the specified job that -- associated with the the nested job p_nested_job_name FUNCTION get_large_param(p_job_name VARCHAR2, p_job_owner VARCHAR2, p_is_library NUMBER, p_nested_job_name VARCHAR2, p_parameter_name VARCHAR2, p_for_update NUMBER DEFAULT 0) RETURN CLOB IS l_job_id MGMT_JOB.job_id%TYPE; BEGIN l_job_id := get_job_id(p_job_name, p_job_owner, p_is_library); -- do not make this call positional; it will cause compilation failure RETURN get_large_param(p_job_id => l_job_id, p_execution_id => NULL, p_nested_job_name => p_nested_job_name, p_parameter_name => p_parameter_name, p_for_update => p_for_update); END; PROCEDURE edit_job(p_job_id RAW, p_description VARCHAR2, p_params MGMT_JOB_PARAM_LIST, p_targets MGMT_JOB_TARGET_LIST, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_overridden_creds MGMT_JOB_CRED_ARRAY DEFAULT NULL, p_job_notify_states SMP_EMD_INTEGER_ARRAY DEFAULT NULL) IS l_target_list_array MGMT_JOB_TARGET_LIST_ARRAY := null; BEGIN IF p_targets IS NOT NULL THEN l_target_list_array := MGMT_JOB_TARGET_LIST_ARRAY(); l_target_list_array.extend(1); l_target_list_array(1) := p_targets; END IF; edit_job(p_job_id, p_description, p_params, l_target_list_array, p_schedule, p_overridden_creds, p_job_notify_states); END; -- Update status of a step; externally callable method PROCEDURE update_step_status(p_step_id INTEGER, p_step_status INTEGER, p_status_code INTEGER, p_status_code_category NUMBER DEFAULT STATUS_CATEGORY_APP, p_force_schedule BOOLEAN DEFAULT false, p_step_being_scheduled BOOLEAN DEFAULT false) IS dummy NUMBER; BEGIN -- Lock all the rows for this execution to serialize updates BEGIN dummy := lock_executions(p_step_id); EXCEPTION -- This could happen in very rare cases when trying to -- update steps for a system job, or if a long-running -- execution was deleted. WHEN NO_DATA_FOUND THEN RETURN; END; update_step_status_nolock(p_step_id, p_step_status, p_status_code, p_status_code_category, p_force_schedule, p_step_being_scheduled); END; -- Delete the specified execution, and all its source executions, if any PROCEDURE delete_execution_chain(p_execution_id RAW) IS l_source_exec_id RAW(16); BEGIN SELECT source_execution_id INTO l_source_exec_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; delete_job_execution(p_execution_id, false, true, false); IF l_source_exec_id != p_execution_id THEN delete_execution_chain(l_source_exec_id); END IF; END; -- Restart a system job. This is a special form of restart that -- does not make a copy of the execution PROCEDURE restart_system_job(p_execution_id RAW) IS l_start_time DATE := SYSDATE_UTC() + mins_to_interval(2); l_step_count INTEGER := 0; BEGIN -- Look for all failed/aborted steps. Set them to SCHEDULED FOR crec IN (SELECT step_id FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND step_type=STEPTYPE_STEP AND step_status IN (FAILED_STATUS, ABORTED_STATUS)) LOOP UPDATE MGMT_JOB_EXECUTION SET step_status=SCHEDULED_STATUS, start_time=l_start_time, sequence_number=0 WHERE step_id IN ( SELECT step_id FROM MGMT_JOB_EXECUTION START WITH step_id=crec.step_id CONNECT BY step_id=prior parent_step_id); l_step_count := l_step_count+1; END LOOP; -- For all stepsets, reset the num_children_completed -- to the correct value FOR crec IN (SELECT step_id FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id AND num_children IS NOT NULL) LOOP UPDATE MGMT_JOB_EXECUTION SET num_children_completed=(SELECT COUNT(*) FROM MGMT_JOB_EXECUTION WHERE parent_step_id=crec.step_id AND step_status = COMPLETED_STATUS) WHERE step_id=crec.step_id; END LOOP; UPDATE MGMT_JOB_EXEC_SUMMARY SET status=SCHEDULED_STATUS, start_time=l_start_time WHERE execution_id=p_execution_id; END; -- Update the status of the execution, and move it to history. All times -- are in utc. Note: p_start_time is the expected start time of the exec. PROCEDURE move_execution_to_history(p_job_id RAW, p_execution_id RAW, p_status NUMBER, p_start_time DATE, p_end_time DATE, p_schedule_next BOOLEAN DEFAULT true, p_error_code NUMBER DEFAULT 0, p_error_code_category NUMBER DEFAULT STATUS_CATEGORY_APP) IS l_system_job NUMBER; l_keep_system_jobs MGMT_PARAMETERS.parameter_value%TYPE := 'FALSE'; l_new_exec_id RAW(16); l_target_list_index NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_queue_id RAW(16); l_corrective_action MGMT_JOB.is_corrective_action%TYPE; l_current_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_new_exec_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_waiting_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id, p_execution_id); -- Release any locks held by the execution; this could -- end up resuming executions blocked on the locks release_execution_locks(p_execution_id, l_job_type_id); SELECT status INTO l_current_status FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; UPDATE MGMT_JOB_EXEC_SUMMARY SET status=p_status, status_code=p_error_code, status_code_category=p_error_code_category, end_time=p_end_time WHERE execution_id=p_execution_id RETURNING queue_id INTO l_queue_id; -- If this is a system job, remove the execution and the job -- from the schema. SELECT system_job INTO l_system_job FROM MGMT_JOB WHERE job_id=p_job_id; IF l_system_job=SYSTEM_JOB_RETRY AND (p_status=ABORTED_STATUS OR p_status=FAILED_STATUS) THEN -- Retry the previous execution if it failed, from the point -- of failure restart_system_job(p_execution_id); RETURN; ELSE BEGIN SELECT is_corrective_action INTO l_corrective_action FROM MGMT_JOB WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; -- If this job/execution has a periodic schedule, we wanna -- schedule the next execution. Note that p_schedule_next -- could be false if this execution was killed. IF p_schedule_next AND l_corrective_action !=1 THEN -- If a waiting execution is being stopped, -- schedule the next one IF l_current_status=WAITING_STATUS THEN l_new_exec_status := WAITING_STATUS; ELSE l_new_exec_status := SCHEDULED_STATUS; END IF; schedule_next_job_execution(p_job_id, p_execution_id, p_start_time, p_end_time, l_new_exec_status); ELSIF NOT p_schedule_next THEN BEGIN -- Check if there are any waiting executions for this -- target list index: if so, stop and remove them SELECT e2.execution_id INTO l_waiting_exec_id FROM MGMT_JOB_EXEC_SUMMARY e1, MGMT_JOB_EXEC_SUMMARY e2 WHERE e1.execution_id=p_execution_id AND e1.job_id=e2.job_id AND e1.target_list_index=e2.target_list_index AND e2.status=WAITING_STATUS; stop_execution(l_waiting_exec_id, false, false, true, true); BEGIN delete_job_execution(l_waiting_exec_id, false, false); EXCEPTION -- This could fail for system jobs WHEN OTHERS THEN NULL; END; EXCEPTION WHEN NO_DATA_FOUND THEN -- No waiting executions found NULL; END; END IF; END IF; -- If this execution was part of a queue, then dequeue it -- this will possibly schedule other executions in the queue IF l_queue_id IS NOT NULL THEN remove_execution_from_queue(l_queue_id, p_execution_id); END IF; BEGIN SELECT upper(parameter_value) INTO l_keep_system_jobs FROM MGMT_PARAMETERS WHERE parameter_name='keep_system_job_history'; EXCEPTION WHEN NO_DATA_FOUND THEN l_keep_system_jobs := 'FALSE'; END; IF l_system_job IN (SYSTEM_JOB, SYSTEM_JOB_RETRY) AND l_keep_system_jobs = 'FALSE' THEN -- Remove the current execution (and dependent executions) -- from the schema delete_execution_chain(p_execution_id); ELSE -- The execution is now in history; long may it live. Remove it -- from the execution table. DELETE FROM MGMT_JOB_EXECUTION WHERE execution_id=p_execution_id; END IF; END; -- Reschedule a step PROCEDURE reschedule_step(p_job_id RAW, p_execution_id RAW, p_step_id NUMBER, p_source_step_id INTEGER, p_original_step_id INTEGER, p_step_name VARCHAR2, p_iterate_param VARCHAR2, p_iterate_param_index NUMBER, p_parent_step_id INTEGER, p_restart_mode INTEGER, p_tzregion VARCHAR2, p_step_status NUMBER, p_start_time DATE) IS l_count NUMBER := 0; l_step_id NUMBER; l_start_time DATE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN l_job_type_id := get_job_type_id(p_job_id, p_execution_id); IF p_step_status=SCHEDULED_STATUS THEN -- Check to see whether this step has parameter sources associated -- with it, and whether they are "volatile" (need to be reexecuted). -- This only applies with credential and property sources when -- cluster targets are involved SELECT COUNT(1) INTO l_count FROM MGMT_JOB_PARAM_SOURCE s WHERE s.job_type_id=l_job_type_id AND step_name=p_step_name AND step_type=STEPTYPE_STEP AND source_type IN (SOURCE_TYPE_PROP, SOURCE_TYPE_CRED); END IF; IF l_count > 0 THEN DELETE FROM MGMT_JOB_EXECUTION WHERE job_id=p_job_id AND execution_id=p_execution_id AND step_name=p_step_name AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC); DELETE FROM MGMT_JOB_HISTORY WHERE job_id=p_job_id AND execution_id=p_execution_id AND step_name=p_step_name AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC); -- Schedule a "re-execute param sources" step l_step_id := insert_scheduled_entry(p_job_id, p_execution_id, p_source_step_id, p_original_step_id, p_restart_mode, p_step_name, STEPTYPE_PARAMSRC_RETRY, p_iterate_param, p_iterate_param_index, p_parent_step_id, p_start_time, p_tzregion, SCHEDULED_STATUS, SYSTEM_COMMAND); ELSE -- Simply set the step to the specified status IF p_step_status=SCHEDULED_STATUS THEN UPDATE MGMT_JOB_EXECUTION SET step_status=p_step_status, start_time=p_start_time, dispatcher_id=-1 WHERE step_id=p_step_id; ELSE UPDATE MGMT_JOB_EXECUTION SET step_status=p_step_status, dispatcher_id=-1 WHERE step_id=p_step_id; END IF; END IF; END; -- Update the status of a step; internal method -- If p_force_schedule is true, then schedule the step dependencies even if -- it has previously run (this is used during restart) -- If p_step_being_scheduled is true, then the step is currently -- being scheduled (it aborted or completed immediately). When -- this happens, parent information is not updated. PROCEDURE update_step_status_nolock(p_step_id INTEGER, p_step_status INTEGER, p_status_code INTEGER, p_status_code_category NUMBER DEFAULT STATUS_CATEGORY_APP, p_force_schedule BOOLEAN DEFAULT false, p_step_being_scheduled BOOLEAN DEFAULT false) IS l_incoming_edge_type INTEGER; l_parent_step_id INTEGER; l_num_children_completed INTEGER; l_num_children INTEGER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_origin_step_name VARCHAR2(64); l_job_id RAW(16); l_top_job_id RAW(16); l_execution_id RAW(16); l_source_step_id INTEGER := -1; l_original_step_id INTEGER := -1; l_source_parent_step_id INTEGER := -1; l_orig_parent_step_id INTEGER := -1; l_restart_mode INTEGER; l_parent_restart_mode INTEGER; l_step_name VARCHAR2(64); l_step_type INTEGER; l_stepset_name VARCHAR2(64); l_stepset_type INTEGER; l_stepset_status INTEGER; l_outgoing_edge_found boolean := false; l_iterate_param MGMT_JOB_EXECUTION.iterate_param%TYPE; l_iterate_param_index NUMBER; l_execution_status NUMBER; l_state_to_update NUMBER; l_step_status INTEGER; l_abort_itserial_stepset BOOLEAN := false; l_prev_step_status NUMBER; l_start_time DATE; l_end_time DATE; l_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_current_time DATE; l_step_already_done BOOLEAN := false; l_scheduled_step_id NUMBER; l_executing_count NUMBER; l_job_state NUMBER; l_schedule_next BOOLEAN; l_schedule MGMT_JOB_SCHEDULE_RECORD; l_active_count NUMBER; BEGIN -- Update step status l_step_status := compute_step_status(p_step_id, p_step_status); BEGIN SELECT job_id, execution_id, step_name, step_type, step_status, parent_step_id, source_step_id, original_step_id, iterate_param, iterate_param_index, restart_mode, start_time, end_time, timezone_region INTO l_job_id, l_execution_id, l_step_name, l_step_type, l_prev_step_status, l_parent_step_id, l_source_step_id, l_original_step_id, l_iterate_param, l_iterate_param_index, l_restart_mode, l_start_time, l_end_time, l_tzregion FROM MGMT_JOB_EXECUTION WHERE step_id=p_step_id; l_job_type_id := get_job_type_id(l_job_id, l_execution_id); -- Figure out the current state of the job (SUSPENDED/EXECUTING) SELECT status, job_id, target_list_index INTO l_execution_status, l_top_job_id, l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=l_execution_id; -- If the current status is SCHEDULED, then retry the step IF l_step_status = SCHEDULED_STATUS THEN IF l_prev_step_status = EXECUTING_STATUS THEN -- Check to see that the execution was not -- put in a suspended state since the step -- was last picked up IF is_system_suspended_status(l_execution_status) THEN l_state_to_update := l_execution_status; l_start_time := SYSDATE_UTC(); ELSE l_state_to_update := SCHEDULED_STATUS; l_start_time := SYSDATE_UTC() + mins_to_interval(1); END IF; ELSIF is_system_suspended_status(l_prev_step_status) OR l_prev_step_status=QUEUED_STATUS THEN -- The step (and therefore the execution) were in -- a suspended state, and the execution is being -- resumed. Retain the earlier start time l_state_to_update := SCHEDULED_STATUS; END IF; reschedule_step(l_job_id, l_execution_id, p_step_id, l_source_step_id, l_original_step_id, l_step_name, l_iterate_param, l_iterate_param_index, l_parent_step_id, l_restart_mode, l_tzregion, l_state_to_update, l_start_time); RETURN; END IF; IF l_prev_step_status != STOPPED_STATUS AND l_prev_step_status != ABORTED_STATUS AND l_prev_step_status != COMPLETED_STATUS AND l_prev_step_status != FAILED_STATUS THEN l_current_time := SYSDATE_UTC(); UPDATE MGMT_JOB_EXECUTION set step_status = l_step_status, step_status_code=p_status_code, step_status_code_category=p_status_code_category, end_time=l_current_time, dispatcher_id=-1 WHERE step_id=p_step_id RETURNING end_time INTO l_end_time; ELSIF p_force_schedule = false THEN -- The step was previously marked as stopped, aborted, -- completed or failed. Do not schedule the dependencies -- again unless specifically asked to do so! RETURN; ELSE l_step_already_done := true; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- It is possible that we received this notification for an -- execution that was stopped and immediately removed. IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('IGNORING NO_DATA_FOUND', MODULE_NAME); END IF; RETURN; END; -- If the status is not completed, failed or aborted, we're done IF l_step_status != COMPLETED_STATUS AND l_step_status != FAILED_STATUS AND l_step_status != ABORTED_STATUS THEN RETURN; END IF; -- The current time in UTC l_current_time := SYSDATE_UTC(); IF l_parent_step_id != -1 THEN SELECT source_step_id, original_step_id, restart_mode INTO l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode FROM MGMT_JOB_EXECUTION WHERE step_id=l_parent_step_id; END IF; -- Figure out what status any new step(s) added to the execution -- will go to. IF is_system_suspended_status(l_execution_status) THEN l_state_to_update := l_execution_status; ELSIF l_execution_status = STOP_PENDING_STATUS THEN -- Stop the execution and send it to history if there -- are no more executing steps SELECT COUNT(1) INTO l_executing_count FROM MGMT_JOB_EXECUTION WHERE execution_id=l_execution_id AND step_type=STEPTYPE_STEP AND step_status=EXECUTING_STATUS; IF l_executing_count=0 THEN UPDATE MGMT_JOB_EXEC_SUMMARY SET status=STOPPED_STATUS, end_time=l_current_time WHERE execution_id=l_execution_id RETURNING start_time, end_time, target_list_index INTO l_start_time, l_end_time, l_target_list_index; -- Check whether the job has been stopped or not -- Join with MGMT_JOB_EXEC_SUMMARY to determine job_id of top level -- job SELECT job_status INTO l_job_state FROM MGMT_JOB j, MGMT_JOB_EXEC_SUMMARY je WHERE je.execution_id=l_execution_id AND j.job_id=je.job_id; IF l_job_state=JOB_STATUS_STOPPED THEN l_schedule_next := false; ELSE l_schedule_next := true; END IF; --mark all steps in EXECUTING_STATUS, STOP_PENDING_STATUS --as STOPPED_STATUS --STEPTYPE_JOB is handled too because its status was set to --STOP_PENDING_STATUS in stop_execution UPDATE MGMT_JOB_EXECUTION SET step_status=STOPPED_STATUS, end_time=l_current_time WHERE execution_id=l_execution_id AND step_status in (EXECUTING_STATUS, STOP_PENDING_STATUS); move_stopped_exec_to_history(l_execution_id, l_target_list_index, l_start_time, l_end_time, l_schedule_next); END IF; -- Do not schedule any more steps RETURN; ELSIF l_execution_status = SUSPEND_PENDING_STATUS THEN -- The execution is pending suspension. Further steps -- are scheduled, but they are put into suspended status l_state_to_update := SUSPENDED_STATUS; -- Determine whether the execution can now be suspended -- We can do this if there are no more executing steps SELECT COUNT(1) INTO l_executing_count FROM MGMT_JOB_EXECUTION WHERE execution_id=l_execution_id AND step_type=1 AND step_status=EXECUTING_STATUS; IF l_executing_count=0 THEN UPDATE MGMT_JOB_EXEC_SUMMARY SET status=SUSPENDED_STATUS, suspend_time=l_current_time WHERE execution_id=l_execution_id; UPDATE MGMT_JOB_EXECUTION SET step_status=SUSPENDED_STATUS WHERE execution_id=l_execution_id AND parent_step_id=-1; process_suspend_callbacks(l_job_id, l_execution_id, SUSPENDED_STATUS); END IF; ELSE l_state_to_update := SCHEDULED_STATUS; END IF; -- If this is a step of type STEPTYPE_PARAMSRC, then -- schedule the actual step it is associated with IF l_step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) THEN -- Get the step type of the step that this -- paramsource entry is associated with SELECT step_type INTO l_step_type FROM MGMT_JOB_EXECPLAN e WHERE e.job_type_id=l_job_type_id AND step_name=l_step_name AND (origin_step_name IS NULL OR origin_step_name != l_step_name); -- Schedule the actual entity associated with the -- paramsource step, only if the parameter source -- completed IF l_step_status != ABORTED_STATUS THEN schedule(l_job_id, l_execution_id, l_parent_step_id, l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode, l_job_type_id, l_step_name, l_step_type, l_iterate_param, l_iterate_param_index, l_current_time, l_tzregion, l_state_to_update); ELSE -- Abort the parent as well update_step_status_nolock(l_parent_step_id, ABORTED_STATUS, 0); END IF; RETURN; END IF; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling next step for step ' || p_step_id, MODULE_NAME); EMDW_LOG.info('Parent step id is ' || l_parent_step_id, MODULE_NAME); END IF; -- Update the number of children counter for the parent stepset IF l_parent_step_id != -1 THEN IF EMDW_LOG.p_is_info_set THEN IF l_step_already_done THEN EMDW_LOG.info('Step ' || p_step_id || ': step already done: true', MODULE_NAME); ELSE EMDW_LOG.info('Step ' || p_step_id || ': step already done: false', MODULE_NAME); END IF; IF p_step_being_scheduled THEN EMDW_LOG.info('Step ' || p_step_id || ': step being scheduled: true', MODULE_NAME); ELSE EMDW_LOG.info('Step ' || p_step_id || ': step being scheduled: false', MODULE_NAME); END IF; END IF; -- Update the reference count for the parent, IF -- The step has not already finished AND -- The step is not currently being scheduled IF NOT l_step_already_done AND NOT p_step_being_scheduled THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Fetching child info for step ' || p_step_id || ' parent ' || l_parent_step_id, MODULE_NAME); END IF; SELECT num_children, num_children_completed INTO l_num_children, l_num_children_completed FROM MGMT_JOB_EXECUTION where step_id=l_parent_step_id; l_num_children_completed := l_num_children_completed+1; UPDATE MGMT_JOB_EXECUTION SET num_children_completed = l_num_children_completed WHERE step_id=l_parent_step_id; END IF; ELSE -- This is the entry for the job itself. SELECT expected_start_time INTO l_start_time FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=l_execution_id; move_execution_to_history(l_job_id, l_execution_id, l_step_status, l_start_time, l_end_time, true, p_status_code); IF (NOT has_active_executions(l_job_id)) THEN UPDATE MGMT_JOB SET job_status=JOB_STATUS_EXPIRED WHERE job_id = l_job_id; END IF; RETURN; END IF; -- Figure out the incoming edge type for this step(set) -- Notice that the incoming edge may not be available if we're -- dealing with the entry for a nested job BEGIN SELECT incoming_edge_type, origin_step_name, stepset_name INTO l_incoming_edge_type, l_origin_step_name, l_stepset_name FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND step_name = l_step_name AND step_type = l_step_type; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Incoming edge type is ' || l_incoming_edge_type, MODULE_NAME); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN l_incoming_edge_type := -1; END; IF l_incoming_edge_type = -1 THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Numchildren ' || l_num_children || ',' || 'NumChildrenCompleted ' || l_num_children_completed, MODULE_NAME); END IF; IF l_num_children_completed >= l_num_children THEN IF l_parent_step_id != -1 THEN update_step_set_status(l_parent_step_id, l_stepset_name, l_step_status, p_status_code, p_status_code_category); END IF; ELSE -- If this is the last step in the execution, this indicates -- a corrupted job type; Abort the execution SELECT COUNT(*) INTO l_active_count FROM MGMT_JOB_EXECUTION WHERE execution_id=l_execution_id AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) AND step_status NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS); IF l_active_count=0 THEN force_abort_execution(l_execution_id, 'No successors found for step ' || l_step_name); END IF; END IF; ELSIF l_incoming_edge_type = ETYPE_PARALLEL_BEGIN OR l_incoming_edge_type = ETYPE_ITERATIVEP_BEGIN OR l_incoming_edge_type = ETYPE_ITERATIVES_BEGIN THEN -- This is a step(set) that has executed within a parallel stepset -- or an iterative child stepset that has executed within a parent. -- Compute the status of the stepset, only if all children have -- completed IF l_incoming_edge_type = ETYPE_ITERATIVES_BEGIN THEN -- This is an iterative serial stepset -- Determine whether the stepset should abort on -- failure l_abort_itserial_stepset := false; IF l_step_status=ABORTED_STATUS OR l_step_status=FAILED_STATUS THEN l_abort_itserial_stepset := get_itserial_halt_on_failure(l_job_type_id, l_step_name); END IF; l_iterate_param_index := l_iterate_param_index+1; IF l_abort_itserial_stepset THEN update_step_set_status(l_parent_step_id, l_stepset_name, l_step_status, p_status_code, p_status_code_category); ELSIF schedule(l_job_id, l_execution_id, l_parent_step_id, l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode, l_job_type_id, l_step_name, STEPTYPE_ITSERIAL_STEPSET, l_iterate_param, l_iterate_param_index, l_iterate_param_index, l_current_time, l_tzregion, l_state_to_update) = 0 THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('ITERATIVE SERIAL stepset ' || l_parent_step_id || ' completed', MODULE_NAME); END IF; update_step_set_status(l_parent_step_id, l_stepset_name, l_step_status, p_status_code, p_status_code_category); END IF; ELSIF l_num_children > 0 AND l_num_children = l_num_children_completed THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('PARALLEL/ITERATIVE stepset ' || l_parent_step_id || ' completed', MODULE_NAME); END IF; -- Compute the status of the stepset, kick off -- any SERIAL_END dependencies update_step_set_status(l_parent_step_id, l_stepset_name, l_step_status, p_status_code, p_status_code_category); END IF; ELSE -- This step(set) has executed after another step(set) -- Are there any outgoing edges in the graph from this step? -- There could be a SERIAL_END edge, or one of (SUCCESSOF/FAILUREOF) -- Note: the code here assumes that the value for the serial_end -- edge type is less than that of the successOf/failureOf/abortOf -- edges l_outgoing_edge_found := false; FOR crec in (SELECT step_name, step_type, incoming_edge_type FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND origin_step_name = l_step_name AND origin_step_type = l_step_type ORDER BY incoming_edge_type) LOOP IF crec.incoming_edge_type = ETYPE_SERIAL_END THEN -- This is a serial edge; schedule it anyway IF l_step_status != ABORTED_STATUS THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling step ' || crec.step_name, MODULE_NAME); END IF; schedule(l_job_id, l_execution_id, l_parent_step_id, l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode, l_job_type_id, crec.step_name, crec.step_type, l_iterate_param, l_iterate_param_index, l_current_time, l_tzregion, l_state_to_update); l_outgoing_edge_found := true; EXIT; END IF; ELSIF crec.incoming_edge_type = ETYPE_SUCCESSOF_END THEN -- Schedule this edge only if the current step succeeded IF l_step_status = COMPLETED_STATUS then IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling step ' || crec.step_name, MODULE_NAME); END IF; schedule(l_job_id, l_execution_id, l_parent_step_id, l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode, l_job_type_id, crec.step_name, crec.step_type, l_iterate_param, l_iterate_param_index, l_current_time, l_tzregion, l_state_to_update); l_outgoing_edge_found := true; EXIT; END IF; ELSIF crec.incoming_edge_type = ETYPE_FAILUREOF_END THEN -- Schedule this edge only if the current step failed IF l_step_status = FAILED_STATUS THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling step ' || crec.step_name, MODULE_NAME); END IF; schedule(l_job_id, l_execution_id, l_parent_step_id, l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode, l_job_type_id, crec.step_name, crec.step_type, l_iterate_param, l_iterate_param_index, l_current_time, l_tzregion, l_state_to_update); l_outgoing_edge_found := true; EXIT; END IF; ELSIF crec.incoming_edge_type = ETYPE_ABORTOF_END THEN -- Schedule this edge only if the current step failed IF l_step_status = ABORTED_STATUS THEN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Scheduling step ' || crec.step_name, MODULE_NAME); END IF; schedule(l_job_id, l_execution_id, l_parent_step_id, l_source_parent_step_id, l_orig_parent_step_id, l_parent_restart_mode, l_job_type_id, crec.step_name, crec.step_type, l_iterate_param, l_iterate_param_index, l_current_time, l_tzregion, l_state_to_update); l_outgoing_edge_found := true; EXIT; END IF; END IF; END LOOP; IF NOT l_outgoing_edge_found THEN -- This is the last step in a serial stepset -- Update the status of the step-set, and kick off dependencies IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Serial stepset ' || l_parent_step_id || ' done', MODULE_NAME); END IF; update_step_set_status(l_parent_step_id, l_stepset_name, l_step_status, p_status_code, p_status_code_category); END IF; END IF; EXCEPTION -- When unhandled errors are encountered, abort the execution WHEN NO_DATA_FOUND OR TOO_MANY_ROWS OR DUP_VAL_ON_INDEX OR SUBSCRIPT_BEYOND_COUNT OR COLLECTION_IS_NULL OR VALUE_ERROR THEN -- If we get an unhandled exception here, we are very likely -- to get it when we try to process the step force_abort_execution(l_execution_id, 'Execution aborted because of unhandled error: ' || SQLERRM); END; -- Compute the status of a step that executed a remote command -- from the exit code of the command FUNCTION get_async_step_status(p_step_id INTEGER, p_exit_code INTEGER) RETURN INTEGER IS l_success_codes MGMT_JOB_PARAMETER.scalar_value%TYPE; l_failure_codes MGMT_JOB_PARAMETER.scalar_value%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_id MGMT_JOB.job_id%TYPE; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_async_error_received NUMBER; EMD_INPUT_WRITE_ERROR NUMBER := 3; BEGIN SELECT job_id, execution_id, async_error_received INTO l_job_id, l_execution_id, l_async_error_received FROM MGMT_JOB_EXECUTION WHERE step_id=p_step_id; l_job_type_id := get_job_type_id(l_job_id, l_execution_id); -- If an async error notification was received, then the status is -- either failed or aborted. It is aborted for all status codes -- other than "Error reading input from Command" IF l_async_error_received=1 THEN IF p_exit_code=EMD_INPUT_WRITE_ERROR THEN -- Fall through; the exit code is actually an -- application exit code. NULL; ELSE -- The error code is a system error code, and the -- step should be aborted RETURN ABORTED_STATUS; END IF; END IF; -- Look for a parameter called successStatus BEGIN SELECT scalar_value INTO l_success_codes FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=l_job_type_id AND step_name=(SELECT step_name FROM MGMT_JOB_EXECUTION WHERE step_id=p_step_id) AND param_name='successStatus'; EXCEPTION WHEN NO_DATA_FOUND THEN l_success_codes := '0'; WHEN OTHERS THEN raise; END; -- Look for a parameter called failureStatus BEGIN SELECT scalar_value INTO l_failure_codes FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=l_job_type_id AND step_name=(SELECT step_name FROM MGMT_JOB_EXECUTION WHERE step_id=p_step_id) AND param_name='failureStatus'; EXCEPTION WHEN NO_DATA_FOUND THEN l_failure_codes := '*'; WHEN OTHERS THEN raise; END; -- Check if the exit code is a specified success code IF l_success_codes IS NOT NULL THEN IF match_number(l_success_codes, p_exit_code) = 1 THEN RETURN COMPLETED_STATUS; END IF; END IF; -- Check if the exit code is a specified failure code IF l_failure_codes IS NOT NULL THEN IF match_number(l_failure_codes, p_exit_code) = 1 THEN RETURN FAILED_STATUS; END IF; END IF; -- If the code was neither a success nor failure, then the step -- was aborted RETURN ABORTED_STATUS; END; -- Update the step sequence number with the specified sequence number. -- Return true if the existing sequence number is less then the -- specified sequence number. This routine also returns false if -- the step is already marked as completed, failed, or aborted (ie -- no further updates to its status are possible) FUNCTION update_sequence_number(p_step_id INTEGER, p_sequence_number INTEGER) RETURN BOOLEAN IS l_step_status INTEGER; BEGIN UPDATE MGMT_JOB_EXECUTION SET sequence_number=p_sequence_number WHERE step_id=p_step_id AND sequence_number < p_sequence_number RETURNING step_status INTO l_step_status; IF SQL%ROWCOUNT=0 THEN RETURN false; ELSE RETURN true; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN false; END; -- Update the step status from the exit code returned by -- a completed remote command. Schedule subsequent -- steps, if any PROCEDURE update_async_op_status(p_step_id INTEGER, p_exit_code INTEGER, p_sequence_number INTEGER) IS l_step_status INTEGER; l_status_category MGMT_JOB_EXECUTION.step_status_code_category%TYPE; BEGIN -- Does pl/sql do short-circuit evaluation? IF p_sequence_number >= 0 THEN IF NOT update_sequence_number(p_step_id, p_sequence_number) THEN RETURN; END IF; END IF; -- Note that the async op could be a remote op or a file transfer. -- Convert the status code to a job system status -- (completed/failed/aborted) l_step_status := get_async_step_status(p_step_id, p_exit_code); -- Set the appropriate status category IF l_step_status=ABORTED_STATUS THEN l_status_category := STATUS_CATEGORY_INTERNAL; ELSE l_status_category := STATUS_CATEGORY_APP; END IF; update_step_status(p_step_id, l_step_status, p_exit_code); END; -- Execute parameter sources for the specified step, -- as well as scheduling the next entries in the execution. -- Return the status of the execution (COMPLETED/ABORTED) -- p_retry is obsolete and will be ignored FUNCTION execute_param_sources(p_step_id NUMBER, p_first_step NUMBER, p_retry NUMBER) RETURN NUMBER IS l_job_id MGMT_JOB.job_id%TYPE; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_step_name MGMT_JOB_EXECUTION.step_name%TYPE; l_step_type MGMT_JOB_EXECUTION.step_type%TYPE; l_orig_step_type MGMT_JOB_EXECUTION.step_type%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_error_msg VARCHAR2(4000); l_status NUMBER := EXECUTING_STATUS; l_creds_set NUMBER; BEGIN SELECT j.job_id, execution_id, e.step_name, e.step_type INTO l_job_id, l_execution_id, l_step_name, l_orig_step_type FROM MGMT_JOB_EXECUTION e, MGMT_JOB j WHERE e.job_id=j.job_id AND e.step_id=p_step_id; l_job_type_id := get_job_type_id(l_job_id, l_execution_id); -- Note that for iterative stepsets, there will be two -- entries with the same step name. The inner serial -- stepset has the origin step name set to the same -- value as the step name. Param sources are always -- associated with the outer iterative stepset entry SELECT step_type INTO l_step_type FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND step_name=l_step_name AND (origin_step_name IS NULL OR origin_step_name != l_step_name); -- First, execute all parameter sources for this step IF l_orig_step_type IN (STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC) THEN l_creds_set := fetch_job_parameters(l_job_id, l_execution_id, l_step_name, l_step_type, 0, l_orig_step_type); IF l_creds_set=0 THEN -- This MUST be a paramsrc entry. Update the step status to -- SUSPENDED_CREDS, it will be retried when the execution -- is resumed update_step_status(p_step_id, SUSPENDED_CREDS_STATUS, 0); RETURN SUSPENDED_CREDS_STATUS; END IF; END IF; -- If this is the first step of the execution, then -- evaluate the security info and lock info sections IF p_first_step=1 THEN -- Check security privileges check_security_info(l_job_id, l_execution_id, 0); -- Try to obtain locks requested by the execution -- If this is not a paramsrc step, set the status of the step -- temporarily to SCHEDULED so that it can be suspended, if -- required IF l_orig_step_type != STEPTYPE_PARAMSRC AND l_orig_step_type != STEPTYPE_PARAMSRC_RETRY AND l_orig_step_type != STEPTYPE_PARAMSRC_RETRY_EXEC THEN UPDATE MGMT_JOB_EXECUTION SET step_status=SCHEDULED_STATUS WHERE step_id=p_step_id; END IF; l_status := get_execution_locks(l_job_id, l_execution_id); -- If the execution was not suspended, flip the status -- back to the original IF l_status != SUSPENDED_LOCK_STATUS THEN UPDATE MGMT_JOB_EXECUTION SET step_status=EXECUTING_STATUS WHERE step_id=p_step_id; END IF; END IF; -- If we're here, all of them succeeded. Update the status, -- which could also result in further steps being scheduled IF (l_orig_step_type=STEPTYPE_PARAMSRC) OR (l_orig_step_type=STEPTYPE_PARAMSRC_RETRY) OR (l_orig_step_type=STEPTYPE_PARAMSRC_RETRY_EXEC) THEN update_step_status(p_step_id, COMPLETED_STATUS, 0); RETURN COMPLETED_STATUS; END IF; RETURN l_status; EXCEPTION -- One of the parameter sources failed WHEN OTHERS THEN ROLLBACK; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('One or more parameter srcs failed: ' || SQLERRM, MODULE_NAME); EMDW_LOG.info('Step id is ' || p_step_id, MODULE_NAME); END IF; l_error_msg := 'Error evaluating parameter sources/security checks/locks ' || SQLERRM; -- Write the error message as the error for the step write_step_error_message(p_step_id, l_error_msg); update_step_status(p_step_id, ABORTED_STATUS, 1); RETURN ABORTED_STATUS; END; -- fix 7714073 -- This procedure tries to submit a BOOKEEPING_STEP and it is -- meant to be called from EM_JOB_CALLBACK.process_queued_changes. PROCEDURE schedule_bookkeeping_step IS BEGIN INSERT INTO MGMT_JOB_EXECUTION (job_id, execution_id, step_id, source_step_id, original_step_id, restart_mode, step_name, step_type, command_type, iterate_param, iterate_param_index, parent_step_id, step_status, start_time, timezone_region) VALUES (NULL, NULL, BOOKKEEPING_STEP_ID, NULL, NULL, 0, BOOKKEEPING_STEP_NAME, STEPTYPE_BOOKKEEPING, WAIT_COMMAND, NULL, NULL, NULL, SCHEDULED_STATUS, SYSDATE_UTC(), 'UTC'); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN -- Check for a stuck step - assume that any entry that is still running -- after 30mins since its start has a thread that is stuck. This -- condition would be extremely rare, but if it happens, this is the -- only way to ensure the bookkeeping operations are not stuck -- forever. -- The cases where the OMS crashes while processing the step are already -- handled with the handling of dispatcher death, so this check is only -- for threads that hang. Switching the status back to SCHEDULED ensures -- that at least another thread may process the current backlog. The -- locking limit of 5mins should prevent the new thread too getting -- stuck at the same place the original thread is stuck UPDATE MGMT_JOB_EXECUTION SET step_status = SCHEDULED_STATUS WHERE step_status = EXECUTING_STATUS AND step_id = BOOKKEEPING_STEP_ID AND start_time < (SYSDATE_UTC() - (30/60/24)); -- If no stuck step, clean up history if no step exists in MJE. This -- condition should never occur as we always delete from HISTORY first -- in process_bookkeeping_step IF SQL%ROWCOUNT = 0 THEN -- remove dangling entry in history DELETE FROM MGMT_JOB_HISTORY h WHERE step_id = BOOKKEEPING_STEP_ID AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_EXECUTION e WHERE e.step_id = h.step_id AND e.step_status = h.step_status); END IF; END schedule_bookkeeping_step; -- Return a set of wait steps PROCEDURE get_wait_steps(p_max_steps INTEGER, p_steps OUT MGMT_JOB_STEP_LIST, p_dispatcher_id INTEGER) IS l_step_record MGMT_JOB_STEP_RECORD; l_count INTEGER; l_dummy INTEGER; l_current_time DATE := SYSDATE_UTC(); l_execution_status NUMBER; n INTEGER := 1; CURSOR steps_cur(max_steps INTEGER) is SELECT job_id, execution_id, step_id, step_name, step_type, step_status, command_type, timezone_region FROM ( -- the bookkeeping step has NULL job_id and execution_id, so use dummy -- values so that the step deserialization in Java is unaffected SELECT NO_EXECUTION job_id, NO_EXECUTION execution_id, step_id, step_name, step_type, step_status, command_type, timezone_region FROM MGMT_JOB_EXECUTION j WHERE step_type = STEPTYPE_BOOKKEEPING AND step_status != EXECUTING_STATUS UNION SELECT job_id, execution_id, step_id, step_name, step_type, step_status, command_type, timezone_region FROM MGMT_JOB_EXECUTION j WHERE step_type IN (STEPTYPE_START_WAIT_STEP, STEPTYPE_GRACE_WAIT_STEP) AND command_type = WAIT_COMMAND AND start_time <= l_current_time) WHERE ROWNUM <= max_steps; BEGIN p_steps := MGMT_JOB_STEP_LIST(); l_count := p_steps.count; FOR crec IN steps_cur(p_max_steps) LOOP BEGIN -- Try to lock the execution BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Processing step ' || crec.step_id, MODULE_NAME); END IF; IF crec.step_id = BOOKKEEPING_STEP_ID THEN SELECT step_id INTO l_dummy FROM MGMT_JOB_EXECUTION WHERE step_id = crec.step_id FOR UPDATE NOWAIT; ELSE l_execution_status := lock_executions(crec.step_id, true); END IF; EXCEPTION -- Skip this step if we cannot lock its execution WHEN OTHERS THEN GOTO next_iteration; END; l_step_record := MGMT_JOB_STEP_RECORD(crec.job_id, crec.execution_id, crec.step_name, crec.step_type, 0, 'wait_step', crec.command_type, null, crec.step_id, null, -1, null, 0, null, TRUST_UNDEFINED); p_steps.extend(1); p_steps(n+l_count) := l_step_record; n := n+1; <> NULL; EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS OR DUP_VAL_ON_INDEX OR SUBSCRIPT_BEYOND_COUNT OR COLLECTION_IS_NULL OR VALUE_ERROR THEN -- If we get an unhandled exception here, we are very likely -- to get it when we try to process the step force_abort_execution(crec.execution_id, 'Execution aborted because of unhandled error: ' || SQLERRM); END; END LOOP; END; -- Return true if the specific command uses large parameters, -- and requires the job system to fetch all job parameters -- (to substitute params in the large parameter) -- This is a HACK and is temporary until we come up with a -- better solution FUNCTION need_all_params(p_step_params MGMT_JOB_PARAM_LIST, p_command_name VARCHAR2) RETURN BOOLEAN IS FUNCTION contains_param(p_param_name VARCHAR2, p_param_value VARCHAR2 DEFAULT NULL) RETURN BOOLEAN AS BEGIN FOR i IN 1..p_step_params.COUNT LOOP IF p_step_params(i).param_name=p_param_name THEN IF p_param_value IS NULL THEN RETURN true; ELSIF UPPER(p_step_params(i).scalar_value) = p_param_value THEN RETURN true; END IF; END IF; END LOOP; RETURN false; END; BEGIN IF p_command_name NOT IN ('remoteOp', 'putFile') THEN RETURN false; END IF; IF p_command_name='remoteOp' THEN RETURN contains_param('largeInputParam') AND contains_param('substituteLargeParam','TRUE'); ELSIF p_command_name='putFile' THEN RETURN contains_param('sourceParam') AND contains_param('substituteLargeParam','TRUE'); END IF; RETURN false; END; -- calculate the trust of the step -- if the Trust is defined at step level return it. -- else move up the nested job hierarchy till we find a job_type where -- trust is defined and return its value -- if non of the job_types have the trust defined return NOT_TRUSTED -- FUNCTION get_step_trust(p_step_id NUMBER) RETURN NUMBER IS l_trusted MGMT_JOB_EXECPLAN.trusted%TYPE; l_step_name MGMT_JOB_EXECUTION.step_name%TYPE; l_step_type MGMT_JOB_EXECUTION.step_type%TYPE; l_job_id MGMT_JOB.job_id%TYPE; l_top_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_nested_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_parent_trusts SMP_EMD_INTEGER_ARRAY := SMP_EMD_INTEGER_ARRAY(); l_nested MGMT_JOB.nested%TYPE; l_is_command_trustable MGMT_JOB_COMMAND.is_trustable%TYPE; BEGIN -- find if the step is part of a nested job SELECT nested, nested_job_type_id, s.job_id, s.execution_id, s.step_name, s.step_type INTO l_nested, l_nested_job_type_id, l_job_id, l_execution_id, l_step_name, l_step_type FROM MGMT_JOB_EXECUTION s, MGMT_JOB j WHERE s.step_id = p_step_id AND s.job_id = j.job_id; SELECT job_type_id INTO l_top_job_type_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE execution_id = l_execution_id; IF l_nested = 0 THEN -- check for trust at step leve SELECT ep.trusted, c.is_trustable INTO l_trusted, l_is_command_trustable FROM MGMT_JOB_EXECPLAN ep, MGMT_JOB_COMMAND c WHERE ep.job_type_id = l_top_job_type_id AND ep.step_name = l_step_name AND ep.step_type = l_step_type AND ep.command_name = c.command_name; -- if the command is not trustable return NOT_TRUSTED IF l_is_command_trustable = NON_TRUSTABLE_COMMAND THEN RETURN NOT_TRUSTED; END IF; IF l_trusted != TRUST_UNDEFINED THEN RETURN l_trusted; END IF; -- check for trust at the jobtype level SELECT trusted INTO l_trusted FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_top_job_type_id; IF l_trusted != TRUST_UNDEFINED THEN RETURN l_trusted; END IF; ELSE -- check for trust at step level SELECT ep.trusted, c.is_trustable INTO l_trusted, l_is_command_trustable FROM MGMT_JOB_EXECPLAN ep, MGMT_JOB_COMMAND c WHERE ep.job_type_id = l_nested_job_type_id AND ep.step_name = l_step_name AND ep.step_type = l_step_type AND ep.command_name = c.command_name; -- if the command is not trustable return NOT_TRUSTED IF l_is_command_trustable = NON_TRUSTABLE_COMMAND THEN RETURN NOT_TRUSTED; END IF; IF l_trusted != TRUST_UNDEFINED THEN RETURN l_trusted; END IF; -- then search the nested job hierarchy upwards for trust SELECT jt.trusted BULK COLLECT INTO l_parent_trusts FROM MGMT_JOB j , MGMT_JOB_TYPE_INFO jt, (SELECT level,e.job_id FROM MGMT_JOB_EXECUTION e WHERE (e.step_id = p_step_id OR e.step_type = STEPTYPE_JOB) START WITH e.step_id=p_step_id CONNECT BY e.step_id = PRIOR e.parent_step_id ORDER BY level) e1 WHERE e1.job_id=j.job_id AND j.nested_job_type_id=jt.job_type_id; FOR i IN 1..l_parent_trusts.count LOOP IF l_parent_trusts(i) != TRUST_UNDEFINED THEN -- if trust is defined at nested job level RETURN l_parent_trusts(i); END IF; END LOOP; -- now check the topmost jobtype for trust -- check for trust at the parent jobtype level SELECT trusted INTO l_trusted FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_top_job_type_id; IF l_trusted != TRUST_UNDEFINED THEN RETURN l_trusted; END IF; END IF; RETURN NOT_TRUSTED; END; PROCEDURE bookkeeping_task; -- to process bookkeeping step, call bookkeeping_task PROCEDURE process_bookkeeping_step IS l_status INTEGER; BEGIN bookkeeping_task; -- now the step is no longer required DELETE FROM MGMT_JOB_HISTORY WHERE step_id = BOOKKEEPING_STEP_ID; DELETE FROM MGMT_JOB_EXECUTION WHERE step_id = BOOKKEEPING_STEP_ID; -- commiting here as bookkeeping_task commits internally COMMIT; END; -- Return a set of steps subject to the specified maximum -- p_command_type=0 for short steps, 1 for long steps, 2 for system steps PROCEDURE get_real_scheduled_steps(p_max_steps INTEGER, p_command_type NUMBER, p_steps OUT MGMT_JOB_STEP_LIST, p_dispatcher_id INTEGER) IS l_step_record MGMT_JOB_STEP_RECORD; l_step_params MGMT_JOB_PARAM_LIST; l_job_params MGMT_JOB_PARAM_LIST := NULL; l_command_name MGMT_JOB_EXECPLAN.command_name%TYPE; l_command_class MGMT_JOB_COMMAND.command_class%TYPE; l_iterate_param_index NUMBER := -1; l_trusted MGMT_JOB_TYPE_INFO.trusted%TYPE := NOT_TRUSTED; n INTEGER := 1; l_job_lock_status NUMBER; l_job_type MGMT_JOB_TYPE_INFO.job_type%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_all_params NUMBER; l_oms_name VARCHAR2(256); l_agent_bound INTEGER; l_count INTEGER; l_nested_job NUMBER; l_dummy MGMT_JOB_INT_ARRAY; l_current_time DATE := SYSDATE_UTC(); l_execution_status NUMBER; l_first_step NUMBER; l_c NUMBER; l_parent_step_ids SMP_EMD_INTEGER_ARRAY; l_top_job_id MGMT_JOB.job_id%TYPE; l_expected_start_time MGMT_JOB_EXEC_SUMMARY.expected_start_time%TYPE; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_current_schedule MGMT_JOB_SCHEDULE_RECORD; l_start_grace_period MGMT_JOB_SCHEDULE.start_grace_period%TYPE := NO_START_GRACE; l_next_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_active NUMBER; l_step_status MGMT_JOB_EXECUTION.step_status%TYPE; l_system_job MGMT_JOB.system_job%TYPE; l_is_ca MGMT_JOB.is_corrective_action%TYPE; l_top_step_id MGMT_JOB_EXECUTION.step_id%TYPE; CURSOR steps_cur(max_steps INTEGER, ctype NUMBER) is SELECT job_id, execution_id, step_id, step_name, step_type, iterate_param, iterate_param_index, command_type, timezone_region FROM MGMT_JOB_EXECUTION j WHERE step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC, STEPTYPE_FLATTEN_TARGETS_STEP) AND step_status = SCHEDULED_STATUS AND command_type = ctype AND start_time <= l_current_time AND ROWNUM <= max_steps; BEGIN p_steps := MGMT_JOB_STEP_LIST(); l_count := p_steps.count; BEGIN SELECT host_url INTO l_oms_name FROM MGMT_FAILOVER_TABLE WHERE failover_id = p_dispatcher_id; EXCEPTION WHEN OTHERS THEN MGMT_LOG.log_error(MODULE_NAME, MGMT_GLOBAL.INVALID_JOB_ERR, 'Exception when retrieving oms name for job id ' || p_dispatcher_id || ':' || SQLERRM); l_oms_name := ''; END; FOR crec IN steps_cur(p_max_steps, p_command_type) LOOP BEGIN BEGIN -- make sure, we have entries in MGMT_JOB and MGMT_JOB_EXEC_SUMMARY -- for this step -- get the top level job id SELECT mj.is_corrective_action, mj.job_id, mjes.expected_start_time, mjes.target_list_index INTO l_is_ca, l_top_job_id, l_expected_start_time, l_target_list_index FROM MGMT_JOB mj, MGMT_JOB_EXEC_SUMMARY mjes WHERE mjes.execution_id = crec.execution_id AND mjes.job_id = mj.job_id AND mj.nested = 0; EXCEPTION WHEN NO_DATA_FOUND THEN -- Write an error message in the topmost step SELECT step_id,job_id INTO l_top_step_id, l_top_job_id FROM MGMT_JOB_EXECUTION WHERE execution_id=crec.execution_id AND parent_step_id = -1; write_step_output(l_top_step_id, 'FATAL ERROR: bug 5470955 : job_id('||l_top_job_id || ')/ execution_id('||crec.execution_id|| ') not found for step ' ||crec.step_id); -- Delete from job execution table. DELETE FROM MGMT_JOB_EXECUTION WHERE execution_id = crec.execution_id; -- Mark all steps in this execution as Aborted. -- so that they are not picked again. UPDATE MGMT_JOB_HISTORY SET step_status = ABORTED_STATUS WHERE execution_id = crec.execution_id; GOTO next_iteration; END; -- Try to grab the exclusive lock first then lock the execution BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Processing step ' || crec.step_id, MODULE_NAME); END IF; l_execution_status := lock_executions(crec.step_id, true); SELECT step_status INTO l_step_status FROM MGMT_JOB_EXECUTION WHERE step_id=crec.step_id; -- Ensure that no other OMS has claimed this step IF l_step_status != SCHEDULED_STATUS THEN GOTO next_iteration; END IF; EXCEPTION -- Skip this step if we cannot lock its execution WHEN OTHERS THEN GOTO next_iteration; END; IF l_execution_status=SCHEDULED_STATUS THEN IF l_is_ca=0 THEN BEGIN l_current_schedule := get_current_schedule(l_top_job_id); l_start_grace_period := l_current_schedule.start_grace_period; EXCEPTION WHEN NO_DATA_FOUND THEN -- This could be null for jobs in a queue l_current_schedule := null; l_start_grace_period := NO_START_GRACE; END; -- this can only happen when the OMS is down. if the agent -- is down we wouldnt have come here because the execution -- would have been in suspended/agent down status. -- skip the execution, delete all scheduled steps and add a -- new execution in waiting status if the execution was not -- started before the grace period -- Set the end time of the step to the end of the grace period IF (l_current_schedule IS NOT NULL) AND (l_start_grace_period != NO_START_GRACE) AND (l_current_time > (l_expected_start_time + mins_to_interval(l_start_grace_period))) THEN skip_exec_and_schedule_next(l_top_job_id, l_target_list_index, l_expected_start_time + mins_to_interval(l_start_grace_period), crec.step_id, SKIPPED_SYSTEM, true); GOTO next_iteration; END IF; END IF; END IF; l_iterate_param_index := crec.iterate_param_index; l_job_type_id := get_job_type_id(crec.job_id, crec.execution_id); SELECT j.job_type, nested, agent_bound, system_job INTO l_job_type, l_nested_job, l_agent_bound, l_system_job FROM MGMT_JOB j, MGMT_JOB_TYPE_INFO ji WHERE j.job_id=crec.job_id AND ji.job_type_id=l_job_type_id; -- For jobs associated with a session ("interactive" jobs) -- agent bound is always false IF l_system_job=SYSTEM_JOB_SESSION THEN l_agent_bound := 0; END IF; IF crec.step_type = STEPTYPE_STEP THEN SELECT command_name, all_params INTO l_command_name, l_all_params FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND step_name = crec.step_name; BEGIN l_step_params := get_job_step_params(crec.job_id, crec.execution_id, crec.step_name, crec.step_id, l_job_type_id, l_all_params, crec.iterate_param, l_iterate_param_index, false, l_dummy); -- HACK for now, fetch all parameters for large params -- IF the step is one of the built-in agent-bound commands -- AND requires a large parameter IF need_all_params(l_step_params, l_command_name) THEN l_job_params := get_all_job_params(crec.job_id, crec.execution_id, crec.step_id); END IF; -- Get the "trusted" information for the step l_trusted := get_step_trust(crec.step_id); EXCEPTION WHEN OTHERS THEN -- Abort the step write_step_error_message(crec.step_id, 'Step aborted because parameter values were too large:' || SQLERRM); update_step_status_nolock(crec.step_id, ABORTED_STATUS, -1); GOTO next_iteration; END; SELECT command_class INTO l_command_class FROM MGMT_JOB_COMMAND WHERE command_name=l_command_name; END IF; l_first_step := 0; IF l_execution_status=SCHEDULED_STATUS OR l_nested_job=1 THEN -- This maybe the first step l_first_step := 1; -- Check if this is the first step in the -- (nested) job SELECT COUNT(*) INTO l_c FROM MGMT_JOB_EXECUTION WHERE job_id=crec.job_id AND execution_id = crec.execution_id AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC, STEPTYPE_FLATTEN_TARGETS_STEP) AND step_status != SCHEDULED_STATUS; IF l_c > 0 THEN l_first_step := 0; ELSE -- Set "first step" to true if there are any -- security/lock info sections associated with the job SELECT COUNT(1) INTO l_c FROM MGMT_JOB_SEC_INFO WHERE job_type_id=l_job_type_id AND apply_at_submission=0; IF l_c > 0 THEN l_first_step := 1; ELSE SELECT COUNT(1) INTO l_c FROM MGMT_JOB_LOCK_INFO WHERE job_type_id=l_job_type_id; IF l_c > 0 THEN l_first_step := 1; END IF; END IF; END IF; END IF; l_step_record := MGMT_JOB_STEP_RECORD(crec.job_id, crec.execution_id, crec.step_name, crec.step_type, l_first_step, l_command_name, crec.command_type, l_command_class, crec.step_id, crec.iterate_param, l_iterate_param_index, l_step_params, l_agent_bound, l_job_params, l_trusted); p_steps.extend(1); p_steps(n+l_count) := l_step_record; -- Set the status to executing. Note that we need to do this -- recursively for all parent steps. SELECT step_id BULK COLLECT INTO l_parent_step_ids FROM MGMT_JOB_EXECUTION WHERE step_status = SCHEDULED_STATUS START WITH step_id=crec.step_id CONNECT BY step_id=prior parent_step_id; -- Do a bulk update for performance FORALL i IN 1..l_parent_step_ids.COUNT UPDATE MGMT_JOB_EXECUTION set step_status = EXECUTING_STATUS, dispatcher_id=p_dispatcher_id, oms_name=l_oms_name WHERE STEP_ID = l_parent_step_ids(i); -- Update the start time for this step only UPDATE MGMT_JOB_EXECUTION SET start_time=l_current_time, error_id = NULL WHERE step_id=crec.step_id; -- Update the status of the job to executing, if required IF l_execution_status = SCHEDULED_STATUS THEN -- Update the status of the execution to executing UPDATE MGMT_JOB_EXEC_SUMMARY SET status=EXECUTING_STATUS, start_time=l_current_time WHERE execution_id=crec.execution_id; END IF; n := n+1; <> NULL; EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS OR DUP_VAL_ON_INDEX OR SUBSCRIPT_BEYOND_COUNT OR COLLECTION_IS_NULL OR VALUE_ERROR THEN -- If we get an unhandled exception here, we are very likely -- to get it when we try to process the step force_abort_execution(crec.execution_id, 'Execution aborted because of unhandled error: ' || SQLERRM); END; END LOOP; END; -- Return a set of steps subject to the specified maximum -- p_command_type=0 for short steps, 1 for long steps, 2 for system steps, -- 3 for wait steps PROCEDURE get_scheduled_steps(p_max_steps INTEGER, p_command_type NUMBER, p_steps OUT MGMT_JOB_STEP_LIST, p_dispatcher_id INTEGER) IS BEGIN IF p_command_type = WAIT_COMMAND THEN get_wait_steps(p_max_steps, p_steps, p_dispatcher_id); ELSE get_real_scheduled_steps(p_max_steps, p_command_type, p_steps, p_dispatcher_id); END IF; END; -- Set the steps specified in the array to SCHEDULED PROCEDURE update_unscheduled_steps(steps_to_reschedule SMP_EMD_INTEGER_ARRAY) IS BEGIN UPDATE MGMT_JOB_EXECUTION SET step_status=SCHEDULED_STATUS, dispatcher_id=-1 WHERE step_id IN (SELECT * FROM TABLE(CAST(steps_to_reschedule AS SMP_EMD_INTEGER_ARRAY))); END; -- Update the status of all steps specified. Like update_step_status, -- but acts on arrays PROCEDURE update_bulk_step_status(p_step_ids SMP_EMD_INTEGER_ARRAY, p_statuses SMP_EMD_INTEGER_ARRAY, p_error_codes SMP_EMD_INTEGER_ARRAY, p_async SMP_EMD_INTEGER_ARRAY, p_emd_urls SMP_EMD_STRING_ARRAY) IS BEGIN FOR i IN 1..p_step_ids.COUNT LOOP IF p_async(i)=0 THEN update_step_status(p_step_ids(i), p_statuses(i), p_error_codes(i)); ELSE reset_dispatcher_id(p_step_ids(i), p_emd_urls(i), 0); END IF; END LOOP; END; FUNCTION get_skipped_reason(p_status NUMBER) RETURN NUMBER IS BEGIN IF is_system_suspended_status(p_status) THEN return SKIPPED_SYSTEM_SUSPEND; ELSIF p_status = SUSPENDED_STATUS THEN return SKIPPED_USER_SUSPEND; ELSIF p_status = EXECUTING_STATUS THEN return SKIPPED_RUNNING; ELSIF p_status = REASSIGNED_STATUS THEN return SKIPPED_REASSIGNED; ELSE -- TBD. validate this assumption return SKIPPED_SYSTEM; END IF; END; -- Special processing for start and grace wait step types. This is called -- from a worker thread when processing WAIT_COMMAND command types. PROCEDURE process_wait_step(p_step_id NUMBER) IS l_current_time DATE := SYSDATE_UTC(); l_current_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_current_job_id MGMT_JOB.job_id%TYPE; l_current_exec_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_current_schedule MGMT_JOB_SCHEDULE_RECORD; l_current_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; l_current_tzregion MGMT_JOB_EXEC_SUMMARY.timezone_region%TYPE; l_job_lock_status NUMBER; l_step_start_time MGMT_JOB_EXECUTION.start_time%TYPE; l_step_type MGMT_JOB_EXECUTION.step_type%TYPE; l_step_end_time MGMT_JOB_EXECUTION.end_time%TYPE; l_step_id MGMT_JOB_EXECUTION.step_id%TYPE; l_expected_start_time MGMT_JOB_EXEC_SUMMARY.expected_start_time%TYPE; l_next_exec_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_target_guid MGMT_TARGETS.target_guid%TYPE; l_skipped_reason MGMT_JOB_EXEC_SUMMARY.status_detail%TYPE; l_ignore NUMBER; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; BEGIN BEGIN -- HACK Bookkeeping steps are currently processed in the same thread -- pool as wait steps, but there is little in common. Hence call the apt -- proc for the bookkeeping step IF p_step_id = BOOKKEEPING_STEP_ID THEN process_bookkeeping_step; RETURN; END IF; SELECT mjes.job_id, mjes.execution_id, mjes.target_list_index, mjes.expected_start_time, mje.start_time, mje.step_type, mjes.timezone_region INTO l_current_job_id, l_next_exec_id, l_target_list_index, l_expected_start_time, l_step_start_time, l_step_type, l_current_tzregion FROM MGMT_JOB_EXEC_SUMMARY mjes, MGMT_JOB_EXECUTION mje WHERE step_id = p_step_id AND mjes.execution_id = mje.execution_id; -- There should always be a current execution in one of -- SCHEDULED, EXECUTING, SUSPENDED or AGENTDOWN states. SELECT execution_id, status INTO l_current_exec_id, l_current_exec_status FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id = l_current_job_id AND target_list_index = l_target_list_index AND status IN (SCHEDULED_STATUS, EXECUTING_STATUS, SUSPENDED_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_BLACKOUT_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS); l_current_schedule := get_current_schedule(l_current_job_id); l_current_grace_period := l_current_schedule.start_grace_period; l_ignore := lock_executions(l_next_exec_id); -- make sure that the wait step still exists and it has not be overwritten bug 5139414 BEGIN SELECT step_id INTO l_ignore FROM MGMT_JOB_HISTORY WHERE step_id=p_step_id; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; END; EXCEPTION WHEN NO_DATA_FOUND THEN -- There is a severe problem when the parent execution -- of this step cannot be found, so we delete the step. EMDW_LOG.error('Encountered NO_DATA_FOUND in process_wait_step for step_id ' || p_step_id, MODULE_NAME); DELETE FROM MGMT_JOB_EXECUTION WHERE step_id=p_step_id RETURNING execution_id INTO l_execution_id; DELETE FROM MGMT_JOB_HISTORY WHERE step_id=p_step_id; DELETE FROM MGMT_JOB_PARAMETER WHERE execution_id=l_execution_id; DELETE FROM MGMT_JOB_TARGET WHERE execution_id=l_execution_id; DELETE FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=l_execution_id; RETURN; END; -- assert(l_step_type = STEPTYPE_START_WAIT_STEP OR l_step_type = STEPTYPE_GRACE_WAIT_STEP); IF l_step_type = STEPTYPE_START_WAIT_STEP THEN IF l_current_grace_period != NO_START_GRACE THEN convert_exec_to_waiting(l_current_job_id, l_next_exec_id, l_step_start_time, l_current_tzregion, STEPTYPE_GRACE_WAIT_STEP, l_current_grace_period); ELSE -- infinite grace period l_skipped_reason := get_skipped_reason(l_current_exec_status); skip_exec_and_schedule_next(l_current_job_id, l_target_list_index, l_step_start_time, p_step_id, l_skipped_reason); END IF; ELSIF l_step_type = STEPTYPE_GRACE_WAIT_STEP THEN l_skipped_reason := SKIPPED_EXPIRY; skip_exec_and_schedule_next(l_current_job_id, l_target_list_index, l_step_start_time, p_step_id, l_skipped_reason); END IF; END process_wait_step; PROCEDURE reset_dispatcher_id(p_step_id NUMBER, p_emd_url VARCHAR2, p_update_time NUMBER DEFAULT 1) IS l_current_time DATE := SYSDATE_UTC(); BEGIN -- start time needs to be updated only if p_update_time is 1, otherwise -- must remain unchanged UPDATE MGMT_JOB_EXECUTION SET dispatcher_id = -1, emd_url = p_emd_url, start_time = DECODE(p_update_time, 1, l_current_time, start_time) WHERE step_id = p_step_id; END; -- Return the output id of the step, generate a new one if necessary FUNCTION get_output_id(p_step_id INTEGER, p_for_update NUMBER) return RAW IS l_output_id RAW(16); BEGIN -- If the step already has an output_id, return that SELECT output_id INTO l_output_id FROM MGMT_JOB_HISTORY WHERE step_id=p_step_id; IF l_output_id IS NULL AND p_for_update=1 THEN -- The assumption is that the execution still exists UPDATE MGMT_JOB_EXECUTION SET output_id=SYS_GUID() WHERE step_id=p_step_id RETURNING output_id INTO l_output_id; IF l_output_id IS NOT NULL THEN INSERT INTO MGMT_JOB_OUTPUT(output_id, output) VALUES(l_output_id, empty_clob()); END IF; RETURN l_output_id; ELSE RETURN l_output_id; END IF; END; -- Return the error id of the step, generate a new one if necessary FUNCTION get_error_id(p_step_id INTEGER, p_for_update NUMBER) return RAW IS l_error_id RAW(16); BEGIN -- If the step already has an error_id, return that SELECT error_id INTO l_error_id FROM MGMT_JOB_HISTORY WHERE step_id=p_step_id; IF l_error_id IS NULL AND p_for_update=1 THEN UPDATE MGMT_JOB_EXECUTION SET error_id=SYS_GUID() WHERE step_id=p_step_id RETURNING error_id INTO l_error_id; IF l_error_id IS NOT NULL THEN INSERT INTO MGMT_JOB_OUTPUT(output_id, output) VALUES(l_error_id, empty_clob()); END IF; RETURN l_error_id; ELSE RETURN l_error_id; END IF; END; -- Write the specified varchar value as the output of the step. -- Note that this overwrites any output previously written PROCEDURE write_step_output(p_step_id INTEGER, p_output VARCHAR2, p_async_error_received NUMBER DEFAULT 0) IS l_output_id RAW(16); BEGIN UPDATE MGMT_JOB_EXECUTION SET output_id=SYS_GUID(), async_error_received=p_async_error_received WHERE step_id=p_step_id RETURNING output_id INTO l_output_id; IF l_output_id IS NOT NULL THEN INSERT INTO MGMT_JOB_OUTPUT(output_id, output) VALUES (l_output_id, p_output); END IF; END; -- Autonomous version of write_step_output() PROCEDURE write_step_output_auto(p_step_id INTEGER, p_output VARCHAR2) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN write_step_output(p_step_id, p_output); COMMIT; END; PROCEDURE write_step_error_message(p_step_id NUMBER, p_error_message VARCHAR2) IS l_error_id RAW(16); BEGIN UPDATE MGMT_JOB_EXECUTION SET error_id=SYS_GUID() WHERE step_id=p_step_id RETURNING error_id INTO l_error_id; IF l_error_id IS NOT NULL THEN INSERT INTO MGMT_JOB_OUTPUT(output_id, output) VALUES (l_error_id, p_error_message); END IF; END; -- Return a handle to the output of this step. What's returned is -- a CLOB locator that the calling code can use to read the -- output of the step. If for_update is true, lock the output row FUNCTION get_output(p_step_id INTEGER, p_for_update INTEGER, p_sequence_number INTEGER, p_lock_nowait INTEGER DEFAULT 0) return CLOB IS l_output_id RAW(16); l_output CLOB; BEGIN IF p_sequence_number >= 0 THEN IF NOT update_sequence_number(p_step_id, p_sequence_number) THEN RETURN null; END IF; END IF; l_output_id := get_output_id(p_step_id, p_for_update); IF p_for_update = 1 THEN IF p_lock_nowait = 1 THEN SELECT output INTO l_output FROM MGMT_JOB_OUTPUT WHERE output_id=l_output_id FOR UPDATE NOWAIT; ELSE SELECT output INTO l_output FROM MGMT_JOB_OUTPUT WHERE output_id=l_output_id FOR UPDATE; END IF; ELSE SELECT output INTO l_output FROM MGMT_JOB_OUTPUT WHERE output_id=l_output_id; END IF; RETURN l_output; EXCEPTION -- Could happen if the job was stopped and immediately -- deleted WHEN NO_DATA_FOUND THEN RETURN null; END; -- Return a handle to the error of this step. What's returned is -- a CLOB locator that the calling code can use to read the -- output of the step. If for_update is true, lock the output row -- p_async_error_received, if true (1), indicates that the call is in -- response to an async error message sent by the agent. FUNCTION get_error_message(p_step_id INTEGER, p_for_update INTEGER, p_sequence_number INTEGER, p_lock_nowait INTEGER DEFAULT 0, p_async_error_received INTEGER DEFAULT 0) return CLOB IS l_error_id RAW(16); l_error_msg CLOB; BEGIN IF p_sequence_number >= 0 THEN IF NOT update_sequence_number(p_step_id, p_sequence_number) THEN RETURN null; END IF; END IF; IF p_async_error_received=1 THEN UPDATE MGMT_JOB_EXECUTION SET async_error_received=p_async_error_received WHERE step_id=p_step_id; END IF; l_error_id := get_error_id(p_step_id, p_for_update); IF p_for_update = 1 THEN IF p_lock_nowait=1 THEN SELECT output INTO l_error_msg FROM MGMT_JOB_OUTPUT WHERE output_id=l_error_id FOR UPDATE NOWAIT; ELSE SELECT output INTO l_error_msg FROM MGMT_JOB_OUTPUT WHERE output_id=l_error_id FOR UPDATE; END IF; ELSE SELECT output INTO l_error_msg FROM MGMT_JOB_OUTPUT WHERE output_id=l_error_id; END IF; RETURN l_error_msg; EXCEPTION -- Could happen if the job was stopped and immediately -- deleted WHEN NO_DATA_FOUND THEN RETURN null; END; -- Remove a reference to a target from the extended list -- If more than one reference is made to the target, the -- reference count is reduced PROCEDURE delete_ext_target(p_job_id RAW, p_execution_id RAW, p_target_list_index NUMBER, p_target_guid RAW) IS l_reference_count NUMBER; BEGIN BEGIN SELECT reference_count INTO l_reference_count FROM MGMT_JOB_EXT_TARGETS WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=p_target_list_index AND target_guid=p_target_guid; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; END; -- If the reference count is down to one, we're -- the only reference, so we can remove the target IF l_reference_count=1 THEN DELETE FROM MGMT_JOB_EXT_TARGETS WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=p_target_list_index AND target_guid=p_target_guid; ELSE UPDATE MGMT_JOB_EXT_TARGETS SET reference_count=reference_count-1 WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=p_target_list_index AND target_guid=p_target_guid; END IF; END; -- Insert a target into the "extended" list, upping the -- ref count as necessary. PROCEDURE insert_ext_target(p_job_id RAW, p_execution_id RAW, p_target_list_index NUMBER, p_target_guid RAW) IS BEGIN INSERT INTO MGMT_JOB_EXT_TARGETS(job_id, execution_id, target_list_index, target_guid, reference_count) VALUES (p_job_id, p_execution_id, p_target_list_index, p_target_guid, 1); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN -- The target was already inserted. Up the -- reference count UPDATE MGMT_JOB_EXT_TARGETS SET reference_count=reference_count+1 WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=p_target_list_index AND target_guid=p_target_guid; END; --Insert a target into the "extended" list, upping the -- ref count as necessary. -- This should only be used as a support routine for -- compute_extended_target_list, below PROCEDURE insert_ext_target(p_job_id RAW, p_execution_id RAW, p_target_list_index NUMBER, p_target_name VARCHAR2, p_target_type VARCHAR2, p_raise_invalid_target BOOLEAN, p_target_name_param VARCHAR2, p_target_type_param VARCHAR2) IS l_target_guid MGMT_TARGETS.target_guid%TYPE; BEGIN SELECT target_guid INTO l_target_guid FROM MGMT_TARGETS WHERE target_name=p_target_name AND target_type=p_target_type; insert_ext_target(p_job_id, p_execution_id, p_target_list_index, l_target_guid); EXCEPTION WHEN NO_DATA_FOUND THEN -- The parameter is specifying a target that does not -- exist. Since this is migration, we will let it go IF p_raise_invalid_target THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'The following set of target parameters reference an invalid target: (' || p_target_name_param || ',' || p_target_type_param || ')'); END IF; END; -- Compute the extended target list for the specified user source. PROCEDURE process_user_src_ext_tgts(p_job_id RAW, p_execution_id RAW, p_source_id RAW, p_raise_errors BOOLEAN DEFAULT TRUE) IS l_target_name_params MGMT_JOB_VECTOR_PARAMS; l_target_type_params MGMT_JOB_VECTOR_PARAMS; l_param_type_tname NUMBER := -1; l_scalar_value_tname VARCHAR2(4000); l_vector_value_tname MGMT_JOB_VECTOR_PARAMS; l_param_type_ttype NUMBER := -1; l_scalar_value_ttype VARCHAR2(4000); l_vector_value_ttype MGMT_JOB_VECTOR_PARAMS; l_target_list_index NUMBER; l_name_param_found BOOLEAN := false; l_type_param_found BOOLEAN := false; BEGIN SELECT target_list_index INTO l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; BEGIN SELECT target_name_params, target_type_params INTO l_target_name_params, l_target_type_params FROM MGMT_JOB_USER_PARAMS WHERE source_id=p_source_id; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; END; IF l_target_name_params IS NULL OR l_target_name_params.COUNT=0 THEN RETURN; END IF; FOR i in 1..l_target_name_params.count LOOP l_name_param_found := false; l_type_param_found := false; -- Yes, we could do an UPDATE...RETURNING INTO here, -- but that does not seem to work for some reason! BEGIN SELECT parameter_type, scalar_value, vector_value INTO l_param_type_tname, l_scalar_value_tname, l_vector_value_tname FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=l_target_name_params(i) AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); l_name_param_found := true; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; BEGIN SELECT parameter_type, scalar_value, vector_value INTO l_param_type_ttype, l_scalar_value_ttype, l_vector_value_ttype FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=l_target_type_params(i) AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); l_type_param_found := true; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; IF l_type_param_found != l_name_param_found THEN -- One of the parameters was not specified raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Parameters "' || l_target_name_params(i) || '" and "' || l_target_type_params(i) || '" are designated as ' || 'target parameters. Both must be specified'); ELSIF l_name_param_found=FALSE THEN -- If both parameters were not provided, -- nothing to do GOTO next_iteration; END IF; IF l_param_type_tname != l_param_type_ttype THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Parameters "' || l_target_name_params(i) || '" and "' || l_target_type_params(i) || '" are designated as ' || 'target parameters. They must be both scalar or both vector'); END IF; -- If the parameters are vector, they must have the same -- number of values IF l_param_type_tname=PARAM_TYPE_VECTOR THEN IF l_vector_value_tname.count != l_vector_value_ttype.count THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Parameters "' || l_target_name_params(i) || '" and "' || l_target_type_params(i) || '" are designated as ' || 'target parameters. Their vector values must have the ' || 'same number of elements'); END IF; END IF; -- Insert into the ext targets table, taking care -- to up the refcount IF l_param_type_tname=PARAM_TYPE_SCALAR THEN insert_ext_target(p_job_id, p_execution_id, l_target_list_index, l_scalar_value_tname, l_scalar_value_ttype, p_raise_errors, l_target_name_params(i), l_target_type_params(i)); ELSE -- Insert the vector FOR j IN 1..l_vector_value_tname.COUNT LOOP insert_ext_target(p_job_id, p_execution_id, l_target_list_index, l_vector_value_tname(j), l_vector_value_ttype(j), p_raise_errors, l_target_name_params(i), l_target_type_params(i)); END LOOP; END IF; <> NULL; END LOOP; END; -- Apply the "user" parameter source to the specified set -- of parameters. If p_encrypt is 1, encrypt the parameters PROCEDURE apply_user_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_required NUMBER, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_required_param_names VARCHAR2(4000); l_count INTEGER := 0; l_target_name_params MGMT_JOB_VECTOR_PARAMS; l_target_type_params MGMT_JOB_VECTOR_PARAMS; l_isscalar_tname NUMBER := -1; l_scalar_value_tname VARCHAR2(4000); l_vector_value_tname MGMT_JOB_VECTOR_PARAMS; l_isscalar_ttype NUMBER := -1; l_scalar_value_ttype VARCHAR2(4000); l_vector_value_ttype MGMT_JOB_VECTOR_PARAMS; l_tname_param_count NUMBER; l_ttype_param_count NUMBER; l_params MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); l_param MGMT_JOB_PARAM_RECORD; ie INTEGER; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Processing user source', MODULE_NAME); END IF; IF p_required = 1 THEN -- Figure out how many parameters are missing SELECT COUNT(parameter_name) INTO l_count FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name IN (SELECT * from TABLE(CAST(p_parameter_names AS MGMT_JOB_VECTOR_PARAMS))); IF l_count != p_parameter_names.COUNT THEN raise_application_error(MGMT_GLOBAL.JOB_PARAM_MISSING_ERR, to_char(p_parameter_names.count-l_count) || ' required job parameter(s) missing'); END IF; END IF; -- Encrypt the parameters if needed IF p_encrypt THEN ie := 1; -- Fetch all the unencrypted parameters first, encrypt and -- write them back FOR crec IN (SELECT execution_id, parameter_name, parameter_type, scalar_value, vector_value FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND encrypted=0 AND parameter_name IN (SELECT * from TABLE(CAST(p_parameter_names AS MGMT_JOB_VECTOR_PARAMS))) AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR)) LOOP l_param := MGMT_JOB_PARAM_RECORD(crec.parameter_name, crec.parameter_type, crec.scalar_value, crec.vector_value); l_params.extend(1); l_params(ie) := l_param; ie := ie +1; END LOOP; -- Write the parameters back IF l_params.COUNT > 0 THEN update_job_parameters(p_job_id, p_execution_id, l_params, true, false); END IF; END IF; -- Add to the "extended" target list of the job process_user_src_ext_tgts(p_job_id, p_execution_id, p_source_id); END; -- Read rows from the cursor, update the specified parameter list -- The cursor is generated by a sql source executing a dynamic sql -- statement PROCEDURE process_sql_cursor(p_cursor IN OUT MGMT_JOB_REFCURSOR, p_params IN OUT MGMT_JOB_PARAM_LIST) IS l_name VARCHAR2(64); l_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_param_index INTEGER; l_len NUMBER; BEGIN LOOP FETCH p_cursor INTO l_name, l_value; EXIT WHEN p_cursor%NOTFOUND; -- Search for the specified parameter and update it l_param_index := -1; FOR j in 1..p_params.count LOOP IF p_params(j).param_name = l_name THEN l_param_index := j; EXIT; END IF; END LOOP; -- If the parameter was not found, we ignore it. This may -- not be the best thing to do... IF l_param_index != -1 THEN IF p_params(l_param_index).param_type=PARAM_TYPE_SCALAR THEN p_params(l_param_index).scalar_value := l_value; ELSIF p_params(l_param_index).param_type=PARAM_TYPE_VECTOR THEN l_len := p_params(l_param_index).vector_value.count; p_params(l_param_index).vector_value.extend(1); p_params(l_param_index).vector_value(l_len+1) := l_value; END IF; END IF; END LOOP; END; -- Initialize a parameter list for use by the sql source. -- p_parameter_names is a set of parameters to initialize. -- p_vector_params is the subset of parameters that are vector -- parameters. FUNCTION init_sql_params(p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_vector_params MGMT_JOB_VECTOR_PARAMS) RETURN MGMT_JOB_PARAM_LIST IS l_param_list MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); l_is_vector BOOLEAN; BEGIN l_param_list.extend(p_parameter_names.COUNT); FOR i in 1..p_parameter_names.count LOOP -- Figure out whether this parameter is a vector param -- Yes, we're doing a linear search, but the number of parameters -- we're retreiving should be small enough to make it OK... l_is_vector := false; FOR j in 1..p_vector_params.count LOOP IF p_parameter_names(i) = p_vector_params(j) THEN l_is_vector := true; exit; END IF; END LOOP; IF l_is_vector THEN -- Set the vector value to an empty array l_param_list(i) := MGMT_JOB_PARAM_RECORD(p_parameter_names(i), 0, null, MGMT_JOB_VECTOR_PARAMS()); ELSE -- Set the scalar value to null, it will be filled in later l_param_list(i) := MGMT_JOB_PARAM_RECORD(p_parameter_names(i), 1, null, null); END IF; END LOOP; RETURN l_param_list; END; -- Apply the "sql" parameter source to the specified set of parameters -- This procedure does not do any consistency checking; it assumes that -- all consistency checking is done by the XML parser PROCEDURE apply_sql_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_sql_statement VARCHAR2(4000); l_vector_params MGMT_JOB_VECTOR_PARAMS; l_is_plsql NUMBER; l_out_param_type VARCHAR2(20); -- the output parameter list l_out_params MGMT_JOB_PARAM_LIST; -- the output cursor l_crec MGMT_JOB_REFCURSOR; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Processing sql source', MODULE_NAME); END IF; -- Prior to processing the SQL statement, substitute for -- any parameter references that may be present -- In dbcontrol, sql paramsrcs are used mainly for determining dbrole -- connect suffix, so there is little risk of SQL injection. GC would have -- a different fix l_sql_statement := cleanse( substitute_params(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, p_source_data)); reset_params(); -- Fetch the params we need SELECT vector_params, out_proc, out_param_type INTO l_vector_params, l_is_plsql, l_out_param_type FROM MGMT_JOB_SQL_PARAMS WHERE source_id=p_source_id; -- Is this a SQL query or a pl/sql procedure? IF l_is_plsql = 1 THEN -- This is a procedure that has only two bind parameters for -- the input parameter names and the output values, respectively IF l_out_param_type = 'paramList' THEN EXECUTE IMMEDIATE l_sql_statement USING p_parameter_names, OUT l_out_params; ELSE -- The out parameter of the procedure is a cursor l_out_params := init_sql_params(p_parameter_names, l_vector_params); EXECUTE IMMEDIATE l_sql_statement USING p_parameter_names, OUT l_crec; process_sql_cursor(l_crec, l_out_params); CLOSE l_crec; END IF; ELSE -- Initialize the parameters l_out_params := init_sql_params(p_parameter_names, l_vector_params); -- This is a sql query that will fetch rows. OPEN l_crec FOR l_sql_statement; process_sql_cursor(l_crec, l_out_params); CLOSE l_crec; END IF; -- Update the parameter list of the job update_job_parameters(p_job_id, p_execution_id, l_out_params, p_encrypt, NOT p_override); EXCEPTION -- We assume here that we are clean; any errors must have been caused by -- the dynamic SQL we executed :-) WHEN OTHERS THEN -- This is to clear the cache before leaving the procedure reset_params(); IF l_crec%ISOPEN THEN CLOSE l_crec; END IF; raise_application_error(MGMT_GLOBAL.SQL_EXECUTION_ERR, 'SQL source: error while executing statement ' || l_sql_statement || ':' || SQLERRM); END; -- This version works only for multi-task jobs, and is used only -- when examining credential information at submit/edit time -- p_job_id is the job id of the parent job -- p_execution_id is the execution id, NO_EXECUTION for CAs FUNCTION get_vector_parameter_mtask(p_job_id RAW, p_execution_id RAW, p_parameter_name VARCHAR2, p_task_name VARCHAR2) RETURN MGMT_JOB_VECTOR_PARAMS IS l_vector_param_value MGMT_JOB_VECTOR_PARAMS; l_scalar_param_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_parameter_type NUMBER; l_is_encrypted NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_all_targets NUMBER; l_is_ca NUMBER; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('get_vector_parameter_mtask(): job id ' || p_job_id || ' execution id ' || p_execution_id || ' task name ' || p_task_name || ' parameter name ' || p_parameter_name, MODULE_NAME); END IF; SELECT is_corrective_action INTO l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id; IF p_execution_id != NO_EXECUTION THEN SELECT target_list_index INTO l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; END IF; IF l_is_ca=1 THEN l_job_type_id := get_job_type_id(p_job_id); ELSE l_job_type_id := get_job_type_id(p_job_id, p_execution_id); END IF; -- Check whether the task has all targets passed from the parent -- job, or specified targets SELECT all_targets INTO l_all_targets FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND step_name=p_task_name AND step_type=STEPTYPE_JOB; -- Special-case the job target names and target types IF p_parameter_name = 'job_target_names' THEN IF l_all_targets=1 THEN IF l_is_ca=1 AND p_execution_id=NO_EXECUTION THEN -- For CAs, get the target name from the CA l_vector_param_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_param_value.extend(1); SELECT target_name INTO l_vector_param_value(1) FROM MGMT_CORRECTIVE_ACTION ca, MGMT_TARGETS t WHERE job_id=p_job_id AND ca.ca_target_guid=t.target_guid; ELSE SELECT target_name BULK COLLECT INTO l_vector_param_value FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index AND jt.target_guid=t.target_guid ORDER BY jt.target_index; END IF; ELSE -- Specified targets. Read from MGMT_NESTED_JOB_TARGETS. -- Note that there is no need to substitute values for -- multitask jobs ONLY, since we know these represent -- the actual targets SELECT target_name BULK COLLECT INTO l_vector_param_value FROM MGMT_NESTED_JOB_TARGETS WHERE job_type_id=l_job_type_id AND step_name=p_task_name AND step_type=STEPTYPE_JOB; END IF; ELSIF p_parameter_name = 'job_target_types' THEN IF l_all_targets=1 THEN IF l_is_ca=1 AND p_execution_id=NO_EXECUTION THEN -- For CAs, get the target type from the CA l_vector_param_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_param_value.extend(1); SELECT target_type INTO l_vector_param_value(1) FROM MGMT_CORRECTIVE_ACTION ca, MGMT_TARGETS t WHERE job_id=p_job_id AND ca.ca_target_guid=t.target_guid; ELSE SELECT target_type BULK COLLECT INTO l_vector_param_value FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index AND jt.target_guid=t.target_guid ORDER BY jt.target_index; END IF; ELSE -- Specified targets. Read from MGMT_NESTED_JOB_TARGETS. -- Note that there is no need to substitute values for -- multitask jobs ONLY, since we know these represent -- the actual targets SELECT target_type BULK COLLECT INTO l_vector_param_value FROM MGMT_NESTED_JOB_TARGETS WHERE job_type_id=l_job_type_id AND step_name=p_task_name AND step_type=STEPTYPE_JOB; END IF; ELSE -- Again note that for multitask jobs the actual values -- are provided, so there is no need to substitute SELECT parameter_type, scalar_value, vector_value, encrypted INTO l_parameter_type, l_scalar_param_value, l_vector_param_value, l_is_encrypted FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=l_job_type_id AND step_name=p_task_name AND param_name=p_parameter_name; IF l_parameter_type=PARAM_TYPE_SCALAR THEN l_vector_param_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_param_value.extend(1); l_vector_param_value(1) := decrypt_scalar(l_is_encrypted, l_scalar_param_value); ELSIF l_parameter_type=PARAM_TYPE_VECTOR THEN l_vector_param_value := decrypt_vector(l_is_encrypted, l_vector_param_value); END IF; END IF; RETURN l_vector_param_value; END; -- Return the value of the specified parameter as a vector. If the -- parameter is not found, a NO_DATA_FOUND exception is propogated FUNCTION get_vector_parameter(p_job_id RAW, p_execution_id RAW, p_parameter_name VARCHAR2, p_task_name VARCHAR2 DEFAULT NULL) RETURN MGMT_JOB_VECTOR_PARAMS IS l_vector_param_value MGMT_JOB_VECTOR_PARAMS; l_scalar_param_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_target_list_index INTEGER; l_parameter_type NUMBER; l_is_encrypted NUMBER; l_is_ca MGMT_JOB.is_corrective_action%TYPE; BEGIN IF p_task_name IS NOT NULL THEN RETURN get_vector_parameter_mtask(p_job_id, p_execution_id, p_parameter_name, p_task_name); END IF; SELECT is_corrective_action INTO l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id; IF p_execution_id != NO_EXECUTION THEN SELECT target_list_index INTO l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; END IF; -- Special-case the job target names and target types IF p_parameter_name = 'job_target_names' THEN IF l_is_ca=1 AND p_execution_id=NO_EXECUTION THEN -- For CAs, get the target name from the CA l_vector_param_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_param_value.extend(1); SELECT target_name INTO l_vector_param_value(1) FROM MGMT_CORRECTIVE_ACTION ca, MGMT_TARGETS t WHERE job_id=p_job_id AND ca.ca_target_guid=t.target_guid; ELSE SELECT target_name BULK COLLECT INTO l_vector_param_value FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index AND jt.target_guid=t.target_guid ORDER BY jt.target_index; END IF; ELSIF p_parameter_name = 'job_target_types' THEN IF l_is_ca=1 AND p_execution_id=NO_EXECUTION THEN -- For CAs, get the target type from the CA l_vector_param_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_param_value.extend(1); SELECT target_type INTO l_vector_param_value(1) FROM MGMT_CORRECTIVE_ACTION ca, MGMT_TARGETS t WHERE job_id=p_job_id AND ca.ca_target_guid=t.target_guid; ELSE SELECT target_type BULK COLLECT INTO l_vector_param_value FROM MGMT_JOB_TARGET jt, MGMT_TARGETS t WHERE job_id=p_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index AND jt.target_guid=t.target_guid ORDER BY jt.target_index; END IF; ELSE SELECT parameter_type, scalar_value, vector_value, encrypted INTO l_parameter_type, l_scalar_param_value, l_vector_param_value, l_is_encrypted FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=p_parameter_name AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); IF l_parameter_type=PARAM_TYPE_SCALAR THEN l_vector_param_value := MGMT_JOB_VECTOR_PARAMS(); l_vector_param_value.extend(1); l_vector_param_value(1) := decrypt_scalar(l_is_encrypted, l_scalar_param_value); ELSIF l_parameter_type=PARAM_TYPE_VECTOR THEN l_vector_param_value := decrypt_vector(l_is_encrypted, l_vector_param_value); END IF; END IF; RETURN l_vector_param_value; END; -- Given a target represented by p_cluster_name and p_cluster_type, -- return 1 via p_is_cluster_out if the target is a cluster. Also -- return the name, type, and emd_url of an appropriate instance of -- the cluster. Currently, it just returns the master instance, which -- is always assumed to be up. PROCEDURE resolve_cluster(p_cluster_name VARCHAR2, p_cluster_type VARCHAR2, p_is_cluster_out OUT NUMBER, p_instance_name_out OUT VARCHAR2, p_instance_type_out OUT VARCHAR2, p_emd_url_out OUT VARCHAR2, p_master_target_guid_out OUT RAW) IS l_master_name MGMT_TARGETS.target_name%TYPE; l_master_type MGMT_TARGETS.target_type%TYPE; l_master_emd_url MGMT_TARGETS.emd_url%TYPE; l_master_target_guid MGMT_TARGETS.target_guid%TYPE; BEGIN BEGIN SELECT property_value INTO p_is_cluster_out FROM MGMT_TYPE_PROPERTIES WHERE target_type=p_cluster_type AND property_name=MGMT_GLOBAL.G_IS_CLUSTER_PROP; EXCEPTION WHEN NO_DATA_FOUND THEN p_is_cluster_out := 0; END; IF p_is_cluster_out=0 THEN RETURN; END IF; -- Choose the master instance SELECT t2.target_name, t2.target_type, t2.target_guid, t2.emd_url INTO l_master_name, l_master_type, l_master_target_guid, l_master_emd_url FROM MGMT_TARGETS t1, MGMT_TARGETS t2, MGMT_TARGET_ASSOCS m WHERE t1.emd_url=t2.emd_url AND t1.target_name=p_cluster_name AND t1.target_type=p_cluster_type AND t1.target_guid=m.source_target_guid AND t2.target_guid=m.assoc_target_guid AND m.assoc_guid = MGMT_ASSOC.g_contains_guid AND ROWNUM=1; p_instance_name_out := l_master_name; p_instance_type_out := l_master_type; p_emd_url_out := l_master_emd_url; p_master_target_guid_out := l_master_target_guid; EXCEPTION WHEN NO_DATA_FOUND THEN p_instance_name_out := null; p_instance_type_out := null; p_emd_url_out := null; p_master_target_guid_out := null; END; -- Return the emd url corresponding to the specified target. If the -- target is a cluster, an appropriate instance is picked. The target -- name and type of the resolved target is returned in the out -- parameters p_target_name_out and p_target_type_out FUNCTION get_emd_url(p_target_name VARCHAR2, p_target_type VARCHAR2, p_target_name_out OUT VARCHAR2, p_target_type_out OUT VARCHAR2) RETURN VARCHAR2 IS l_is_cluster NUMBER; l_emd_url MGMT_TARGETS.emd_url%TYPE; l_master_guid MGMT_TARGETS.target_guid%TYPE; BEGIN resolve_cluster(p_target_name, p_target_type, l_is_cluster, p_target_name_out, p_target_type_out, l_emd_url, l_master_guid); IF l_is_cluster=0 THEN SELECT emd_url INTO l_emd_url FROM MGMT_TARGETS WHERE target_name=p_target_name AND target_type=p_target_type; p_target_name_out := p_target_name; p_target_type_out := p_target_type; END IF; RETURN l_emd_url; END; -- Fetch target name and target type parameters for credential -- sources, duly substituted. Used by a couple of credential -- sources and the security stuff. p_error_code is the exception -- code to use when throwing an exception. If p_handle_clusters -- is true, then substitute all cluster targets with their master -- instances -- If p_task_name is not null, then we are fetching parameters -- on behalf of a task in a multi-task job. PROCEDURE handle_target_params(p_target_names_param VARCHAR2, p_target_types_param VARCHAR2, p_target_names IN OUT NOCOPY MGMT_JOB_VECTOR_PARAMS, p_target_types IN OUT NOCOPY MGMT_JOB_VECTOR_PARAMS, p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_handle_clusters BOOLEAN, p_error_code INTEGER) IS l_is_cluster NUMBER; l_master_name MGMT_TARGETS.target_name%TYPE; l_master_type MGMT_TARGETS.target_type%TYPE; l_master_emd_url MGMT_TARGETS.emd_url%TYPE; l_master_guid MGMT_TARGETS.target_guid%TYPE; BEGIN IF p_target_names_param IS NOT NULL THEN p_target_names := get_vector_parameter(p_job_id, p_execution_id, p_target_names_param, p_task_name); ELSIF p_target_names IS NOT NULL THEN -- Loop through the target names, substituting actual -- values for them FOR i IN 1..p_target_names.count LOOP p_target_names(i) := substitute_params(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, p_target_names(i), false, p_task_name); END LOOP; END IF; IF p_target_types_param IS NOT NULL THEN p_target_types := get_vector_parameter(p_job_id, p_execution_id, p_target_types_param, p_task_name); ELSIF p_target_types IS NOT NULL THEN -- Loop through the target types, substituting actual values FOR i IN 1..p_target_types.count LOOP p_target_types(i) := substitute_params(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, p_target_types(i), false, p_task_name); END LOOP; END IF; IF p_target_names IS NOT NULL THEN IF p_target_types IS NULL THEN raise_application_error(p_error_code, 'Both target name and type parameters need to be non-null'); END IF; IF p_target_names.count != p_target_types.count THEN raise_application_error(p_error_code, 'Target name/type parameters are not of the same length'); END IF; END IF; IF p_target_names IS NULL OR NOT p_handle_clusters THEN RETURN; END IF; -- Loop through the cluster targets in the list, and replace -- them with the appropriate cluster instances FOR i IN 1..p_target_names.COUNT LOOP resolve_cluster(p_target_names(i), p_target_types(i), l_is_cluster, l_master_name, l_master_type, l_master_emd_url, l_master_guid); IF l_is_cluster=1 THEN IF l_master_name IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'Could not resolve instance for cluster ' || p_target_names(i) || ':' || p_target_types(i)); END IF; p_target_names(i) := l_master_name; p_target_types(i) := l_master_type; END IF; END LOOP; END; -- Upsert information into the MGMT_JOB_EXEC_CRED_INFO table -- regarding whether or not credentials are set for a specific -- target or not PROCEDURE upsert_exec_cred_info(p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_target_guid RAW, p_container_location VARCHAR2, p_credential_set_name VARCHAR2, p_creds_set NUMBER) IS l_container_location MGMT_JOB_CREDENTIALS.container_location%TYPE := p_container_location; l_is_ca NUMBER; l_task_name MGMT_JOB_EXEC_CRED_INFO.task_name%TYPE := p_task_name; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE := p_execution_id; BEGIN -- We do not support older-style cred sets IF MGMT_CREDENTIAL.is_40_style_set(p_credential_set_name) THEN RETURN; END IF; IF l_task_name IS NULL THEN l_task_name := '<0>'; END IF; IF l_container_location IS NULL THEN l_container_location := MGMT_CREDENTIAL.DEFAULT_CONTAINER; END IF; SELECT is_corrective_action INTO l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id; IF l_is_ca=1 THEN l_execution_id := NO_EXECUTION; END IF; BEGIN INSERT INTO MGMT_JOB_EXEC_CRED_INFO(job_id, execution_id, task_name, target_guid, container_location, credential_set_name, credentials_set) VALUES (p_job_id, l_execution_id, l_task_name, p_target_guid, l_container_location, p_credential_set_name, p_creds_set); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=p_creds_set WHERE job_id=p_job_id AND execution_id=l_execution_id AND task_name=l_task_name AND target_guid=p_target_guid AND container_location=l_container_location AND credential_set_name=p_credential_set_name; END; END; -- encode the username to mask "$" with "\$" FUNCTION encode_pdp_data(p_username VARCHAR2, p_pdp_data VARCHAR2) RETURN VARCHAR2 IS BEGIN RETURN p_username || MGMT_CREDENTIAL.PDP_USERNAME_SEPERATOR || p_pdp_data; END; -- Apply the "credentials" parameter source to the specified set of -- parameters. This assumes that all validations are done by the -- XML parser, which inserts entries into the credential source tables -- If p_insert_params is true, the parameters are actually inserted; -- if not, parameters are not inserted but the credential information is -- If p_task_name is not null, then we are fetching parameters -- on behalf of a task in a multi-task job. FUNCTION apply_cred_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN, p_insert_params BOOLEAN DEFAULT TRUE, p_task_name VARCHAR2 DEFAULT NULL) RETURN BOOLEAN IS l_credential_set_name MGMT_JOB_CRED_PARAMS.credential_set_name%TYPE; l_credential_set_ttype MGMT_JOB_CRED_PARAMS.credential_set_target_type%TYPE; l_credential_columns MGMT_JOB_VECTOR_PARAMS; l_credential_columns_param MGMT_JOB_CRED_PARAMS.credential_columns_param%TYPE; l_target_names MGMT_JOB_VECTOR_PARAMS; l_target_types MGMT_JOB_VECTOR_PARAMS; l_target_names_param MGMT_JOB_CRED_PARAMS.target_names_param%TYPE; l_target_types_param MGMT_JOB_CRED_PARAMS.target_types_param%TYPE; l_parameter_values MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); l_container_paths MGMT_JOB_VECTOR_PARAMS; l_container_paths_param MGMT_JOB_CRED_PARAMS.container_paths_param%TYPE; l_container_path VARCHAR2(4000); l_target_guid MGMT_TARGETS.target_guid%TYPE; l_creds MGMT_CRED_RECORD; l_set_override NUMBER; l_job_creds MGMT_JOB_CRED_ARRAY; l_job_cred_index NUMBER := 1; l_is_cluster NUMBER; l_master_name MGMT_TARGETS.target_name%TYPE; l_master_type MGMT_TARGETS.target_type%TYPE; l_master_emd_url MGMT_TARGETS.emd_url%TYPE; l_master_guid MGMT_TARGETS.target_guid%TYPE; l_creds_set NUMBER := 1; l_current_creds_set NUMBER := 0; l_job_type_id MGMT_JOB_PARAM_SOURCE.job_type_id%TYPE; l_suspend_on_nocreds MGMT_JOB_TYPE_INFO.suspend_on_nocreds%TYPE; l_count NUMBER; l_encrypt_info MGMT_JOB_INT_ARRAY := MGMT_JOB_INT_ARRAY(); l_key_col_name MGMT_CREDENTIAL_SET_COLUMNS.set_column_name%TYPE := NULL; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Applying credential source', MODULE_NAME); END IF; -- If we're calling this to compute credential information -- AND the parameters are already specified, then do nothing. IF NOT p_insert_params THEN SELECT count(*) INTO l_count FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name IN (SELECT * FROM TABLE(CAST(p_parameter_names AS MGMT_JOB_VECTOR_PARAMS))); IF l_count=p_parameter_names.COUNT THEN RETURN true; END IF; END IF; -- Get our job type id. Note. This needs to be the job type id -- of the parent job IF p_execution_id=NO_EXECUTION THEN l_job_type_id := get_job_type_id(p_job_id); ELSE l_job_type_id := get_job_type_id(p_job_id, p_execution_id); END IF; -- Use the job type id of the source to figure out the -- suspend_on_nocreds parameter. SELECT suspend_on_nocreds INTO l_suspend_on_nocreds FROM MGMT_JOB_TYPE_INFO ji, MGMT_JOB_PARAM_SOURCE s WHERE ji.job_type_id=s.job_type_id AND s.source_id=p_source_id; SELECT credential_set_name, credential_set_target_type, credential_columns, credential_columns_param, target_names, target_types, container_paths, target_names_param, target_types_param, container_paths_param, set_override INTO l_credential_set_name, l_credential_set_ttype, l_credential_columns, l_credential_columns_param, l_target_names, l_target_types, l_container_paths, l_target_names_param, l_target_types_param, l_container_paths_param, l_set_override FROM MGMT_JOB_CRED_PARAMS WHERE source_id=p_source_id; -- The credential set name and type are substitutable params l_credential_set_name := substitute_params(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, l_credential_set_name, false, p_task_name); IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Credential set name:' || l_credential_set_name, MODULE_NAME); END IF; l_credential_set_ttype := substitute_params(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, l_credential_set_ttype, false, p_task_name); IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Credential set ttype:' || l_credential_set_ttype, MODULE_NAME); END IF; IF l_credential_columns IS NULL THEN l_credential_columns := get_vector_parameter(p_job_id, p_execution_id, l_credential_columns_param, p_task_name); IF l_credential_columns IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Credential column parameter not specified'); END IF; END IF; -- Fetch target name and type parameters BEGIN handle_target_params(l_target_names_param, l_target_types_param, l_target_names, l_target_types, p_job_id, p_execution_id, p_task_name, p_job_name, p_job_owner, false, MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR); EXCEPTION -- If the parameter was not found, that's OK, since the user can -- conditionally use the parameter in the job type WHEN NO_DATA_FOUND THEN reset_params; RETURN true; END; IF l_container_paths_param IS NOT NULL THEN l_container_paths := get_vector_parameter(p_job_id, p_execution_id, l_container_paths_param, p_task_name); ELSIF l_container_paths IS NOT NULL THEN -- Loop through the target names, substituting actual -- values for them FOR i IN 1..l_container_paths.count LOOP l_container_paths(i) := substitute_params(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, l_container_paths(i), false, p_task_name); END LOOP; END IF; reset_params(); IF l_target_names IS NULL OR l_target_types IS NULL THEN -- The XML parser will validate that the target names and -- types are not null; the only way this can happen is if -- a parameter name with a null value has been specified -- We will consider this to be a valid case, since the job -- type can control the usage of these paramters via a -- switch stepset IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('NULL target list, done: ', MODULE_NAME); END IF; RETURN true; ELSIF l_target_names.count != l_target_types.count THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'The target names and types params must have the same number of elements'); END IF; -- If the container paths param is specified, it must of the same -- length as the target names and types IF l_container_paths IS NOT NULL AND l_container_paths.count != l_target_names.count THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'The container paths parameter must have the same number of elements as the target names parameter'); END IF; l_encrypt_info.extend(p_parameter_names.count); -- The parameters set by the credential source are always vector parameters l_parameter_values.extend(p_parameter_names.count); FOR j IN 1..p_parameter_names.count LOOP l_parameter_values(j) := MGMT_JOB_PARAM_RECORD(p_parameter_names(j), 0, null, MGMT_JOB_VECTOR_PARAMS()); l_parameter_values(j).vector_value.extend(l_target_names.count); END LOOP; -- Loop over each target, get the credential and assign each column -- to the appropriate vector parameter, at slot i FOR i IN 1..l_target_names.COUNT LOOP SELECT target_guid INTO l_target_guid FROM MGMT_TARGETS WHERE target_name=l_target_names(i) AND target_type=l_target_types(i); IF l_container_paths IS NULL THEN l_container_path := null; ELSE l_container_path := l_container_paths(i); END IF; l_creds := null; resolve_cluster(l_target_names(i), l_target_types(i), l_is_cluster, l_master_name, l_master_type, l_master_emd_url, l_master_guid); IF l_is_cluster=1 THEN -- Note that for clusters, the assumption is that there is -- an identical credential set defined for the cluster -- target type -- for cluster type tgts of pass l_master_guid, l_master_type -- instead of null,null see bug#5840623 for details l_creds := MGMT_CREDENTIAL.get_job_creds_default(p_job_id, p_job_owner, l_credential_set_name, l_target_types(i), l_target_guid, l_container_path, l_master_guid, l_master_type, l_job_type_id, p_task_name); ELSIF p_task_name IS NOT NULL AND l_target_types(i) != l_credential_set_ttype THEN GOTO next_iteration; ELSE l_creds := MGMT_CREDENTIAL.get_job_creds_default(p_job_id, p_job_owner, l_credential_set_name, l_credential_set_ttype, l_target_guid, l_container_path, null, null, l_job_type_id, p_task_name); END IF; IF l_is_cluster=1 AND l_creds IS NULL THEN l_creds := MGMT_CREDENTIAL.get_job_creds_default(p_job_id, p_job_owner, l_credential_set_name, l_credential_set_ttype, l_master_guid, l_container_path, null, null, l_job_type_id, p_task_name); END IF; IF NOT p_encrypt AND l_key_col_name IS NULL THEN BEGIN l_key_col_name := MGMT_CREDENTIAL.get_credential_set_key_column(l_credential_set_name, l_credential_set_ttype); EXCEPTION WHEN NO_DATA_FOUND THEN MGMT_LOG.log_error(MODULE_NAME, MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Error getting key column for ' || l_credential_set_name || ' for target type ' || l_credential_set_ttype); END; END IF; IF l_creds IS NOT NULL THEN l_current_creds_set := 1; FOR j IN 1..l_creds.creds.COUNT LOOP -- Assign each column to the appropriate parameter name FOR k IN 1..l_credential_columns.COUNT LOOP IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('FOUND info for column: ' || l_credential_columns(k), MODULE_NAME); END IF; IF l_credential_columns(k)=l_creds.creds(j).credential_set_column THEN IF l_creds.pdp_data IS NOT NULL AND UPPER(TRIM(l_credential_columns(k))) LIKE '%USERNAME%' AND UPPER(TRIM(l_parameter_values(k).param_name)) NOT LIKE '%_NO_PDP' THEN l_parameter_values(k).vector_value(i) := encode_pdp_data(l_creds.creds(j).credential_value, l_creds.pdp_data); ELSE l_parameter_values(k).vector_value(i) := l_creds.creds(j).credential_value; END IF; -- Encryption applies only to key column. -- Set the flag only if it was not already set IF l_encrypt_info(k) IS NULL THEN IF NOT p_encrypt AND l_credential_columns(k) = l_key_col_name THEN l_encrypt_info(k) := 0; ELSE l_encrypt_info(k) := 1; END IF; END IF; END IF; END LOOP; END LOOP; IF l_set_override=1 THEN IF l_job_creds IS NULL THEN l_job_creds := MGMT_JOB_CRED_ARRAY(); END IF; -- Add these credentials as override credentials for -- this target l_job_creds.extend(1); l_job_creds(l_job_cred_index) := MGMT_JOB_CRED_RECORD.NEW(l_target_names(i), l_target_types(i), l_container_path, MGMT_CRED_RECORD.NEW(p_job_owner, l_credential_set_name, l_creds.creds, l_creds.pdp_data)); l_job_cred_index := l_job_cred_index+1; END IF; ELSE IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('NULL credentials!', MODULE_NAME); END IF; l_current_creds_set := 0; l_creds_set := 0; END IF; IF l_suspend_on_nocreds=1 THEN upsert_exec_cred_info(p_job_id, p_execution_id, p_task_name, l_target_guid, l_container_path, l_credential_set_name, l_current_creds_set); END IF; <> NULL; END LOOP; -- Set override credentials if required IF l_set_override=1 AND l_job_creds IS NOT NULL THEN MGMT_CREDENTIAL.set_job_credentials(p_job_id, l_job_creds); END IF; -- Finally, update the parameters of the job IF p_insert_params THEN update_job_parameters(p_job_id, p_execution_id, l_parameter_values, l_encrypt_info, p_encrypt, NOT p_override); END IF; IF l_suspend_on_nocreds=1 THEN IF l_creds_set=0 THEN RETURN false; ELSE RETURN true; END IF; ELSE IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('suspend on no creds is false: returning', MODULE_NAME); END IF; RETURN true; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- Bug 6855856: Call reset_params before raising error reset_params(); raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Missing or invalid source parameters in credentials source'); WHEN OTHERS THEN -- Exception block to ensure that we always clear the cache. reset_params(); RAISE; END; -- Apply the "substValues" parameter source PROCEDURE apply_subst_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_source_param_names MGMT_JOB_VECTOR_PARAMS; l_parameter_values MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); l_source_params MGMT_JOB_PARAM_LIST; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Applying substValues source', MODULE_NAME); END IF; SELECT source_params INTO l_source_param_names FROM MGMT_JOB_SUBST_PARAMS WHERE source_id=p_source_id; SELECT MGMT_JOB_PARAM_RECORD(parameter_name, parameter_type, scalar_value, vector_value) BULK COLLECT INTO l_source_params FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name IN (SELECT * FROM TABLE(CAST(l_source_param_names AS MGMT_JOB_VECTOR_PARAMS))) AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); -- If none of the source parameters were found, we've -- nothing to do IF l_source_params IS NULL OR l_source_params.COUNT=0 THEN RETURN; END IF; -- Substitute param values in the source list substitute_param_values(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, l_source_params); -- Change the parameter names to the destination names FOR i IN 1..l_source_params.COUNT LOOP -- Find this in the source param name list FOR j IN 1..l_source_param_names.COUNT LOOP IF l_source_param_names(j)=l_source_params(i).param_name THEN l_source_params(i).param_name := p_parameter_names(j); EXIT; END IF; END LOOP; END LOOP; reset_params(); -- Finally, update the parameters of the job update_job_parameters(p_job_id, p_execution_id, l_source_params, p_encrypt, NOT p_override); -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Apply the association parameter source -- Note. The xml verifier should have verified that we should -- have two and only two parameters specifying the vector -- parameters holding the target names and types respectively. PROCEDURE apply_assoc_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_job_assoc_record MGMT_JOB_ASSOC_PARAMS%ROWTYPE; l_assoc_cur EM_ASSOC.ASSOCIATION_CVTYPE; l_assoc_record EM_ASSOC.ASSOCIATION_ROWTYPE; l_params MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); l_index NUMBER := 0; l_target_names MGMT_JOB_VECTOR_PARAMS; l_target_types MGMT_JOB_VECTOR_PARAMS; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('apply_assoc_source:enter,job='|| p_job_id || ',exec=' || p_execution_id || ',source=' || p_source_id, MODULE_NAME); END IF; IF p_parameter_names.count != 2 THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Exactly two parameters should be specified for association parameter sources'); END IF; SELECT * INTO l_job_assoc_record FROM MGMT_JOB_ASSOC_PARAMS WHERE source_id=p_source_id; l_params.extend(p_parameter_names.count); l_params(1) := MGMT_JOB_PARAM_RECORD(l_job_assoc_record.target_names_param, 0, null, MGMT_JOB_VECTOR_PARAMS()); l_params(2) := MGMT_JOB_PARAM_RECORD(l_job_assoc_record.target_types_param, 0, null, MGMT_JOB_VECTOR_PARAMS()); EM_ASSOC.get_associated_targets ( l_job_assoc_record.src_target_name, l_job_assoc_record.src_target_type, l_job_assoc_record.assoc_name, l_assoc_cur ); LOOP FETCH l_assoc_cur into l_assoc_record; EXIT WHEN l_assoc_cur%NOTFOUND; IF l_assoc_record.assoc_target_type = l_job_assoc_record.assoc_target_type THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('apply_assoc_source: srcTgtName=' || l_job_assoc_record.src_target_name || ',srcTgtType=' || l_job_assoc_record.src_target_type || ',assocTgtName=' || l_assoc_record.assoc_target_name || ',assocTgtType=' || l_assoc_record.assoc_target_type, MODULE_NAME); END IF; l_params(1).vector_value.extend(1); l_params(2).vector_value.extend(1); l_index := l_index + 1; l_params(1).vector_value(l_index) := l_assoc_record.assoc_target_name; l_params(2).vector_value(l_index) := l_assoc_record.assoc_target_type; END IF; END LOOP; -- TBD. mark these params as target params so they are inserted into the ext targets table. reset_params(); -- Finally, update the parameters of the job update_job_parameters(p_job_id, p_execution_id, l_params, p_encrypt, NOT p_override); -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Apply the "properties" parameter source to the specified set of -- parameters. This assumes that all validations are done by the -- XML parser, which inserts entries into the properties source tables PROCEDURE apply_prop_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names IN OUT MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_property_names MGMT_JOB_VECTOR_PARAMS; l_property_names_param MGMT_JOB_PROP_PARAMS.property_names_param%TYPE; l_target_names MGMT_JOB_VECTOR_PARAMS; l_target_types MGMT_JOB_VECTOR_PARAMS; l_target_names_param MGMT_JOB_PROP_PARAMS.target_names_param%TYPE; l_target_types_param MGMT_JOB_PROP_PARAMS.target_types_param%TYPE; l_targets MGMT_JOB_TARGET_INDEX_LIST := MGMT_JOB_TARGET_INDEX_LIST(); l_target_guid RAW(16) := null; l_parameter_values MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); l_parameter_names MGMT_JOB_VECTOR_PARAMS := p_parameter_names; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Applying properties source', MODULE_NAME); END IF; SELECT property_names, property_names_param, target_names, target_types, target_names_param, target_types_param INTO l_property_names, l_property_names_param, l_target_names, l_target_types, l_target_names_param, l_target_types_param FROM MGMT_JOB_PROP_PARAMS WHERE source_id=p_source_id; -- Fetch the target name and type parameters BEGIN handle_target_params(l_target_names_param, l_target_types_param, l_target_names, l_target_types, p_job_id, p_execution_id, null, p_job_name, p_job_owner, false, MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR); EXCEPTION -- If the parameter was not found, that's OK, since the job type -- can choose to use the parameter conditionally WHEN NO_DATA_FOUND THEN reset_params; RETURN; END; reset_params(); IF l_target_names IS NULL OR l_target_types IS NULL THEN -- The XML parser will validate that the target names and -- types are not null; the only way this can happen is if -- a parameter name with a null value has been specified -- We will consider this valid usage, since the jobtype -- can use a switch stepset to control access to this parameter RETURN; ELSIF l_target_names.count != l_target_types.count THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'The target names and types params must have the same number of elements'); END IF; -- The property names could be an indirection IF l_property_names IS NULL AND l_property_names_param IS NULL THEN IF l_target_types.COUNT=0 THEN RETURN; END IF; -- Get all target properties for the specified target type. All -- targets are assumed to be of the same type. SELECT DISTINCT property_name BULK COLLECT INTO l_property_names FROM MGMT_TARGET_PROP_DEFS WHERE target_type=l_target_types(1) AND property_type='INSTANCE' AND credential_flag=0; IF l_property_names IS NULL THEN RETURN; END IF; ELSIF l_property_names IS NULL THEN l_property_names := get_vector_parameter(p_job_id, p_execution_id, l_property_names_param); IF l_property_names IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Property names parameter not specified'); END IF; END IF; -- If the job param names array is null, set it to the property -- names array, ie, fetch into parameters of the same name IF l_parameter_names IS NULL THEN l_parameter_names := MGMT_JOB_VECTOR_PARAMS(); l_parameter_names.extend(l_property_names.COUNT); FOR i IN 1..l_parameter_names.COUNT LOOP l_parameter_names(i) := l_property_names(i); END LOOP; p_parameter_names := l_parameter_names; END IF; -- Copy the target names and types into a target_with_index array l_targets.extend(l_target_names.count); FOR i in 1..l_target_names.count LOOP l_targets(i) := MGMT_JOB_TARGET_WITH_INDEX(l_target_names(i), l_target_types(i), null, i); END LOOP; -- The parameters set by the properties source -- are always vector parameters l_parameter_values.extend(l_parameter_names.count); FOR j IN 1..l_parameter_names.count LOOP l_parameter_values(j) := MGMT_JOB_PARAM_RECORD(l_parameter_names(j), 0, null, MGMT_JOB_VECTOR_PARAMS()); l_parameter_values(j).vector_value.extend(l_targets.count); END LOOP; -- Loop over the proeprty names.... FOR i in 1..l_property_names.COUNT LOOP FOR crec IN (SELECT /*+ ORDERED cardinality(jt 10) */ jt.target_index AS target_index, property_name, property_value FROM TABLE(CAST(l_targets AS MGMT_JOB_TARGET_INDEX_LIST)) jt, MGMT_TARGETS t, MGMT_TARGET_PROPERTIES p WHERE t.target_name = jt.target_name AND t.target_type = jt.target_type AND p.target_guid = t.target_guid AND p.property_name = l_property_names(i) AND p.property_type = 'INSTANCE' ORDER BY jt.target_index) LOOP l_parameter_values(i).vector_value(crec.target_index) := crec.property_value; END LOOP; END LOOP; -- Finally, update the parameters of the job update_job_parameters(p_job_id, p_execution_id, l_parameter_values, p_encrypt, NOT p_override); EXCEPTION WHEN NO_DATA_FOUND THEN -- Bug 6855856: Call reset_params before raising error reset_params(); raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'Missing or invalid source parameters in properties source'); WHEN OTHERS THEN -- Exception block to ensure that we always clear the cache. reset_params(); RAISE; END; -- Apply the "inline" source, which sets the parameters to the specified -- values, after substitution PROCEDURE apply_inline_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_params MGMT_JOB_PARAM_LIST; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Applying inline source', MODULE_NAME); END IF; SELECT param_values INTO l_params FROM MGMT_JOB_VALUE_PARAMS WHERE source_id=p_source_id; -- Substitute values for parameters substitute_param_values(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, l_params); -- Now insert them and we're done update_job_parameters(p_job_id, p_execution_id, l_params, p_encrypt, NOT p_override); END; -- Return true if the specified parameter value matches the value -- of a job parameter with the same name, false if the values don't -- match or the job parameter does not exist FUNCTION check_param(p_job_id RAW, p_execution_id RAW, p_param MGMT_JOB_PARAM_RECORD) RETURN BOOLEAN IS l_job_param MGMT_JOB_PARAM_RECORD; l_parameter_type NUMBER(1); l_scalar_value MGMT_JOB_PARAMETER.scalar_value%TYPE; l_vector_value MGMT_JOB_VECTOR_PARAMS; BEGIN SELECT parameter_type, decrypt_scalar(encrypted, scalar_value), decrypt_vector(encrypted, vector_value) INTO l_parameter_type, l_scalar_value, l_vector_value FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND parameter_name=p_param.param_name; l_job_param := MGMT_JOB_PARAM_RECORD(p_param.param_name, l_parameter_type, l_scalar_value, l_vector_value); IF l_job_param.param_type != p_param.param_type THEN RETURN false; END IF; IF l_job_param.param_type = PARAM_TYPE_SCALAR THEN RETURN (l_job_param.scalar_value=p_param.scalar_value); ELSIF l_job_param.param_type = PARAM_TYPE_VECTOR THEN -- For vector params, each value must match IF l_job_param.vector_value.count != p_param.vector_value.count THEN RETURN false; END IF; FOR i in 1..l_job_param.vector_value.count LOOP IF l_job_param.vector_value(i) != p_param.vector_value(i) THEN RETURN false; END IF; END LOOP; -- If we're here, all values ended up being equal RETURN true; ELSE raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SRC_ERR, 'check_values can be applied only to scalar or vector parameters'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN false; WHEN OTHERS THEN RAISE; END; -- Apply the check_values source. Throw an exception if there are -- parameters with incorrect values PROCEDURE apply_check_values_source(p_job_id RAW, p_execution_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_source_id RAW, p_source_data VARCHAR2, p_parameter_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN, p_override BOOLEAN) IS l_params MGMT_JOB_PARAM_LIST; l_count NUMBER := 0; l_error_string VARCHAR2(1000) := ''; l_action VARCHAR2(32); BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Applying check_values source', MODULE_NAME); END IF; SELECT param_values, action INTO l_params, l_action FROM MGMT_JOB_VALUE_PARAMS WHERE source_id=p_source_id; -- Substitute values for parameters substitute_param_values(p_job_id, p_execution_id, -1, null, -1, p_job_name, p_job_owner, l_params); -- Now loop through the parameters and check each value against -- actual job parameters. Throw an exception if values don't match FOR i in 1..l_params.count LOOP IF NOT check_param(p_job_id, p_execution_id, l_params(i)) THEN IF l_count = 0 THEN l_error_string := l_params(i).param_name; ELSE l_error_string := ', ' || l_params(i).param_name; END IF; l_count := l_count+1; END IF; END LOOP; IF l_count > 0 THEN IF l_action = 'suspend' THEN suspend_job_execution(p_execution_id); ELSE -- Throw an exception; the enclosing code will either -- abort the job or propogate the exception, depending -- on when this parameter source is being evaluated raise_application_error(MGMT_GLOBAL.INCORRECT_VALUES_ERR, 'checkValues source: The following parameter(s) had incorrect values: ' || l_error_string); END IF; END IF; END; -- Return the count of how many parameters in the specified set -- of parameter names have already been provided by the user. -- If p_encrypt is true, then encrypt all parameters FUNCTION process_override(p_job_id RAW, p_execution_id RAW, p_param_names MGMT_JOB_VECTOR_PARAMS, p_encrypt BOOLEAN) RETURN NUMBER IS l_count NUMBER := 0; BEGIN IF p_encrypt THEN UPDATE MGMT_JOB_PARAMETER SET scalar_value=encrypt_scalar(decode(encrypted, 1, 0, 1), scalar_value), vector_value=encrypt_vector(decode(encrypted, 1, 0, 1), vector_value), encrypted=1 WHERE job_id=p_job_id AND execution_id=p_execution_id AND created_at_submit=1 AND parameter_name IN (SELECT * FROM TABLE(CAST(p_param_names AS MGMT_JOB_VECTOR_PARAMS))); RETURN SQL%ROWCOUNT; ELSE SELECT --+ index(MGMT_JOB_PARAMETER) COUNT(*) INTO l_count FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id AND execution_id=p_execution_id AND created_at_submit=1 AND parameter_name IN (SELECT * FROM TABLE(CAST(p_param_names AS MGMT_JOB_VECTOR_PARAMS))); RETURN l_count; END IF; END; -- Suspend the job or CA on credentials PROCEDURE suspend_job_or_ca_on_creds(p_job_id RAW, p_execution_id RAW) IS l_is_ca MGMT_JOB.is_corrective_action%TYPE; BEGIN SELECT is_corrective_action INTO l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id; IF l_is_ca=1 THEN UPDATE MGMT_JOB SET broken=1, broken_reason=BROKEN_NO_CREDS WHERE job_id=p_job_id; END IF; IF p_execution_id != NO_EXECUTION THEN suspend_job_execution(p_execution_id, SUSPENDED_CREDS_STATUS, 0); END IF; END; -- Re-execute all submission/execution time credential sources. -- Called when -- an execution was suspended on missing credentials at submission -- OR -- to look up execution time parameter sources at submission time -- for credential purposes -- If p_insert_creds is 0, then the credential sources are evaluated, -- but do not actually insert parameters. They are used to suspend -- the execution FUNCTION compute_cred_info(p_job_id RAW, p_execution_id RAW, p_task_name VARCHAR2, p_at_submit NUMBER, p_insert_creds NUMBER) RETURN NUMBER IS CURSOR C(p_job_type_id RAW, p_submit NUMBER) IS SELECT MGMT_JOB_PARAMSRC_RECORD(source_id, source_type, source_data, override_user, required, encrypted, parameter_names) FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND apply_at_submission=p_submit AND source_type=SOURCE_TYPE_CRED ORDER BY source_index; l_param_srcs MGMT_JOB_PARAMSRC_ARRAY; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_creds_set NUMBER := 1; l_enc_bool BOOLEAN; l_override_bool BOOLEAN; l_insert_params_bool BOOLEAN := (p_insert_creds=1); l_task_job_type MGMT_JOB_EXECPLAN.nested_job_type%TYPE; l_suspend_on_nocreds MGMT_JOB_TYPE_INFO.suspend_on_nocreds%TYPE; BEGIN IF p_execution_id=NO_EXECUTION THEN l_job_type_id := get_job_type_id(p_job_id); ELSE l_job_type_id := get_job_type_id(p_job_id, p_execution_id); END IF; -- If a task name is provided, get the job type id of the task IF p_task_name IS NOT NULL THEN SELECT nested_job_type INTO l_task_job_type FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_job_type_id AND step_name=p_task_name AND step_type=STEPTYPE_JOB; SELECT mv.job_type_id INTO l_job_type_id FROM MGMT_JOB_TYPE_INFO ji, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE mv.job_type=l_task_job_type AND mv.job_type_id=ji.job_type_id AND mv.major_version= (SELECT MAX(major_version) FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=l_task_job_type); END IF; SELECT suspend_on_nocreds INTO l_suspend_on_nocreds FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; IF l_suspend_on_nocreds=0 THEN RETURN 1; END IF; get_job_name_and_owner(p_job_id, l_job_name, l_job_owner); OPEN C(l_job_type_id, p_at_submit); FETCH C BULK COLLECT INTO l_param_srcs; CLOSE C; IF l_param_srcs IS NOT NULL AND l_param_srcs.COUNT > 0 THEN FOR i IN 1..l_param_srcs.COUNT LOOP BEGIN -- *Sigh* l_enc_bool := (l_param_srcs(i).encrypted=1); l_override_bool := (l_param_srcs(i).override_user=1); IF NOT apply_cred_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_enc_bool, l_override_bool, l_insert_params_bool, p_task_name) THEN l_creds_set := 0; END IF; EXCEPTION WHEN MGMT_GLOBAL.invalid_params_in_param_src THEN -- It is possible that some of the parameters may be -- incomplete at submission time. So ignore errors thrown by -- the source NULL; END; END LOOP; END IF; IF p_insert_creds=0 AND l_creds_set=0 THEN suspend_job_or_ca_on_creds(p_job_id, p_execution_id); END IF; RETURN l_creds_set; END; -- Open and return an appropriate cursor to fetch parameter -- source information. -- if p_restarted_execution = 0 then all entries are fetched -- else only those entries are fetched which have apply_on_retry set to 1 -- if the step type is STEPTYPE_PARAMSRC_RETRY_EXEC -- then only those entries are fetched which have apply_on_retry set to 1 FUNCTION open_paramsrc_cursor(p_job_type_id RAW, p_step_name VARCHAR2, p_step_type VARCHAR2, p_submission NUMBER, p_paramsrc_step_type NUMBER, p_restarted_execution NUMBER DEFAULT 0) RETURN MGMT_JOB_REFCURSOR IS l_cr MGMT_JOB_REFCURSOR; BEGIN IF p_paramsrc_step_type=STEPTYPE_PARAMSRC_RETRY THEN OPEN l_cr FOR SELECT MGMT_JOB_PARAMSRC_RECORD(source_id, source_type, source_data, override_user, required, encrypted, parameter_names) FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND (apply_at_submission=1 OR (step_name=p_step_name AND step_type=p_step_type) ) ORDER BY source_index; ELSIF p_submission=1 THEN IF p_paramsrc_step_type IS NOT NULL AND p_paramsrc_step_type=STEPTYPE_PARAMSRC_RETRY_EXEC THEN OPEN l_cr FOR SELECT MGMT_JOB_PARAMSRC_RECORD(source_id, source_type, source_data, override_user, required, encrypted, parameter_names) FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND apply_at_submission=1 AND apply_on_retry=1 ORDER BY source_index; ELSE OPEN l_cr FOR SELECT MGMT_JOB_PARAMSRC_RECORD(source_id, source_type, source_data, override_user, required, encrypted, parameter_names) FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND apply_at_submission=1 ORDER BY source_index; END IF; ELSE IF p_paramsrc_step_type!=STEPTYPE_PARAMSRC_RETRY_EXEC THEN OPEN l_cr FOR SELECT MGMT_JOB_PARAMSRC_RECORD(source_id, source_type, source_data, override_user, required, encrypted, parameter_names) FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=p_step_type AND apply_at_submission=0 ORDER BY source_index; ELSE OPEN l_cr FOR SELECT MGMT_JOB_PARAMSRC_RECORD(source_id, source_type, source_data, override_user, required, encrypted, parameter_names) FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=p_job_type_id AND step_name=p_step_name AND step_type=p_step_type AND apply_at_submission=0 AND apply_on_retry=1 ORDER BY source_index; END IF; END IF; RETURN l_cr; END; -- Fetch job parameters from parameter sources. p_submission is true(1) -- if this is job submission time, 1 if this is job execution time -- If p_retry is true, this means that the step is being retried (this -- means that an earlier attempt to execute the step was unsuccessful, -- not to be confused with restarting an execution), In this case, -- Only creddenials and properties sources are re-executed. FUNCTION fetch_job_parameters(p_job_id RAW, p_execution_id RAW, p_step_name VARCHAR2, p_step_type VARCHAR2, p_submission NUMBER, p_paramsrc_step_type NUMBER DEFAULT NULL) RETURN NUMBER IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_num_existing_params NUMBER; l_encrypted BOOLEAN; l_override BOOLEAN; l_param_srcs MGMT_JOB_PARAMSRC_ARRAY; l_cr MGMT_JOB_REFCURSOR; l_job_name MGMT_JOB.job_name%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_restarted_exec INTEGER := 0; l_creds_set NUMBER := 1; BEGIN l_job_type_id := get_job_type_id(p_job_id, p_execution_id); -- check if this execution is a restarted one SELECT job_name, job_owner, decode(source_execution_id, p_execution_id, 0, 1) INTO l_job_name, l_job_owner, l_restarted_exec FROM MGMT_JOB j, MGMT_JOB_EXEC_SUMMARY e WHERE e.execution_id=p_execution_id AND j.job_id=e.job_id; l_cr := open_paramsrc_cursor(l_job_type_id, p_step_name, p_step_type, p_submission, p_paramsrc_step_type, l_restarted_exec); FETCH l_cr BULK COLLECT INTO l_param_srcs; CLOSE l_cr; -- Loop through each of the parameter sources (if any) -- and dispatch them FOR i IN 1..l_param_srcs.COUNT LOOP l_num_existing_params := 0; IF l_param_srcs(i).encrypted=1 THEN l_encrypted := true; ELSE l_encrypted := false; END IF; IF l_param_srcs(i).override_user=1 THEN l_override := true; ELSE l_override := false; END IF; -- The checkValues and user sources must execute regardless -- of override IF l_param_srcs(i).source_type = SOURCE_TYPE_CHECKVALUES THEN apply_check_values_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override); ELSIF l_param_srcs(i).source_type = SOURCE_TYPE_USER THEN apply_user_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).required, l_param_srcs(i).parameter_names, l_encrypted, l_override); ELSE -- Check if the parameter(s) have already been provided by the user -- If yes, then cache the user-provided values. IF l_param_srcs(i).override_user=0 AND l_param_srcs(i).parameter_names IS NOT NULL THEN l_num_existing_params := process_override(p_job_id, p_execution_id, l_param_srcs(i).parameter_names, l_encrypted); END IF; IF l_param_srcs(i).parameter_names IS NOT NULL AND l_num_existing_params=l_param_srcs(i).parameter_names.COUNT THEN -- Do not execute the parameter source if all the -- parameters have been overridden. NULL; ELSIF l_param_srcs(i).source_type = SOURCE_TYPE_SQL THEN apply_sql_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override); ELSIF l_param_srcs(i).source_type = SOURCE_TYPE_CRED THEN IF NOT apply_cred_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override) THEN l_creds_set := 0; END IF; ELSIF l_param_srcs(i).source_type= SOURCE_TYPE_PROP THEN apply_prop_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override); ELSIF l_param_srcs(i).source_type = SOURCE_TYPE_INLINE THEN apply_inline_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override); ELSIF l_param_srcs(i).source_type = SOURCE_TYPE_SUBSTVALUES THEN apply_subst_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override); ELSIF l_param_srcs(i).source_type = SOURCE_TYPE_ASSOC THEN apply_assoc_source(p_job_id, p_execution_id, l_job_name, l_job_owner, l_param_srcs(i).source_id, l_param_srcs(i).source_data, l_param_srcs(i).parameter_names, l_encrypted, l_override); END IF; END IF; END LOOP; reset_params; -- If credentials could not be fetched, suspend the execution. -- It will be resumed when credentials are available IF l_creds_set=0 THEN suspend_job_execution(p_execution_id, SUSPENDED_CREDS_STATUS, 0); END IF; RETURN l_creds_set; EXCEPTION WHEN OTHERS THEN reset_params; RAISE; END; -- Check and enforce all security directives for this job PROCEDURE check_security_info(p_job_id RAW, p_execution_id RAW, p_submission NUMBER DEFAULT 1) IS l_privilege_name MGMT_JOB_SEC_INFO.privilege_name%TYPE; l_privilege_type NUMBER(1); l_target_names_param MGMT_JOB_SEC_INFO.target_names_param%TYPE; l_target_types_param MGMT_JOB_SEC_INFO.target_types_param%TYPE; l_target_names MGMT_JOB_VECTOR_PARAMS; l_target_types MGMT_JOB_VECTOR_PARAMS; l_job_name MGMT_JOB.job_name%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; l_cr MGMT_JOB_REFCURSOR; BEGIN l_job_type_id := get_job_type_id(p_job_id, p_execution_id); SELECT job_name, job_owner INTO l_job_name, l_job_owner FROM MGMT_JOB WHERE job_id=p_job_id; FOR crec IN (SELECT privilege_name, privilege_type, target_names, target_types, target_names_param, target_types_param FROM MGMT_JOB_SEC_INFO WHERE job_type_id=l_job_type_id AND apply_at_submission=p_submission) LOOP IF crec.privilege_type=SYSTEM_PRIVILEGE THEN -- Check that the owner of the job has this privilege and -- we're done IF MGMT_USER.has_priv(l_job_owner, crec.privilege_name)=0 THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'The user does not have sufficient privileges to submit this job'); END IF; ELSE l_target_names := crec.target_names; l_target_types := crec.target_types; l_target_names_param := crec.target_names_param; l_target_types_param := crec.target_types_param; -- Fetch the target names and types handle_target_params(l_target_names_param, l_target_types_param, l_target_names, l_target_types, p_job_id, p_execution_id, null, l_job_name, l_job_owner, false, MGMT_GLOBAL.INVALID_PARAMS_IN_SEC_ERR); reset_params; -- If there are no matching targets, that's OK; it just -- means there are no security checks IF l_target_names IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_IN_SEC_ERR, 'null target names/types param'); END IF; FOR i in 1..l_target_names.count LOOP -- Check that the owner of the job has the specified -- privilege on the specified target IF MGMT_USER.has_priv(l_job_owner, crec.privilege_name, l_target_names(i), l_target_types(i))=0 THEN raise_application_error(MGMT_GLOBAL.INSUFFICIENT_PRIVILEGES_ERR, 'The user does not have sufficient privileges to submit this job'); END IF; END LOOP; END IF; END LOOP; -- Exception block to ensure that we always clear the cache. EXCEPTION WHEN OTHERS THEN reset_params(); RAISE; END; -- Update the status of a command block. If p_status is -- COMPLETED, the error message is null. -- If p_status is FAILED then we insert error messages -- for these some cases from the java layer. for example, -- 1. the command block size is too large for us to handle (>32K) -- 2. there is a mismatch between the procedure signature -- which was registerd and the arguments that were passed in. PROCEDURE update_command_block_status(p_step_id NUMBER, p_sequence_number INTEGER, p_status NUMBER, p_text_id RAW DEFAULT NULL, p_error_message VARCHAR2 DEFAULT NULL) IS BEGIN IF p_sequence_number >= 0 THEN IF NOT update_sequence_number(p_step_id, p_sequence_number) THEN RETURN; END IF; END IF; INSERT INTO MGMT_JOB_STEP_COMMAND_LOG (step_id, command_block_text_id, command_block_id, command_block_status, command_block_error) VALUES (p_step_id, p_text_id, MGMT_JOB_SEQUENCE.NEXTVAL, p_status, p_error_message); END; -- Apply a command block corresponding to the specified step. PROCEDURE apply_command_block(p_step_id NUMBER, p_command_block VARCHAR2, p_sequence_number INTEGER) IS l_message VARCHAR2(5000); l_output CLOB; BEGIN l_output := get_output(p_step_id, 1, p_sequence_number); IF l_output IS NULL THEN RETURN; END IF; l_message := CRLF || 'Ignoring command block ' || p_command_block || CRLF; DBMS_LOB.writeappend(l_output, LENGTH(l_message), l_message); EXCEPTION WHEN OTHERS THEN write_step_error_message(p_step_id, 'Error updating command block: ' || SQLERRM); END; -- Set the targets of a step PROCEDURE set_step_targets(p_step_id NUMBER, p_targets MGMT_JOB_TARGET_LIST) IS BEGIN DELETE FROM MGMT_JOB_STEP_TARGETS WHERE step_id=p_step_id; FOR i IN 1..p_targets.COUNT LOOP INSERT INTO MGMT_JOB_STEP_TARGETS(step_id, target_guid) SELECT p_step_id, target_guid FROM MGMT_TARGETS WHERE target_name=p_targets(i).target_name AND target_type=p_targets(i).target_type; END LOOP; END; -- Autonomous transaction version PROCEDURE set_step_targets_auto(p_step_id NUMBER, p_targets MGMT_JOB_TARGET_LIST) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN set_step_targets(p_step_id, p_targets); COMMIT; END; PROCEDURE raise_invalid_purge(p_message VARCHAR2) IS BEGIN raise_application_error(MGMT_GLOBAL.INVALID_PURGE_CRITERION_ERR, p_message); END; PROCEDURE save_target_purge_criterion(p_policy_name VARCHAR2, p_criterion_index NUMBER, p_purge_criterion MGMT_JOB_PURGE_CRITERION) IS BEGIN -- The criterion_values holds the target names, and the -- addl_criterion_values holds the target types IF p_purge_criterion.criterion_values.count != p_purge_criterion.addl_criterion_values.count THEN raise_invalid_purge('The number of target names must be equal to the number of types'); END IF; -- Insert the targets FOR i in 1..p_purge_criterion.criterion_values.count LOOP INSERT INTO MGMT_JOB_PURGE_TARGETS (policy_name, criterion_index, purge_tguid) SELECT p_policy_name, p_criterion_index, target_guid FROM MGMT_TARGETS WHERE target_name = p_purge_criterion.criterion_values(i) AND target_type = p_purge_criterion.addl_criterion_values(i); IF SQL%ROWCOUNT = 0 THEN raise_invalid_purge('Invalid target ' || p_purge_criterion.criterion_values(i)); END IF; END LOOP; END; PROCEDURE save_jobtype_purge_criterion(p_policy_name VARCHAR2, p_criterion_index NUMBER, p_purge_criterion MGMT_JOB_PURGE_CRITERION) IS BEGIN -- Insert the job types FOR i in 1..p_purge_criterion.criterion_values.count LOOP INSERT INTO MGMT_JOB_PURGE_VALUES (policy_name, criterion_index, purge_value) SELECT p_policy_name, p_criterion_index, p_purge_criterion.criterion_values(i) FROM DUAL WHERE EXISTS ( SELECT 1 FROM MGMT_JOB_TYPE_INFO WHERE job_type = p_purge_criterion.criterion_values(i)); IF SQL%ROWCOUNT = 0 THEN raise_invalid_purge('Invalid job type ' || p_purge_criterion.criterion_values(i)); END IF; END LOOP; END; PROCEDURE save_user_purge_criterion(p_policy_name VARCHAR2, p_criterion_index NUMBER, p_purge_criterion MGMT_JOB_PURGE_CRITERION) IS BEGIN -- Insert the user names FOR i in 1..p_purge_criterion.criterion_values.count LOOP INSERT INTO MGMT_JOB_PURGE_VALUES (policy_name, criterion_index, purge_value) SELECT p_policy_name, p_criterion_index, user_name FROM MGMT_CREATED_USERS WHERE user_name = UPPER(p_purge_criterion.criterion_values(i)); IF SQL%ROWCOUNT = 0 THEN raise_invalid_purge('Invalid user ' || p_purge_criterion.criterion_values(i)); END IF; END LOOP; END; PROCEDURE save_jobname_purge_criterion(p_policy_name VARCHAR2, p_criterion_index NUMBER, p_purge_criterion MGMT_JOB_PURGE_CRITERION) IS BEGIN -- jobname like can have exactly one value IF p_purge_criterion.criterion_values.count > 1 THEN raise_invalid_purge('Jobname like criterion can have only one matching value'); END IF; INSERT INTO MGMT_JOB_PURGE_VALUES (policy_name, criterion_index, purge_value) VALUES (p_policy_name, p_criterion_index, p_purge_criterion.criterion_values(1)); END; FUNCTION get_job_based_purge_clause(p_negated INTEGER, p_job_filter VARCHAR2, p_bind_var_index IN OUT NUMBER) RETURN VARCHAR2 IS l_negate_string VARCHAR2(5); l_clause VARCHAR2(4000) := JOB_BASED_CRIT_CLAUSE; BEGIN IF p_negated = 1 THEN l_clause := REPLACE(l_clause, '$NEGATE$', NEGATE_STRING); ELSE l_clause := REPLACE(l_clause, '$NEGATE$', NULL); END IF; p_bind_var_index := p_bind_var_index + 1; l_clause := REPLACE(l_clause, '$FILTER$', p_job_filter); l_clause := REPLACE(l_clause, '$INDEX$', TO_CHAR(p_bind_var_index)); RETURN l_clause; END; -- Return a clause to enforce a target criterion FUNCTION get_target_purge_clause(p_negated INTEGER, p_bind_var_index IN OUT NUMBER) RETURN VARCHAR2 IS l_negate_string VARCHAR2(5); BEGIN IF p_negated = 1 THEN l_negate_string := NEGATE_STRING; END IF; p_bind_var_index := p_bind_var_index + 1; RETURN ' AND '|| l_negate_string || ' EXISTS ( SELECT purge_tguid FROM MGMT_JOB_PURGE_TARGETS WHERE policy_name = :pname AND criterion_index = :idx' || TO_CHAR(p_bind_var_index) || ' AND purge_tguid IN (SELECT target_guid FROM MGMT_JOB_EXT_TARGETS ext WHERE ext.execution_id = e.execution_id)) '; END; -- Return a clause to enforce the jobtype criterion FUNCTION get_jobtype_purge_clause(p_negated INTEGER, p_bind_var_index IN OUT NUMBER) RETURN VARCHAR2 IS BEGIN RETURN get_job_based_purge_clause(p_negated, 'job_type IN', p_bind_var_index); END; -- Return a clause to enforce the user criterion FUNCTION get_user_purge_clause(p_negated INTEGER, p_bind_var_index IN OUT NUMBER) RETURN VARCHAR2 IS BEGIN RETURN get_job_based_purge_clause(p_negated, 'job_owner IN', p_bind_var_index); END; -- Return a clause to enforce the jobname criterion FUNCTION get_jobname_purge_clause(p_negated INTEGER, p_bind_var_index IN OUT NUMBER) RETURN VARCHAR2 IS BEGIN RETURN get_job_based_purge_clause(p_negated, 'job_name LIKE', p_bind_var_index); END; -- Register a purge criterion with the specified purge policy PROCEDURE register_purge_criterion(p_policy_name VARCHAR2, p_criterion_index NUMBER, p_purge_criterion MGMT_JOB_PURGE_CRITERION, p_bind_var_index IN OUT NUMBER) IS BEGIN IF p_purge_criterion.criterion_values IS NULL OR p_purge_criterion.criterion_values.count=0 THEN raise_invalid_purge('There must be at least one purge criterion value in a criterion'); END IF; CASE p_purge_criterion.criterion_type WHEN TARGET_CRITERION THEN save_target_purge_criterion(p_policy_name, p_criterion_index, p_purge_criterion); WHEN USER_CRITERION THEN save_user_purge_criterion(p_policy_name, p_criterion_index, p_purge_criterion); WHEN JOBTYPE_CRITERION THEN save_jobtype_purge_criterion(p_policy_name, p_criterion_index, p_purge_criterion); WHEN JOBNAME_CRITERION THEN save_jobname_purge_criterion(p_policy_name, p_criterion_index, p_purge_criterion); ELSE raise_invalid_purge('Invalid purge criterion type'); END CASE; -- Insert an entry for this purge criterion INSERT INTO MGMT_JOB_PURGE_CRITERIA (policy_name, criterion_index, criterion_type, negated) VALUES (p_policy_name, p_criterion_index, p_purge_criterion.criterion_type, p_purge_criterion.negated); END; -- Register a new purge policy with the specified name, timeframe (in days) -- and purge criteria PROCEDURE register_purge_policy(p_policy_name VARCHAR2, p_timeframe NUMBER, p_purge_criteria MGMT_JOB_PURGE_CRITERION_LIST) IS unique_constraint EXCEPTION; PRAGMA EXCEPTION_INIT(unique_constraint, -1); l_policy_name VARCHAR2(32) := upper(p_policy_name); l_bind_var_index NUMBER := 0; BEGIN -- Only superusers can register and drop purge policies... check_priv(MGMT_USER.SUPER_USER); -- Insert an entry for this purge policy BEGIN INSERT INTO MGMT_JOB_PURGE_POLICIES (policy_name, time_frame) VALUES (l_policy_name, p_timeframe); EXCEPTION WHEN unique_constraint THEN raise_application_error(MGMT_GLOBAL.PURGE_POLICY_EXISTS_ERR, 'Purge policy exists'); END; -- It is permissible for the purge criteria array to be empty: -- it simply means purge all executions with the given timeframe IF p_purge_criteria IS NULL OR p_purge_criteria.count=0 THEN RETURN; END IF; -- Process each of the purge criteria FOR i in 1..p_purge_criteria.count LOOP register_purge_criterion(l_policy_name, i, p_purge_criteria(i), l_bind_var_index); END LOOP; END; -- Drop a purge policy that has the specified name PROCEDURE drop_purge_policy(p_policy_name VARCHAR2) IS BEGIN -- Only superusers can register and drop purge policies... check_priv(MGMT_USER.SUPER_USER); DELETE FROM MGMT_JOB_PURGE_POLICIES WHERE policy_name = UPPER(p_policy_name); END; -- Remove target-scoped CAs when a target is deleted PROCEDURE remove_orphaned_cas(p_target_guid RAW) IS l_ca_ids MGMT_JOB_GUID_ARRAY; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: remove_orphaned_cas:enter', MODULE_NAME); END IF; SELECT job_id BULK COLLECT INTO l_ca_ids FROM MGMT_CORRECTIVE_ACTION WHERE ca_scope = MGMT_CA.CA_SCOPE_TARGET AND ca_target_guid = p_target_guid ORDER BY job_id; IF l_ca_ids IS NOT NULL AND l_ca_ids.COUNT > 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: ca count:' || l_ca_ids.COUNT, MODULE_NAME); END IF; FOR i IN 1..l_ca_ids.COUNT LOOP BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: deleting CA:' || l_ca_ids(i), MODULE_NAME); END IF; delete_ca(l_ca_ids(i)); EXCEPTION WHEN OTHERS THEN --CA might have been deleted, so ignore. log_error(l_ca_ids(i), MGMT_GLOBAL.INVALID_JOB_ERR, 'Failed to delete CA:' || SQLERRM, false); END; END LOOP; END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: remove_orphaned_cas:exit', MODULE_NAME); END IF; END; -- Purge the specified execution PROCEDURE purge_execution(p_execution_id RAW) IS l_dep_exec_ids MGMT_JOB_GUID_ARRAY; l_statuses SMP_EMD_INTEGER_ARRAY; BEGIN -- Select all execution ids that are dependent -- on the current one: they need to be deleted -- first SELECT execution_id, status BULK COLLECT INTO l_dep_exec_ids, l_statuses FROM MGMT_JOB_EXEC_SUMMARY WHERE source_execution_id=p_execution_id AND execution_id != p_execution_id; IF l_dep_exec_ids IS NOT NULL AND l_dep_exec_ids.COUNT>0 THEN FOR i IN 1..l_dep_exec_ids.COUNT LOOP BEGIN -- If the dependent (retried) execution is still active, -- then this execution cannot be removed IF l_statuses(i) NOT IN (COMPLETED_STATUS, STOPPED_STATUS, ABORTED_STATUS, FAILED_STATUS, SKIPPED_STATUS, DELETE_PENDING_STATUS) THEN RETURN; END IF; delete_job_execution(l_dep_exec_ids(i), false, true); EXCEPTION WHEN OTHERS THEN NULL; END; END LOOP; END IF; -- Delete this execution once all source executions -- have been removed delete_job_execution(p_execution_id, false, true); COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; IF SQLCODE NOT IN (MGMT_GLOBAL.INVALID_EXECUTION_ERR, MGMT_GLOBAL.ACTIVE_EXECUTIONS_EXIST_ERR) THEN log_error(p_execution_id, SQLCODE, 'Unable to purge exception: ' || SQLERRM); END IF; END; -- Apply the specified purge policy PROCEDURE apply_purge_policy(p_policy_name VARCHAR2, p_time_frame NUMBER) IS l_select_statement VARCHAR2(32767); l_crsr INTEGER; l_num_criteria NUMBER := 0; n NUMBER; l_execution_id VARCHAR2(64); l_rowset_size NUMBER := 500; l_idx NUMBER := 1; l_cnt NUMBER; l_bulk_ids DBMS_SQL.VARCHAR2_TABLE; l_purge_clause VARCHAR2(4000); BEGIN -- Get the rowset size from the MGMT_PARAMETERS table BEGIN SELECT parameter_value INTO l_rowset_size FROM MGMT_PARAMETERS WHERE parameter_name=PURGE_ROWSET_SIZE_NAME; EXCEPTION WHEN NO_DATA_FOUND OR VALUE_ERROR THEN NULL; END; l_select_statement := 'SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j ' || ' WHERE e.job_id = j.job_id AND j.is_corrective_action = 0 ' || ' AND status IN (' || COMPLETED_STATUS || ',' || FAILED_STATUS || ',' || ABORTED_STATUS || ',' || SKIPPED_STATUS || ',' || STOPPED_STATUS || ') AND (CAST(SYS_EXTRACT_UTC(SYSTIMESTAMP) AS DATE) - e.start_time) > (:tf) '; FOR crec IN (SELECT criterion_type, negated FROM MGMT_JOB_PURGE_CRITERIA WHERE policy_name = p_policy_name ORDER BY criterion_index) LOOP CASE crec.criterion_type WHEN TARGET_CRITERION THEN l_purge_clause := get_target_purge_clause(crec.negated, l_num_criteria); WHEN USER_CRITERION THEN l_purge_clause := get_job_based_purge_clause(crec.negated, 'job_owner IN', l_num_criteria); WHEN JOBTYPE_CRITERION THEN l_purge_clause := get_job_based_purge_clause(crec.negated, 'job_type IN', l_num_criteria); WHEN JOBNAME_CRITERION THEN l_purge_clause := get_job_based_purge_clause(crec.negated, 'job_name LIKE', l_num_criteria); ELSE raise_invalid_purge('Invalid purge criterion type'); END CASE; l_select_statement := l_select_statement || l_purge_clause; END LOOP; -- Read in rowsetSize rows at a time to avoid snapshot too old -- issues l_select_statement := l_select_statement || ' AND ROWNUM < ' || l_rowset_size; -- this ordering is to delete retried executions first -- since we are deleting out of the "standard" order we need to commit -- after each delete to prevent deadlocks (see purge above) l_select_statement := l_select_statement || ' ORDER BY start_time desc'; -- Native dynamic SQL cannot be used here unless we replace all -- bind parameters with static constants while generating the SQL -- (native dynamic SQL assumes that the number of bind params is -- known in advance). However, generating SQL without bind parameters -- means we lose cursor reuse. Hence the use of dbms_sql here. l_crsr := DBMS_SQL.open_cursor; DBMS_SQL.PARSE(l_crsr, l_select_statement, DBMS_SQL.native); LOOP l_idx := 1; IF l_bulk_ids IS NOT NULL THEN l_bulk_ids.DELETE; END IF; DBMS_SQL.define_array(l_crsr, 1, l_bulk_ids, l_rowset_size, l_idx); DBMS_SQL.bind_variable(l_crsr, 'tf', p_time_frame); IF l_num_criteria > 0 THEN DBMS_SQL.BIND_VARIABLE(l_crsr, 'pname', p_policy_name); END IF; -- Now go ahead and bind each index bind variable in turn FOR i in 1..l_num_criteria LOOP DBMS_SQL.BIND_VARIABLE(l_crsr, 'idx' || i, i); END LOOP; n := DBMS_SQL.execute(l_crsr); l_cnt := DBMS_SQL.fetch_rows(l_crsr); IF l_cnt > 0 THEN DBMS_SQL.COLUMN_VALUE(l_crsr, 1, l_bulk_ids); -- Loop over all retrieved execution ids and purge them FOR i IN 1..l_cnt LOOP l_execution_id := HEXTORAW(l_bulk_ids(i)); purge_execution(l_execution_id); END LOOP; ELSE EXIT; END IF; END LOOP; DBMS_SQL.CLOSE_CURSOR(l_crsr); EXCEPTION WHEN OTHERS THEN MGMT_LOG.log_error(MODULE_NAME, MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Job Purge encountered error: ' || SQLERRM); IF DBMS_SQL.is_open(l_crsr) THEN DBMS_SQL.CLOSE_CURSOR(l_crsr); END IF; END; PROCEDURE DBMSJOB_EXTENDED_SQL_TRACE_ON(p_value IN BOOLEAN) IS BEGIN MGMT_SQLTRACE.EXTENDED_SQL_TRACE_ON(EST_JOB_NAME, p_value); END DBMSJOB_EXTENDED_SQL_TRACE_ON; -- Apply all registered purge policies PROCEDURE apply_purge_policies IS BEGIN MGMT_SQLTRACE.EXTENDED_SQL_TRACE(EST_JOB_NAME); FOR crec IN (SELECT policy_name, time_frame FROM MGMT_JOB_PURGE_POLICIES) LOOP apply_purge_policy(crec.policy_name, crec.time_frame); END LOOP; END; -- Compute the "extended" target list of the job based on current -- parameters. PROCEDURE compute_extended_target_list(p_execution_id RAW) IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_target_list_index NUMBER; l_job_id RAW(16); BEGIN BEGIN SELECT j.job_id, target_list_index, e.job_type_id INTO l_job_id, l_target_list_index, l_job_type_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE execution_id=p_execution_id AND j.job_id=e.job_id; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; END; -- We're computing the list from scratch, so get rid of any -- existing targets DELETE FROM MGMT_JOB_EXT_TARGETS WHERE job_id=l_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index; -- First pull the targets from the target list of the job -- and insert them FOR crec IN (SELECT target_guid FROM MGMT_JOB_TARGET WHERE job_id=l_job_id AND execution_id=p_execution_id AND target_list_index=l_target_list_index) LOOP insert_ext_target(l_job_id, p_execution_id, l_target_list_index, crec.target_guid); END LOOP; -- Next pull out all the "target" parameters and insert those -- as well FOR source_cursor IN (SELECT source_id FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=l_job_type_id AND source_type=SOURCE_TYPE_USER) LOOP process_user_src_ext_tgts(l_job_id, p_execution_id, source_cursor.source_id, false); END LOOP; END; PROCEDURE mark_jobs_on_del_target(p_target_guid RAW, p_target_type VARCHAR2, p_chk_finished_execs BOOLEAN) IS l_execution_ids MGMT_JOB_GUID_ARRAY; l_exec_statuses MGMT_JOB_INT_ARRAY; l_has_other_tgt_execs MGMT_JOB_INT_ARRAY; l_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; l_dummy NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_filter_exec_statuses MGMT_JOB_INT_ARRAY; l_filter_job_statuses MGMT_JOB_INT_ARRAY; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('TGT_DEL: starting target:'||p_target_guid||', check finished execs:' || CASE p_chk_finished_execs WHEN TRUE THEN 'true' ELSE 'false' END, MODULE_NAME); END IF; -- The filter_xxx_statuses contain the statuses that may be ignored while -- stopping/marking the job/execution IF p_chk_finished_execs THEN l_filter_job_statuses := MGMT_JOB_INT_ARRAY(JOB_STATUS_DELETE_PENDING); l_filter_exec_statuses := MGMT_JOB_INT_ARRAY(DELETE_PENDING_STATUS); ELSE l_filter_job_statuses := MGMT_JOB_INT_ARRAY(JOB_STATUS_DELETE_PENDING, JOB_STATUS_EXPIRED); l_filter_exec_statuses := MGMT_JOB_INT_ARRAY(DELETE_PENDING_STATUS, COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS, SKIPPED_STATUS); END IF; -- select all jobs that have active executions referencing the target being deleted FOR crec IN (SELECT DISTINCT j.job_id, j.restartable, j.is_corrective_action, j.broken FROM MGMT_JOB j, MGMT_JOB_EXEC_SUMMARY s, MGMT_JOB_EXT_TARGETS ex WHERE ex.target_guid=p_target_guid AND s.execution_id = ex.execution_id AND j.job_id = s.job_id AND j.nested = 0 AND j.job_status NOT IN (SELECT * FROM TABLE(CAST(l_filter_job_statuses AS MGMT_JOB_INT_ARRAY))) AND s.status NOT IN (SELECT * FROM TABLE(CAST(l_filter_exec_statuses AS MGMT_JOB_INT_ARRAY))) ORDER BY j.job_id) LOOP IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: START processing job id: ' || crec.job_id, MODULE_NAME); END IF; BEGIN -- get all appropriate executions against specified job and -- specified target with a flag indicating whether the same -- execution runs on other targets as well SELECT s.execution_id, s.status, MAX(DECODE(ext.target_guid, p_target_guid, 0, 1)) BULK COLLECT INTO l_execution_ids, l_exec_statuses, l_has_other_tgt_execs FROM MGMT_JOB_EXEC_SUMMARY s, MGMT_JOB_EXT_TARGETS ext WHERE s.job_id = crec.job_id AND s.execution_id = ext.execution_id AND s.status NOT IN (SELECT * FROM TABLE(CAST(l_filter_exec_statuses AS MGMT_JOB_INT_ARRAY))) GROUP BY s.execution_id, s.status, s.expected_start_time, s.target_list_index HAVING MAX(DECODE(ext.target_guid, p_target_guid, 1, 0))=1 ORDER BY s.expected_start_time, s.target_list_index; --lock the job. Beyond this point, the job would have some update l_status := lock_job(crec.job_id); IF l_execution_ids.COUNT > 0 THEN --any exception that requires us to skip the entry in the loop will be --caught by handler at the end of loop DECLARE l_multiple_targets NUMBER; BEGIN -- Because 1 is TRUE and 0 is FALSE, MAX acts like a group "OR" on -- the values in the following query -- Thus, l_multiple_targets will let us know if there were any -- executions that ran against another target in addition to the -- deleted target SELECT MAX(column_value) INTO l_multiple_targets FROM TABLE(CAST(l_has_other_tgt_execs AS MGMT_JOB_INT_ARRAY)); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: value of l_multiple_targets: ' || l_multiple_targets, MODULE_NAME); END IF; FOR i IN 1..l_execution_ids.COUNT LOOP IF l_exec_statuses(i) NOT IN (COMPLETED_STATUS, FAILED_STATUS, ABORTED_STATUS, STOPPED_STATUS, SKIPPED_STATUS) THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: Stopping active execution: ' || l_execution_ids(i), MODULE_NAME); END IF; BEGIN -- Stop the execution: check security - false, schedule next - false -- force stop - true, stop waiting - true stop_execution(l_execution_ids(i), false, false, true, true); EXCEPTION WHEN OTHERS THEN -- Waiting executions may already be removed IF SQLCODE != MGMT_GLOBAL.INVALID_EXECUTION_ERR THEN log_error(l_execution_ids(i), SQLCODE, 'Error stopping exec: '||SQLERRM||CHR(10)||DBMS_UTILITY.format_error_stack); END IF; END; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: Stopped active execution: ' || l_execution_ids(i), MODULE_NAME); END IF; IF l_multiple_targets=0 OR l_exec_statuses(i) = WAITING_STATUS THEN -- mark waiting execs as delete pending -- in case we still have some waiting exec it will -- be later cleaned in post delete UPDATE MGMT_JOB_EXEC_SUMMARY SET status = DELETE_PENDING_STATUS, deleted_target_guid = p_target_guid WHERE execution_id = l_execution_ids(i); END IF; END IF; END LOOP; END; END IF; -- Handle CAs triggered in the past -- 1) If the target being deleted is the target against which CA is -- configured, delete the CA -- 2) If the target being deleted is in the submission list, but is -- not the target against which CA is configured, mark the CA as -- broken. This can happen for single execution mtask jobs where CA -- is configured for target A, but target B - which is in one of the -- tasks, is being deleted. IF crec.is_corrective_action = 1 AND crec.broken = 0 THEN DECLARE l_this_tgt_execs NUMBER; l_other_tgt_execs NUMBER; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: Job is a CA: ' || crec.job_id, MODULE_NAME); END IF; -- Look for CAs configured against the target being deleted -- l_this_tgt_execs will be true (1) if there were any CAs -- submitted against the deleted target and were to run against -- the deleted target. -- l_other_tgt_execs will be true (1) if there were any CAs -- submitted against the deleted target but ran against another SELECT MAX(DECODE(ca.ca_target_guid, p_target_guid, 1, 0)), MAX(DECODE(ca.ca_target_guid, p_target_guid, 0, 1)) INTO l_this_tgt_execs, l_other_tgt_execs FROM MGMT_CORRECTIVE_ACTION ca, MGMT_JOB_EXT_TARGETS ex WHERE ca.job_id = crec.job_id AND ca.ca_scope = CA_SCOPE_TARGET AND ex.job_id = crec.job_id AND ex.execution_id = NO_EXECUTION AND ex.target_guid = p_target_guid GROUP BY ex.execution_id; IF l_this_tgt_execs > 0 THEN -- The CA is configured against the target being deleted delete_ca( p_job_id => crec.job_id ); ELSIF l_other_tgt_execs > 0 THEN -- the target being deleted is in the submission list, but -- is not the target against which CA is configured. IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: Marking CA as broken: ' || crec.job_id, MODULE_NAME); END IF; -- As the target would be reset for these CAs, we need to -- update the broken flag here, else the later loop that -- handles non-triggered CAs would not catch these jobs UPDATE MGMT_JOB SET broken = 1, broken_reason = BROKEN_TARGET_DELETED WHERE job_id = crec.job_id; END IF; END; END IF; -- End Handle triggered CAs l_job_type_id := get_job_type_id(crec.job_id); IF is_single_target_job_type_id(l_job_type_id) THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: marking job executions to delete pending for single tgt job.', MODULE_NAME); END IF; -- in case of single target executions -- mark the execution to be deleted_pending and store the -- target_guid in MGMT_JOB_EXEC_SUMMARY.deleted_target_guid UPDATE MGMT_JOB_EXEC_SUMMARY SET status = DELETE_PENDING_STATUS, deleted_target_guid = p_target_guid WHERE job_id = crec.job_id AND status != DELETE_PENDING_STATUS AND execution_id IN (SELECT * FROM TABLE(CAST(l_execution_ids AS MGMT_JOB_GUID_ARRAY))); ELSIF crec.restartable = 1 THEN -- mark job as non-restartable if job is restartable -- We need to do this only for execs against multiple targets, -- but the single-execution check should be sufficient IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: marking job as non-restartable', MODULE_NAME); END IF; UPDATE MGMT_JOB j SET j.restartable = 0 WHERE j.job_id = crec.job_id; END IF; -- Earlier, this block ran only for single-execution jobs. It seems -- safe to run it for single-target jobs as well (and the perf load -- is not that high) DECLARE l_job_ids MGMT_JOB_GUID_ARRAY; l_del_execution_ids MGMT_JOB_GUID_ARRAY; l_target_list_indexes SMP_EMD_INTEGER_ARRAY; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: setting NO_GUID for the job and execs. nExecs: ' || l_execution_ids.COUNT, MODULE_NAME); END IF; -- update target guids to NO_GUID in all executions of the job -- and also at the job level -- So add NO_EXECUTION to l_execution_ids to take care of job l_execution_ids.EXTEND; l_execution_ids(l_execution_ids.COUNT) := NO_EXECUTION; -- in case of single exec jobs we set target_guid as NO_GUID -- In post delete, if all the target guids for an execution are -- NO_GUID then we mark the execution as delete pending. UPDATE MGMT_JOB_TARGET jt SET jt.target_guid = NO_GUID WHERE jt.execution_id IN (SELECT * FROM TABLE(CAST(l_execution_ids AS MGMT_JOB_GUID_ARRAY))) AND jt.target_guid = p_target_guid; -- to avoid primary key violation, delete first and then -- re-insert deleted target entries -- reference_count column will contain count of deleted targets DELETE FROM MGMT_JOB_EXT_TARGETS WHERE target_guid = p_target_guid AND execution_id IN (SELECT * FROM TABLE(CAST(l_execution_ids AS MGMT_JOB_GUID_ARRAY))) RETURNING job_id, execution_id, target_list_index BULK COLLECT INTO l_job_ids, l_del_execution_ids, l_target_list_indexes; FOR idx IN 1..l_del_execution_ids.COUNT LOOP insert_ext_target(l_job_ids(idx), l_del_execution_ids(idx), l_target_list_indexes(idx), NO_GUID); END LOOP; END; EXCEPTION WHEN OTHERS THEN log_error(crec.job_id, MGMT_GLOBAL.INVALID_JOB_ERR, 'Exception while processing job id:' || SQLERRM || CHR(10) || DBMS_UTILITY.format_error_stack, false); END; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: END processing job id: ' || crec.job_id, MODULE_NAME); END IF; END LOOP; -- Process target CAs that have not triggered yet (The ones that have -- already triggered are processed in above loop and have their target guids -- reset to zeros. -- Mark CA as broken if the target being deleted is in the submission list, -- but is not the target against which CA is configured DECLARE l_job_ids MGMT_JOB_GUID_ARRAY; BEGIN SELECT ca.job_id BULK COLLECT INTO l_job_ids FROM MGMT_CORRECTIVE_ACTION ca, MGMT_JOB_EXT_TARGETS ex WHERE ca.job_id = ex.job_id AND ca.ca_scope = CA_SCOPE_TARGET AND ca.ca_target_guid != p_target_guid AND ex.execution_id = NO_EXECUTION AND ex.target_guid = p_target_guid ORDER BY ca.job_id; -- We are not locking separately as the row is being updated anyway -- lock_job(l_job_ids(i)) FORALL i IN 1..l_job_ids.COUNT UPDATE MGMT_JOB SET broken = 1, broken_reason = BROKEN_TARGET_DELETED WHERE job_id = l_job_ids(i); END; --If the target being deleted is an aggregate target, --and if that target happens to be in submission list of any active single-target job --stop all executions of that job and mark the job for deletion. -- This assumes that all jobs that have the aggregate in execution list -- have already been processed in above loop, and hence their entries -- have been removed from M_J_T and M_J_E_T. So now if we find an entry -- for the target in these tables, it means that it's a single target -- job that has the aggregate in the submission list SELECT COUNT(1) INTO l_dummy FROM MGMT_TYPE_PROPERTIES WHERE target_type = p_target_type AND property_name = MGMT_GLOBAL.G_IS_AGGREGATE_PROP AND property_value = 1; IF l_dummy > 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: Target is aggregate. Processing jobs submitted against the target', MODULE_NAME); END IF; -- by now, if entry is found for the target, it must be single target job FOR crec IN (SELECT j.job_id, j.job_status FROM MGMT_JOB j, MGMT_JOB_EXT_TARGETS ex WHERE j.is_library = 0 AND j.is_corrective_action = 0 AND j.job_id = ex.job_id AND ex.target_guid = p_target_guid AND ex.execution_id = NO_EXECUTION AND j.job_status NOT IN (SELECT * FROM TABLE(CAST(l_filter_job_statuses AS MGMT_JOB_INT_ARRAY))) ORDER BY j.job_id) LOOP BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: processing job id: ' || crec.job_id || ' : job status: ' || crec.job_status, MODULE_NAME); END IF; IF crec.job_status != JOB_STATUS_EXPIRED THEN l_status := lock_job(crec.job_id); stop_all_executions_with_id(crec.job_id, TRUE); END IF; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL: marking job as delete pending: ' || crec.job_id, MODULE_NAME); END IF; --update job status to JOB_STATUS_DELETE_PENDING UPDATE MGMT_JOB SET job_status = JOB_STATUS_DELETE_PENDING WHERE job_id = crec.job_id; EXCEPTION WHEN OTHERS THEN log_error(crec.job_id, MGMT_GLOBAL.INVALID_JOB_ERR, 'Exception while deleting job:' || SQLERRM || CHR(10) || DBMS_UTILITY.format_error_stack, false); END; END LOOP; END IF; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('TGT_DEL: ending target:'||p_target_guid||' check finished execs:' || CASE p_chk_finished_execs WHEN TRUE THEN 'true' ELSE 'false' END, MODULE_NAME); END IF; END; -- Handle the deletion of a target PROCEDURE handle_target_pre_delete(p_target_name VARCHAR2, p_target_type VARCHAR2, p_target_guid RAW) IS BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('TGT_DEL_PRE: enter target=' || p_target_name || ':' || p_target_type, MODULE_NAME); END IF; mark_jobs_on_del_target(p_target_guid, p_target_type, false); IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('TGT_DEL_PRE: exit target=' || p_target_name || ':' || p_target_type, MODULE_NAME); END IF; END; PROCEDURE handle_target_post_delete(p_target_name VARCHAR2, p_target_type VARCHAR2, p_target_guid RAW) IS CURSOR c_del_execs(p_target_guid RAW) IS SELECT e.execution_id, j.is_corrective_action FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE e.job_id = j.job_id AND e.status = DELETE_PENDING_STATUS AND e.deleted_target_guid = p_target_guid AND j.job_status != JOB_STATUS_DELETE_PENDING ORDER BY e.expected_start_time; l_job_ids MGMT_JOB_GUID_ARRAY; l_dummy NUMBER; l_failure_count NUMBER := 0; l_total_count NUMBER := 0; l_batch_size NUMBER := 500; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('TGT_DEL_POST: handle_target_post_delete enter' , MODULE_NAME); END IF; --call pre-delete again to cover jobs submitted after pre-delete was --run the first time. mark_jobs_on_del_target(p_target_guid, p_target_type, true); --select all executions whose all targets have been marked NO_GUID -- i.e. that don't have any valid targets any more --make sure that targetless executions are not selected --mark these executions delete pending and store the -- deleted_target_guid in the MGMT_JOB_EXEC_SUMMARY table. --this can be done in post-delete since VPD will ensure that these --executions are not visible after pre-delete. IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL_POST: marking execs with only NO_GUID targets as delete pending' , MODULE_NAME); END IF; UPDATE MGMT_JOB_EXEC_SUMMARY e SET e.status = DELETE_PENDING_STATUS, e.deleted_target_guid = p_target_guid WHERE NOT EXISTS (SELECT ex.execution_id FROM MGMT_JOB_EXT_TARGETS ex WHERE ex.execution_id = e.execution_id AND ex.target_guid != NO_GUID AND ROWNUM = 1) AND EXISTS (SELECT ex.execution_id FROM MGMT_JOB_EXT_TARGETS ex WHERE ex.execution_id = e.execution_id AND ex.target_guid = NO_GUID AND ROWNUM = 1) AND e.status != DELETE_PENDING_STATUS; COMMIT; --Delete all jobs that are marked delete pending -- we cannot use delete_job_complete since the commit/rollback semantics -- there are slightly different -- select all jobs that are marked as delete pending. -- in pre-delete, we could have marked only active jobs for deletion, -- so exclude CAs and library jobs -- order by job_id to avoid deadlock. SELECT job_id BULK COLLECT INTO l_job_ids FROM MGMT_JOB WHERE job_status=JOB_STATUS_DELETE_PENDING AND is_corrective_action = 0 AND is_library = 0 AND nested=0 ORDER BY job_id; IF l_job_ids.COUNT > 0 THEN FOR i in 1..l_job_ids.COUNT LOOP BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL_POST: deleting job id ' || l_job_ids(i), MODULE_NAME); END IF; l_dummy := lock_job(l_job_ids(i)); do_delete_job(l_job_ids(i)); EXCEPTION WHEN OTHERS THEN log_error(l_job_ids(i), MGMT_GLOBAL.INVALID_JOB_ERR, 'Exception when deleting the job:' || SQLERRM, false); l_failure_count := l_failure_count + 1; END; END LOOP; COMMIT; END IF; -- select all executions that are marked as delete pending. -- exclude executions for which corresponding jobs have been marked for deletion -- order by execution_id to avoid deadlock. --there could be be a very large number of executions. So delete in batches. OPEN c_del_execs(p_target_guid); DECLARE l_execution_ids MGMT_JOB_GUID_ARRAY; l_is_ca_flags SMP_EMD_INTEGER_ARRAY; l_delete_job BOOLEAN; BEGIN LOOP FETCH c_del_execs BULK COLLECT INTO l_execution_ids, l_is_ca_flags LIMIT l_batch_size; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL_POST: processing ' || l_execution_ids.COUNT || ' executions in this iteration', MODULE_NAME); END IF; EXIT WHEN l_execution_ids.COUNT=0; FOR j IN 1..l_execution_ids.COUNT LOOP BEGIN --If this is a CA execution, do not delete job -- check_security -> false, delete_job == is not CA l_delete_job := (l_is_ca_flags(j) = 0); delete_job_execution(l_execution_ids(j), false, l_delete_job); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL_POST: deleted execution id ' || l_execution_ids(j), MODULE_NAME); END IF; EXCEPTION WHEN OTHERS THEN log_error(l_execution_ids(j), MGMT_GLOBAL.INVALID_EXECUTION_ERR, 'Failed to delete execution:' || SQLERRM, true); l_failure_count := l_failure_count + 1; END; END LOOP; --commit after every batch COMMIT; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL_POST: successfully deleted one batch', MODULE_NAME); END IF; END LOOP; END; CLOSE c_del_execs; --remove target CAs. remove_orphaned_cas(p_target_guid); COMMIT; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('TGT_DEL_POST: Doing bulk update in M_J_T, M_J_E_T and M_J_F_T', MODULE_NAME); END IF; --remove target entries from target tables DELETE FROM MGMT_JOB_TARGET jt WHERE jt.target_guid = p_target_guid AND jt.execution_id = NO_EXECUTION; DELETE FROM MGMT_JOB_EXT_TARGETS ex WHERE ex.target_guid = p_target_guid AND ex.execution_id = NO_EXECUTION; --remove entries from mgmt_job_flat_targets DELETE FROM MGMT_JOB_FLAT_TARGETS ft WHERE ft.target_guid = p_target_guid; COMMIT; --if deletion of executions, submission target entries or corrective actions failed for any reason, raise an exception IF l_failure_count > 0 THEN raise_application_error(MGMT_GLOBAL.JOB_TARGET_POST_DELETE_ERR, 'Got ' || l_failure_count || ' exceptions while deleting executions/submission target entries.'); END IF; IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('TGT_DEL_POST: handle_target_post_delete exit' , MODULE_NAME); END IF; END; -- Callback when an oms death is detected PROCEDURE handle_dispatcher_death(p_dispatcher_id INTEGER, last_timestamp DATE) IS BEGIN UPDATE MGMT_JOB_EXECUTION SET step_status=SCHEDULED_STATUS, dispatcher_id=-1 WHERE (dispatcher_id=p_dispatcher_id OR dispatcher_id NOT IN (SELECT failover_id FROM mgmt_failover_table) ) AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC, STEPTYPE_BOOKKEEPING) AND dispatcher_id != -1 AND step_status=EXECUTING_STATUS; END; -- Callback when an emd bounces; this is called when the emd -- "startup" notification is received by the job receiver PROCEDURE handle_startup_notification(p_emd_url VARCHAR2) IS l_current_time DATE := SYSDATE_UTC(); BEGIN INSERT INTO MGMT_JOB_EMD_STATUS_QUEUE(emd_url, event_type, occur_time) VALUES (p_emd_url, EVENT_EMD_BOUNCED, l_current_time); END; PROCEDURE handle_emd_state_change(p_emd_url VARCHAR2, p_state INTEGER) IS l_event INTEGER; l_current_time DATE := SYSDATE_UTC(); BEGIN IF p_state=1 THEN -- UP l_event := EVENT_EMD_UP; ELSE l_event := EVENT_EMD_DOWN; END IF; INSERT INTO MGMT_JOB_EMD_STATUS_QUEUE(emd_url, event_type, occur_time) VALUES (p_emd_url, l_event, l_current_time); END; -- A procedure that processes entries in the job system emd event -- queue (MGMT_JOB_EMD_STATUS_QUEUE). This is usually called from -- a periodically running dbms_job PROCEDURE process_emd_queue_entries IS BEGIN schedule_bookkeeping_step; END; -- fix 7714073 -- oms thread will pick up bookkeeping step and this procedure will be executed -- by the oms thread(always has emkey). PROCEDURE bookkeeping_task IS l_current_time DATE := SYSDATE_UTC(); -- Some new entries may come in after l_current_time; handle those next time CURSOR C IS SELECT emd_url, event_type, occur_time FROM MGMT_JOB_EMD_STATUS_QUEUE WHERE occur_time < l_current_time -- NOT "<=" !!! ORDER BY occur_time, emd_url, event_type ; -- todo: add this in 11gc -- FOR UPDATE NOWAIT; -- protect against concurrent calls -- This is not gaurenteed to be a unique order... -- In theory, a DOWN and UP could come in w/ the same DATE -- In practice, that can't happen because of how ping jobs work -- OK because we process DOWN, then UP: -- UP will *not* resume unless the ping table shows "all UP" -- but DOWN will suspend no matter what. -- By doing UP last, we always get the right result, -- even if the UP really came first. -- todo: add this in 11gc -- second_caller_nowait EXCEPTION; --PRAGMA EXCEPTION_INIT(second_caller_nowait, -54); -- the second caller will get ORA-54 due to the NOWAIT -- this ORA # is per Werner -- todo verify if this is the correct number and check -- if there is a predefined name for it -- NOTE Didn't find a predefined name at -- http://st doc site/10/102/appdev.102/b14261/errors.htm#i9355 l_params SMP_EMD_NVPAIR_ARRAY; l_execution_ids MGMT_JOB_GUID_ARRAY; l_job_id MGMT_JOB.job_id%TYPE; l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN -- First, process failover. We should really do this in a separate -- job, but we'll piggyback here. Commit to ensure that failover -- is always processed... BEGIN MGMT_FAILOVER.check_failure; COMMIT; EXCEPTION WHEN OTHERS THEN MGMT_LOG.log_error(MODULE_NAME, 1, 'Error calling OMS failover code: ' || SQLERRM); COMMIT; END; -- Abort all steps whose execution latency has expired FOR ABORTCR IN ( SELECT /*+ RULE */ step_id FROM MGMT_JOB_EXECUTION e, MGMT_JOB j WHERE e.job_id=j.job_id AND step_status IN (SCHEDULED_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_EVENT_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS) AND step_type=STEPTYPE_STEP AND j.execution_timeout > 0 AND (l_current_time-e.start_time) > (j.execution_timeout/24)) LOOP update_step_status(ABORTCR.step_id, ABORTED_STATUS, AGENT_DOWN_ERROR); END LOOP; -- Resume all executions whose suspend_timeout has expired FOR RESUMECR IN ( SELECT /*+ RULE */ job_id, execution_id, status, suspend_timeout FROM MGMT_JOB_EXEC_SUMMARY WHERE status IN (SUSPENDED_EVENT_STATUS) AND suspend_timeout > 0 AND (l_current_time-suspend_time) > (suspend_timeout/(24*60))) LOOP BEGIN l_start_grace_period := get_start_grace_period(RESUMECR.job_id); resume_job_execution(RESUMECR.execution_id, RESUMECR.status, l_start_grace_period, RESUMECR.suspend_timeout); EXCEPTION WHEN OTHERS THEN log_error(RESUMECR.execution_id, SQLCODE, 'process_emd_queue_entries(): could not ' || 'suspend execution ' || SQLERRM); END; END LOOP; -- Process emd entries BEGIN FOR crec in C LOOP -- todo: for 11gc -- DELETE FROM MGMT_JOB_EMD_STATUS_QUEUE WHERE CURRENT OF C; DELETE FROM MGMT_JOB_EMD_STATUS_QUEUE WHERE emd_url = crec.emd_url AND event_type = crec.event_type AND occur_time = crec.occur_time; EMDW_LOG.error('process_emd_queue_entries(): url ' || crec.emd_url || ' Event ' || crec.event_type, MODULE_NAME); -- If there is an entry in the table, it means one of three things: -- an emd was detected by the ping system as UP/DOWN, or an emd -- bounced, and the ping system may or may not have detected it. -- In either case, we have to abort all remote ops dispatched -- to that emd. Note that we only consider steps that were -- dispatched later than the occur time. Also, note that the emd_url -- is set only for async remote ops. -- Use 'like' to handle trailing slashes... IF crec.event_type=EVENT_EMD_BOUNCED THEN -- Raise the "agentBounced" event l_params := SMP_EMD_NVPAIR_ARRAY(); l_params.extend(1); l_params(1) := SMP_EMD_NVPAIR(AGENTBOUNCED_EMDURL, crec.emd_url); raise_event(AGENTBOUNCED_EVENT, l_params); FOR C2 IN (SELECT /*+ RULE */ step_id FROM MGMT_JOB_EXECUTION WHERE step_status=EXECUTING_STATUS AND start_time < crec.occur_time AND emd_url=crec.emd_url ORDER BY execution_id, step_id) LOOP write_step_error_message(C2.step_id, 'Step aborted because agent went down'); -- Note: we cannot simply do an update here since the -- status has to propogate upwards update_step_status(C2.step_id, ABORTED_STATUS, AGENT_DOWN_ERROR); -- Commit so that we don't have to redo all the work next time COMMIT; -- todo: remove the commit in 11.1 END LOOP; ELSIF crec.event_type=EVENT_EMD_DOWN THEN -- Suspend all executions that this target is -- part of SELECT /*+ RULE */ execution_id BULK COLLECT INTO l_execution_ids FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j, MGMT_JOB_TYPE_INFO i WHERE status IN (SCHEDULED_STATUS, EXECUTING_STATUS) AND e.job_id=j.job_id AND i.job_type_id=e.job_type_id AND i.agent_bound=1 AND j.system_job != SYSTEM_JOB_SESSION AND EXISTS (SELECT 1 FROM MGMT_JOB_EXT_TARGETS jt, MGMT_TARGETS t WHERE e.job_id=jt.job_id AND e.execution_id=jt.execution_id AND e.target_list_index=jt.target_list_index AND jt.target_guid=t.target_guid AND t.emd_url=crec.emd_url) ORDER BY execution_id; -- Suspend each execution IF l_execution_ids IS NOT NULL AND l_execution_ids.COUNT > 0 THEN FOR i IN 1..l_execution_ids.COUNT LOOP BEGIN suspend_job_execution(l_execution_ids(i), AGENTDOWN_STATUS, 0); EXCEPTION WHEN OTHERS THEN log_error(l_execution_ids(i), SQLCODE, 'process_emd_queue_entries(): could not ' || 'suspend execution ' || SQLERRM); END; END LOOP; END IF; ELSIF crec.event_type=EVENT_EMD_UP THEN -- We have detected that an agent is reacheable again. -- Wake up all suspended jobs that have no targets -- currently marked unreacheable -- NOTE: this actually resumes all executions that can be resumed, -- so we only need to call it once -- Subsequent calls are NOT harmful, but do waste processing power -- todo: in 11g, move this out of the loop and just check if there are any -- UP events, if there are, delete them all and do the resume below SELECT /*+ RULE */ execution_id BULK COLLECT INTO l_execution_ids FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=AGENTDOWN_STATUS AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_EXT_TARGETS jt, MGMT_TARGETS t, MGMT_TARGETS agents, MGMT_EMD_PING p WHERE e.job_id=jt.job_id AND e.execution_id=jt.execution_id AND e.target_list_index=jt.target_list_index AND jt.target_guid=t.target_guid AND t.emd_url=agents.emd_url AND agents.target_type=MGMT_GLOBAL.G_AGENT_TARGET_TYPE AND agents.target_guid=p.target_guid AND p.status = EM_PING.NODE_STATUS_DOWN ) ORDER BY execution_id; -- Resume each execution IF l_execution_ids IS NOT NULL AND l_execution_ids.COUNT > 0 THEN FOR i IN 1..l_execution_ids.COUNT LOOP BEGIN SELECT job_id INTO l_job_id FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=l_execution_ids(i); l_start_grace_period := get_start_grace_period(l_job_id); resume_job_execution(l_execution_ids(i), AGENTDOWN_STATUS, l_start_grace_period); EXCEPTION WHEN OTHERS THEN log_error(l_execution_ids(i), SQLCODE, 'process_emd_queue_entries(): could not ' || 'resume execution ' || SQLERRM); END; END LOOP; END IF; END IF; COMMIT; -- commit for each row/event END LOOP; -- todo: add this in 11g -- EXCEPTION WHEN second_caller_nowait -- not TIMEOUT_ON_RESOURCE -- THEN -- Verified this works manually by adding a sleep inside the loop -- and removing this EXCEPTION block - second caller gets the exception NULL; -- Handle second caller's error due to NOWAIT in C above END; -- pre 6151563 code that causes the bug by deleting unprocessed entries -- DELETE FROM MGMT_JOB_EMD_STATUS_QUEUE; -- COMMIT; END; -- Register a new event PROCEDURE add_event(p_event_name VARCHAR2, p_event_callback VARCHAR2) IS BEGIN INSERT INTO MGMT_JOB_EVENT(event_name, event_callback) VALUES (p_event_name, p_event_callback); END; -- Raise an already registered event with the specified params PROCEDURE raise_event(p_event_name VARCHAR2, p_event_params SMP_EMD_NVPAIR_ARRAY) IS l_event_callback MGMT_JOB_EVENT.event_callback%TYPE; l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; l_total_keys INTEGER := 0; N INTEGER := 0; BEGIN BEGIN SELECT event_callback INTO l_event_callback FROM MGMT_JOB_EVENT WHERE event_name=p_event_name; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE MGMT_GLOBAL.invalid_event; END; -- If the callback has been defined, then call it. The -- callback has a chance to raise an exception, invalidating -- the event IF l_event_callback IS NOT NULL THEN EXECUTE IMMEDIATE 'BEGIN ' || EM_CHECK.QUALIFIED_SQL_NAME(l_event_callback) || '(:1, :2); END;' USING p_event_name, p_event_params; END IF; IF p_event_params IS NOT NULL THEN N := p_event_params.COUNT; END IF; -- Resume all executions that are suspended on the specified -- event and parameters FOR crec IN ( SELECT /*+ RULE */ job_id, execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_EVENT_STATUS AND suspend_event=p_event_name AND N = (SELECT COUNT(*) FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=e.execution_id AND (param_name, param_value) IN (SELECT name, value FROM TABLE(CAST(p_event_params AS SMP_EMD_NVPAIR_ARRAY))))) LOOP SELECT COUNT(*) INTO l_total_keys FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=crec.execution_id; IF N < l_total_keys THEN -- still more events on this exec -- delete the ones that match and keep going DELETE FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=crec.execution_id AND (param_name, param_value) IN (SELECT name, value FROM TABLE(CAST(p_event_params AS SMP_EMD_NVPAIR_ARRAY))); ELSE -- last event on this exec l_start_grace_period := get_start_grace_period(crec.job_id); resume_job_execution(crec.execution_id, SUSPENDED_EVENT_STATUS, l_start_grace_period); END IF; END LOOP; END; -- Suspend the job execution (excluding the current step) on -- the specified event, having the specified parameters PROCEDURE suspend_job_execution_on_event(p_execution_id RAW, p_event_name VARCHAR2, p_event_params SMP_EMD_NVPAIR_ARRAY, p_suspend_timeout NUMBER DEFAULT 0) IS BEGIN associate_event_with_execution(p_execution_id, p_event_name, p_event_params); suspend_job_execution(p_execution_id, SUSPENDED_EVENT_STATUS, p_suspend_timeout); END; PROCEDURE suspend_job_exec_on_event_auto(p_execution_id RAW, p_event_name VARCHAR2, p_event_params SMP_EMD_NVPAIR_ARRAY, p_suspend_timeout NUMBER DEFAULT 0) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN suspend_job_execution_on_event(p_execution_id, p_event_name, p_event_params, p_suspend_timeout); COMMIT; END; -- "Associate" an event with the specified execution, without -- actually suspending it. This generally indicates an intention -- to eventually suspend the execution based on that event. PROCEDURE associate_event_with_execution(p_execution_id RAW, p_event_name VARCHAR2, p_event_params SMP_EMD_NVPAIR_ARRAY) IS l_event_callback MGMT_JOB_EVENT.event_callback%TYPE; BEGIN BEGIN SELECT event_callback INTO l_event_callback FROM MGMT_JOB_EVENT WHERE event_name=p_event_name; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE MGMT_GLOBAL.invalid_event; END; -- do not delete all for exec; there might be some from a different step -- DELETE FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=p_execution_id; FOR i IN 1..p_event_params.COUNT LOOP BEGIN INSERT INTO MGMT_JOB_EXEC_EVENT_PARAMS(execution_id, param_name, param_value) VALUES (p_execution_id, p_event_params(i).name, p_event_params(i).value); EXCEPTION -- OK if (exec, key, value) is already there -- Relies on pk def: PRIMARY KEY(execution_id, param_name, param_value) WHEN DUP_VAL_ON_INDEX THEN -- ? assert that suspend_event=p_event_name -- WHERE execution_id=p_execution_id; NULL; END; END LOOP; UPDATE MGMT_JOB_EXEC_SUMMARY SET suspend_event=p_event_name WHERE execution_id=p_execution_id; END; -- The callback for the 'bounceAgent' event. This checks that the -- parameters passed are OK. It also rejects the event if there -- are currently jobs executing that have registered interest in -- this event PROCEDURE bounce_agent_callback(p_event_name VARCHAR2, p_event_params SMP_EMD_NVPAIR_ARRAY) IS l_emd_url VARCHAR2(1024); l_count NUMBER; BEGIN IF p_event_params.count != 1 THEN raise_application_error(MGMT_GLOBAL.INVALID_EVENT_ERR, 'Incorrect number of parameters in bounceAgent event'); END IF; -- There must be exactly one parameter, called emd_url IF p_event_params(1).name != AGENTBOUNCED_EMDURL THEN raise_application_error(MGMT_GLOBAL.INVALID_EVENT_ERR, 'Invalid parameter name ' || p_event_params(1).name); END IF; l_emd_url := p_event_params(1).value; -- Check to see if there are any executions that have -- expressed interest in the bounce of this emd. If so, -- we will have to reject the event... SELECT count(*) INTO l_count FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=EXECUTING_STATUS AND suspend_event= AGENTBOUNCED_EVENT AND EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_EVENT_PARAMS WHERE execution_id=e.execution_id AND param_name= AGENTBOUNCED_EMDURL AND param_value=l_emd_url); -- If there are executing jobs waiting for the event, then -- reject the event (and hence, the startup notification). IF l_count > 0 THEN raise_application_error(MGMT_GLOBAL.INVALID_EVENT_ERR, 'There are executing jobs interested in the start notif event'); END IF; END; -- Resume all executions that were hitherto blacked out, but -- no longer need to be (ie, no blackout exists that affects -- these executions) PROCEDURE resume_blacked_out_executions IS l_start_grace_period MGMT_JOB_SCHEDULE.START_GRACE_PERIOD%TYPE; BEGIN FOR crec IN ( SELECT j.job_id, e.execution_id FROM MGMT_JOB j, MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_BLACKOUT_STATUS AND j.job_id=e.job_id AND NOT EXISTS (SELECT bw.blackout_guid FROM MGMT_BLACKOUT_WINDOWS bw, MGMT_JOB_EXT_TARGETS ex, MGMT_BLACKOUTS b WHERE ex.job_id=e.job_id AND ex.execution_id=e.execution_id AND ex.target_list_index=e.target_list_index AND bw.target_guid=ex.target_guid AND bw.blackout_guid=b.blackout_guid AND e.start_time >= bw.utc_start_time AND (bw.utc_end_time IS NULL OR e.start_time <= bw.utc_end_time) AND bw.status NOT IN (MGMT_BLACKOUT_ENGINE.BLK_STATE_ENDED, MGMT_BLACKOUT_ENGINE.BLK_STATE_STOPPED) AND b.job_flag=0) ORDER BY e.execution_id) LOOP l_start_grace_period := get_start_grace_period(crec.job_id); resume_job_execution(crec.execution_id, SUSPENDED_BLACKOUT_STATUS, l_start_grace_period); END LOOP; END; -- A new blackout window was registered, or possibly modified -- Note that start time and end time are always in repository time PROCEDURE blackout_window_started(p_blackout_guid RAW, p_job_flag NUMBER, p_start_time DATE, p_end_time DATE) IS l_current_user MGMT_CREATED_USERS.user_name%TYPE := MGMT_USER.get_current_em_user; BEGIN -- Run this routine as superuser SETEMUSERCONTEXT(MGMT_USER.GET_REPOSITORY_OWNER, MGMT_USER.OP_SET_IDENTIFIER); -- Account for targets that may have been removed resume_blacked_out_executions; IF p_job_flag=0 THEN -- Suspend jobs currently in scheduled status whose -- extended target list contains any of the targets -- currently in the blackout, which is scheduled in -- the window of the blackout FOR crec IN ( SELECT job_name, execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE e.job_id=j.job_id AND e.status=SCHEDULED_STATUS AND j.system_job=0 AND e.start_time >= p_start_time AND (p_end_time IS NULL OR e.start_time <= p_end_time) AND EXISTS (SELECT ex.target_guid FROM MGMT_JOB_EXT_TARGETS ex, MGMT_BLACKOUT_FLAT_TARGETS ft WHERE e.job_id=ex.job_id AND e.execution_id=ex.execution_id AND e.target_list_index=ex.target_list_index AND ex.target_guid=ft.target_guid AND ft.blackout_guid=p_blackout_guid) ORDER BY execution_id ) LOOP BEGIN suspend_job_execution(crec.execution_id, SUSPENDED_BLACKOUT_STATUS, 0); EXCEPTION WHEN NO_DATA_FOUND THEN -- This could happen if the execution has now -- subsequently completed and removed (eg., -- from a system job log_error(crec.execution_id, 0, 'Could not suspend execution for job ' || crec.job_name); END; END LOOP; END IF; SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); EXCEPTION WHEN OTHERS THEN IF l_current_user IS NOT NULL THEN SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); END IF; RAISE; END; -- A blackout ended, and had the job flag on PROCEDURE blackout_window_ended(p_blackout_guid RAW) IS l_current_user MGMT_CREATED_USERS.user_name%TYPE := MGMT_USER.get_current_em_user; BEGIN -- Run this routine as superuser SETEMUSERCONTEXT(MGMT_USER.GET_REPOSITORY_OWNER, MGMT_USER.OP_SET_IDENTIFIER); resume_blacked_out_executions; SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); EXCEPTION WHEN OTHERS THEN IF l_current_user IS NOT NULL THEN SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); END IF; RAISE; END; -- Validate the job type PROCEDURE validate_job_type(p_job_type VARCHAR2, p_commands SMP_EMD_STRING_ARRAY, p_nested_job_types SMP_EMD_STRING_ARRAY) IS l_dummy NUMBER; l_invalid_commands SMP_EMD_STRING_ARRAY := SMP_EMD_STRING_ARRAY(); l_invalid_jobTypes SMP_EMD_STRING_ARRAY := SMP_EMD_STRING_ARRAY(); l_command_errors BOOLEAN := false; l_nested_jt_errors BOOLEAN := false; l_command_count NUMBER := 0; l_jobtype_count NUMBER := 0; l_err_msg VARCHAR2(30000); BEGIN FOR i IN 1..p_commands.COUNT LOOP BEGIN SELECT 1 INTO l_dummy FROM MGMT_JOB_COMMAND WHERE command_name=p_commands(i); EXCEPTION WHEN NO_DATA_FOUND THEN l_command_errors := true; l_invalid_commands.extend(1); l_command_count := l_command_count+1; l_invalid_commands(l_command_count) := p_commands(i); END; END LOOP; FOR i IN 1..p_nested_job_types.COUNT LOOP BEGIN SELECT 1 INTO l_dummy FROM MGMT_JOB_TYPE_INFO WHERE job_type=p_nested_job_types(i) AND ROWNUM=1; EXCEPTION WHEN NO_DATA_FOUND THEN l_nested_jt_errors := true; l_invalid_jobTypes.extend(1); l_jobtype_count := l_jobtype_count+1; l_invalid_jobTypes(l_jobtype_count) := p_nested_job_types(i); END; END LOOP; IF l_command_errors THEN l_err_msg := 'ERROR: The job type ' || p_job_type || ' references the following invalid command(s): '; FOR i IN 1..l_invalid_commands.COUNT LOOP l_err_msg := l_err_msg || l_invalid_commands(i) || CRLF; END LOOP; END IF; IF l_nested_jt_errors THEN IF l_command_errors THEN l_err_msg := l_err_msg || CRLF; END IF; l_err_msg := l_err_msg || 'ERROR: The job type ' || p_job_type || ' uses the following invalid job type(s): '; FOR i IN 1..l_invalid_jobTypes.COUNT LOOP l_err_msg := l_err_msg || l_invalid_jobTypes(i) || CRLF; END LOOP; END IF; IF l_command_errors OR l_nested_jt_errors THEN l_err_msg := l_err_msg || '(Note that command and jobtype names are case-sensitive)'; raise_application_error(MGMT_GLOBAL.INVALID_JOBTYPE_ERR, l_err_msg); END IF; END; -- Handle changes in the membership of a group, propogate to all -- jobs submitted against the group PROCEDURE handle_membership_change(p_group_guid RAW) IS l_job_ids MGMT_JOB_GUID_ARRAY; l_exec_ids MGMT_JOB_GUID_ARRAY; l_current_time DATE := SYSDATE_UTC(); l_dummy NUMBER; BEGIN IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('handle_membership_change for target ' || p_group_guid, MODULE_NAME); END IF; -- Find all scheduled jobs submitted against the affected targets SELECT /*+ ORDERED */ j.job_id BULK COLLECT INTO l_job_ids FROM -- Get groups that contained this group (and this group) -- These are the targets affected by the change (SELECT source_target_guid FROM MGMT_FLAT_TARGET_ASSOC WHERE assoc_target_guid = p_group_guid AND is_membership = 1 UNION SELECT p_group_guid FROM DUAL) t, MGMT_JOB_TARGET jt, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS v, MGMT_JOB_TYPE_INFO i WHERE jt.target_guid = t.source_target_guid AND jt.execution_id = NO_EXECUTION AND j.job_id = jt.job_id AND v.job_type = j.job_type AND v.major_version = j.job_type_major_version AND i.job_type_id = v.job_type_id AND i.single_target = 1 AND EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=j.job_id AND status NOT IN ( COMPLETED_STATUS, ABORTED_STATUS, STOPPED_STATUS, FAILED_STATUS, SKIPPED_STATUS, DELETE_PENDING_STATUS)) ORDER BY job_id; IF l_job_ids IS NULL OR l_job_ids.COUNT=0 THEN RETURN; END IF; -- For each job, recompute the target lists FOR i IN 1..l_job_ids.COUNT LOOP -- Lock the job first so that this does not conflict with -- an edit, for example l_dummy := lock_job(l_job_ids(i)); l_exec_ids := schedule_single_target_execs(l_job_ids(i), null, null, l_current_time); END LOOP; END; -- Handle changes to the job system when a user is deleted PROCEDURE drop_user_jobs(p_user_name IN VARCHAR2, type_in IN NUMBER) IS l_repository_owner VARCHAR2(256) := MGMT_USER.GET_REPOSITORY_OWNER; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('IN drop_user_jobs()', MODULE_NAME); END IF; -- For all jobs owned by the user, force stop all active executions -- and delete the job FOR crec IN (SELECT job_id, job_name,system_job FROM MGMT_JOB WHERE job_owner=UPPER(p_user_name) AND nested=0 AND is_corrective_action = 0 AND job_status != JOB_STATUS_DELETE_PENDING ORDER BY job_id) LOOP IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('DELETING JOB ' || crec.job_name, MODULE_NAME); END IF; -- Stop and delete non-system jobs only IF crec.system_job <= 0 THEN stop_all_executions_with_id(crec.job_id,TRUE); delete_job(p_job_id=>crec.job_id); ELSE -- reassign system job to repository user UPDATE MGMT_JOB SET job_owner = l_repository_owner WHERE job_id = crec.job_id; END IF ; COMMIT; END LOOP; END; -- --For a given job type id, determines if it requires credentials. It checks --all the nested job types too. --Updates the associative array with the information. --Returns 1 if job type requires credentials, and 0 otherwise. FUNCTION does_job_type_require_creds(p_job_type_id IN RAW, p_creds_jobtype_map IN OUT MGMT_CREDENTIAL_JOB_TYPE_MAP) RETURN NUMBER IS l_credentials_required NUMBER; l_job_type_ids MGMT_JOB_GUID_ARRAY; l_return NUMBER := 0; l_exists NUMBER; l_cred_source_count NUMBER; BEGIN l_job_type_ids := MGMT_JOB_GUID_ARRAY(); --create array of self and all nested job type ids find_job_type_ids(p_job_type_id, l_job_type_ids); --Determine if the parent or any of the children require credentials. --Update associative array along the way. --Do not break out of the loop since we have done the expensive work --of determining nested job types. So ensure that we update the associative --array with all available job types. FOR i in 1..l_job_type_ids.COUNT LOOP l_exists := 0; l_credentials_required := 0; BEGIN l_credentials_required := p_creds_jobtype_map(RAWTOHEX(l_job_type_ids(i))); l_exists := 1; EXCEPTION WHEN NO_DATA_FOUND THEN --entry does not exist l_exists := 0; END; IF l_exists = 0 THEN --check if job type requires credentials SELECT count(*) INTO l_cred_source_count FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=l_job_type_ids(i) AND SOURCE_TYPE=SOURCE_TYPE_CRED; IF l_cred_source_count > 0 THEN l_credentials_required := 1; END IF; --update the associative array p_creds_jobtype_map(RAWTOHEX(l_job_type_ids(i))) := l_credentials_required; END IF; IF l_return = 0 AND l_credentials_required = 1 THEN l_return := 1; END IF; END LOOP; --if any of the nested job types require credentials, --mark the parent also as requiring credentials IF l_return = 1 THEN p_creds_jobtype_map(RAWTOHEX(p_job_type_id)) := 1; END IF; RETURN l_return; END; --given a job type id, return array of nested job type ids --the array also includes the given job type id PROCEDURE find_job_type_ids(p_job_type_id IN RAW, p_job_type_id_array IN OUT MGMT_JOB_GUID_ARRAY) IS l_nested_job_types SMP_EMD_STRING_ARRAY; l_major_version_out MGMT_JOB_TYPE_MAX_VERSIONS.major_version%TYPE; l_minor_version1_out MGMT_JOB_TYPE_MAX_VERSIONS.minor_version1%TYPE; l_minor_version2_out MGMT_JOB_TYPE_MAX_VERSIONS.minor_version2%TYPE; l_nested_job_type_id_out MGMT_JOB_TYPE_MAX_VERSIONS.job_type_id%TYPE; BEGIN --add self first p_job_type_id_array.extend(1); p_job_type_id_array(p_job_type_id_array.COUNT) := p_job_type_id; --find all nested job types SELECT nested_job_type BULK COLLECT INTO l_nested_job_types FROM MGMT_JOB_EXECPLAN WHERE job_type_id = p_job_type_id AND nested_job_type IS NOT NULL; --for each nested job type... IF l_nested_job_types IS NOT NULL AND l_nested_job_types.COUNT > 0 THEN FOR i IN 1..l_nested_job_types.COUNT LOOP --find job type id get_max_versions(l_nested_job_types(i), l_major_version_out, l_minor_version1_out, l_minor_version2_out, l_nested_job_type_id_out); --call recursively find_job_type_ids(l_nested_job_type_id_out, p_job_type_id_array); END LOOP; END IF; END; --common routine for jobs and CAs to reassign user jobs -- --INPUT PARAMETERS -- p_user_name old username -- p_new_user_name new username -- type_in type -- isCA 0 if we are in context of jobs, 1 if we are in context of CAs -- -- RETURN count of jobs/cas that were not reassigned FUNCTION process_reassign_user_jobs(p_user_name IN VARCHAR2, p_new_user_name IN VARCHAR2, type_in IN NUMBER, isCA IN NUMBER) RETURN NUMBER IS l_execution_ids MGMT_JOB_GUID_ARRAY; l_current_user VARCHAR2(256) := MGMT_USER.GET_CURRENT_EM_USER; l_has_priv NUMBER := MGMT_USER.USER_HAS_PRIV; l_user_name MGMT_CREATED_USERS.user_name%TYPE := UPPER(p_user_name); l_new_user_name MGMT_CREATED_USERS.user_name%TYPE := UPPER(p_new_user_name); l_cred_source_count NUMBER; l_dummy NUMBER; l_ca_scope NUMBER; l_failure_count NUMBER := 0; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_credential_jobtype_map MGMT_CREDENTIAL_JOB_TYPE_MAP; l_exists NUMBER; l_credentials_required NUMBER; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:IN process_reassign_user_jobs()', MODULE_NAME); END IF; -- mark all stopped jobs as non restartable UPDATE MGMT_JOB SET restartable = 0 WHERE job_owner = l_user_name AND job_status = JOB_STATUS_STOPPED AND nested = 0 AND is_corrective_action = isCA; COMMIT; --NOTE: We are committing above since we know that this procedure is called -- from a job. This procedure will be retried till it succeeds. -- 1. move all active jobs to JOB_STATUS_REASSIGN -- exclude jobs that do not require credentials -- 2. Delete all overridden credentials -- 3. Change owner -- 4. Suspend executions, if appropriate FOR crec IN (SELECT job_id, job_name, nested, is_library, job_type, job_type_major_version -- remove job_name after testing FROM MGMT_JOB WHERE job_owner=l_user_name AND is_corrective_action = isCA AND nested = 0 AND job_status != JOB_STATUS_DELETE_PENDING ORDER BY job_id) LOOP IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:PROCESSING JOB ' || crec.job_name, MODULE_NAME); END IF; l_credentials_required := 0; --lock the job l_dummy := lock_job(crec.job_id); --if this is a multi-task job, delete overridden credentials from --job type definition l_job_type_id := get_job_type_id(crec.job_id); SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_job_type_id; IF l_job_type_category = JOBTYPE_CATEGORY_HIDDEN THEN DELETE FROM MGMT_NESTED_JOB_CRED_INFO WHERE job_type_id=l_job_type_id; --update job type owner UPDATE MGMT_JOB_TYPE_INFO SET job_type_owner=l_new_user_name WHERE job_type_id=l_job_type_id; END IF; -- --determine if the job type requires credentials -- l_exists := 0; BEGIN l_credentials_required := l_credential_jobtype_map(RAWTOHEX(l_job_type_id)); l_exists := 1; EXCEPTION WHEN NO_DATA_FOUND THEN l_exists := 0; END; IF l_exists = 0 THEN l_credentials_required := does_job_type_require_creds(l_job_type_id, l_credential_jobtype_map); END IF; BEGIN --process all nested jobs FOR crecNested IN (SELECT job_id, nested_job_type_id, job_name FROM MGMT_JOB WHERE is_corrective_action = isCA START WITH parent_job_id=crec.job_id CONNECT BY prior job_id=parent_job_id) LOOP IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:PROCESSING NESTED JOB ' || crecNested.job_name, MODULE_NAME); END IF; -- Delete all overridden credentials for the job MGMT_CREDENTIAL.delete_job_credentials(crecNested.job_id); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:DELETED CREDENTIALS FOR NESTED JOB ' || crecNested.job_name, MODULE_NAME); END IF; -- update owner UPDATE MGMT_JOB SET job_owner = l_new_user_name WHERE job_id = crecNested.job_id; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:UPDATED OWNER FOR NESTED JOB ' || crecNested.job_name, MODULE_NAME); END IF; --not setting the job_status of nested jobs to reassigned. is it okay? END LOOP; -- end process nested jobs -- Delete all overridden credentials for the job MGMT_CREDENTIAL.delete_job_credentials(crec.job_id); IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:DELETED CREDENTIALS FOR JOB ' || crec.job_name, MODULE_NAME); END IF; --In 10.2, system jobs will be covered by this check since they do not --have credentials --Do not set status to reassign for library jobs IF l_credentials_required = 1 AND crec.is_library = 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:REASSIGNING JOB ' || crec.job_name, MODULE_NAME); END IF; -- all active executions are set to reassigned -- presently EXECUTING executions are left untouched SELECT execution_id BULK COLLECT INTO l_execution_ids FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=crec.job_id AND status NOT IN (EXECUTING_STATUS, ABORTED_STATUS, FAILED_STATUS, COMPLETED_STATUS, STOPPED_STATUS, WAITING_STATUS, STOP_PENDING_STATUS, INACTIVE_STATUS, QUEUED_STATUS, FAILED_RETRIED_STATUS, SKIPPED_STATUS, DELETE_PENDING_STATUS); -- any more statuses? IF l_execution_ids IS NOT NULL AND l_execution_ids.COUNT > 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:SUSPENDING EXECUTIONS FOR JOB ' || crec.job_name, MODULE_NAME); END IF; FOR i IN 1..l_execution_ids.COUNT LOOP suspend_job_execution(l_execution_ids(i), REASSIGNED_STATUS, 0); END LOOP; -- move the job to REASSIGNED status since it has active executions IF isCA = 0 THEN UPDATE MGMT_JOB SET job_status = JOB_STATUS_REASSIGNED WHERE job_id = crec.job_id; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:SET JOB STATUS TO REASSIGN FOR JOB ' || crec.job_name, MODULE_NAME); END IF; END IF; END IF; IF isCA = 1 THEN -- mark broken only if it is a target CA SELECT ca_scope INTO l_ca_scope FROM MGMT_CORRECTIVE_ACTION WHERE job_id=crec.job_id; IF l_ca_scope = CA_SCOPE_TARGET THEN -- move the CA to REASSIGNED status -- mark it as broken. UPDATE MGMT_JOB SET job_status = JOB_STATUS_REASSIGNED, broken = 1, broken_reason = BROKEN_USER_REASSIGN WHERE job_id = crec.job_id; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:SET JOB STATUS TO REASSIGN FOR JOB ' || crec.job_name, MODULE_NAME); END IF; END IF; END IF; END IF; IF isCA = 0 THEN -- temporarily make the current user as the job owner -- NOTE : only job owner can give FULL_JOB to a new user UPDATE MGMT_JOB SET job_owner = l_current_user WHERE job_id = crec.job_id; -- assign full job to the new user MGMT_USER.GRANT_PRIV(l_new_user_name,MGMT_USER.FULL_JOB,crec.job_id); END IF; -- reassign job to new user UPDATE MGMT_JOB SET job_owner = l_new_user_name WHERE job_id = crec.job_id; IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:CHANGED OWNER FOR JOB ' || crec.job_name, MODULE_NAME); END IF; COMMIT; EXCEPTION WHEN OTHERS THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:Exception while reassigning job ' || crec.job_name || SQLERRM, MODULE_NAME); END IF; log_error(crec.job_id, MGMT_GLOBAL.REASSIGN_JOB_ERR, 'Exception while reassigning job:' || SQLERRM, false); l_failure_count := l_failure_count + 1; --rollback changes for this job ROLLBACK; END; END LOOP; RETURN l_failure_count; END; -- Handle changes to the job system when a user is deleted PROCEDURE reassign_user_jobs(p_user_name IN VARCHAR2, p_new_user_name IN VARCHAR2, type_in IN NUMBER) IS l_failure_count NUMBER := 0; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:IN reassign_user_jobs()', MODULE_NAME); END IF; l_failure_count := process_reassign_user_jobs(p_user_name, p_new_user_name, type_in, 0); --if deletion failed for any reason, raise an exception so that the job --can retry IF l_failure_count > 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('FAILED TO REASSIGN JOBS: ' || l_failure_count, MODULE_NAME); END IF; raise_application_error(MGMT_GLOBAL.REASSIGN_JOB_ERR, 'Failed to reassign ' || l_failure_count || ' job(s).'); END IF; END; -- get_user_jobs_and_ca is user model callback -- user_name_in : the name of user which is being deleted -- user_objects_out : the list of jobs and ca owned by the user is returned -- type_in : type of user model callback PROCEDURE get_user_jobs(user_name_in IN VARCHAR2, user_objects_out OUT MGMT_USER_OBJECTS, type_in IN NUMBER) IS i INTEGER := 0; l_user_jobs MGMT_USER_OBJECTS := MGMT_USER_OBJECTS(); CURSOR c_jobs IS SELECT job_name FROM MGMT_JOB WHERE job_owner=user_name_in AND job_status != JOB_STATUS_DELETE_PENDING AND nested = 0 AND is_corrective_action = 0; BEGIN -- we want all jobs to be dropped asynchronously since the drop/reassign -- procedures commit after every job FOR c IN c_jobs LOOP l_user_jobs.extend(1); i := i + 1; l_user_jobs(i) := MGMT_USER_OBJECT(MGMT_USER.USER_OBJECT_JOB, c.job_name, null, MGMT_USER.ASYNC_DROP_OBJECT); END LOOP; user_objects_out := l_user_jobs; END; -- Handle changes to the job system when a user is deleted PROCEDURE drop_user_ca(p_user_name IN VARCHAR2, type_in IN NUMBER) IS BEGIN -- For all jobs owned by the user, force stop all active executions -- and delete the job FOR crec IN (SELECT job_id, job_name,system_job FROM MGMT_JOB WHERE job_owner=UPPER(p_user_name) AND nested=0 AND job_status != JOB_STATUS_DELETE_PENDING AND is_corrective_action = 1 ORDER BY job_id) LOOP stop_all_executions_with_id(crec.job_id,TRUE); -- System jobs are deleted by stop_all_executions IF crec.system_job <= 0 THEN delete_ca(p_job_id=>crec.job_id); END IF ; COMMIT; END LOOP; END; --validate if the specified user has atleast VIEW access on all targets --on which job with the specified job id operates. --returns MGMT_USER.USER_DOES_NOT_HAVE_PRIV or MGMT_USER.USER_HAS_PRIV FUNCTION has_priv_on_job_targets(p_username IN VARCHAR2, p_job_id IN RAW) RETURN NUMBER IS l_current_user MGMT_CREATED_USERS.user_name%TYPE; l_all_target_guids MGMT_JOB_GUID_ARRAY; l_visible_count NUMBER; l_has_priv NUMBER := MGMT_USER.USER_HAS_PRIV; BEGIN --get list of target guids against which job is operating SELECT DISTINCT target_guid BULK COLLECT INTO l_all_target_guids FROM MGMT_JOB_EXT_TARGETS WHERE job_id=p_job_id AND execution_id = NO_EXECUTION AND target_guid != NO_GUID; IF l_all_target_guids IS NOT NULL AND l_all_target_guids.COUNT > 0 THEN BEGIN l_current_user := MGMT_USER.GET_CURRENT_EM_USER; --become the new user SETEMUSERCONTEXT(p_username, MGMT_USER.OP_SET_IDENTIFIER); --use the VPD layer to get count of visible targets SELECT COUNT(DISTINCT target_guid) INTO l_visible_count FROM MGMT_TARGETS WHERE target_guid IN (SELECT * FROM TABLE(CAST(l_all_target_guids AS MGMT_JOB_GUID_ARRAY))); --become the original user SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); EXCEPTION WHEN OTHERS THEN IF l_current_user IS NOT NULL THEN SETEMUSERCONTEXT(l_current_user, MGMT_USER.OP_SET_IDENTIFIER); END IF; RAISE; END; IF l_visible_count < l_all_target_guids.COUNT THEN l_has_priv := MGMT_USER.USER_DOES_NOT_HAVE_PRIV; END IF; END IF; RETURN l_has_priv; END; -- check if the user has OPERAOR PRIV ON the targets on which the -- the ca operates on FUNCTION has_priv_on_ca_targets(user_name_in IN VARCHAR2, ca_guid_in IN RAW) RETURN NUMBER IS CURSOR c_ca_targets IS SELECT target_guid FROM MGMT_JOB_EXT_TARGETS WHERE job_id = ca_guid_in AND target_guid != NO_GUID; has_priv NUMBER := MGMT_USER.USER_HAS_PRIV; BEGIN FOR c IN c_ca_targets LOOP has_priv := MGMT_USER.has_priv(user_name_in,MGMT_USER.OPERATOR_TARGET,c.target_guid); IF has_priv != MGMT_USER.USER_HAS_PRIV THEN RETURN MGMT_USER.USER_DOES_NOT_HAVE_PRIV; END IF; END LOOP; RETURN MGMT_USER.USER_HAS_PRIV; END; PROCEDURE check_reassign_user_jobs(p_username IN VARCHAR2, p_new_username IN VARCHAR2, p_user_objects_out OUT MGMT_USER_OBJECTS, p_type IN NUMBER) IS CURSOR c_jobs IS SELECT job_id, job_status,job_name FROM MGMT_JOB WHERE job_owner=UPPER(p_username) AND job_status != JOB_STATUS_DELETE_PENDING AND is_corrective_action = 0; l_count NUMBER; l_has_view_target NUMBER; l_bad_user_jobs MGMT_USER_OBJECTS := MGMT_USER_OBJECTS(); BEGIN FOR c IN c_jobs LOOP l_has_view_target := has_priv_on_job_targets(UPPER(p_new_username), c.job_id); IF l_has_view_target != MGMT_USER.USER_HAS_PRIV THEN l_bad_user_jobs.extend(1); l_bad_user_jobs(l_bad_user_jobs.COUNT) := MGMT_USER_OBJECT( MGMT_USER.USER_OBJECT_JOB, c.job_name, null, MGMT_USER.ASYNC_DROP_OBJECT); ELSE SELECT COUNT(job_id) INTO l_count FROM MGMT_JOB WHERE job_owner=UPPER(p_new_username) AND job_name = c.job_name AND job_status != JOB_STATUS_DELETE_PENDING AND nested = 0 AND is_corrective_action = 0; --the new user has a job with a same job name IF l_count > 0 THEN l_bad_user_jobs.extend(1); l_bad_user_jobs(l_bad_user_jobs.COUNT) := MGMT_USER_OBJECT( MGMT_USER.USER_OBJECT_JOB, c.job_name, null, MGMT_USER.ASYNC_DROP_OBJECT); END IF; END IF; END LOOP; p_user_objects_out := l_bad_user_jobs; END; -- Handle changes to the job system when a user is deleted PROCEDURE reassign_user_ca(p_user_name IN VARCHAR2, p_new_user_name IN VARCHAR2, type_in IN NUMBER) IS l_failure_count NUMBER := 0; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('REASSIGN:IN reassign_user_ca()', MODULE_NAME); END IF; l_failure_count := process_reassign_user_jobs(p_user_name, p_new_user_name, type_in, 1); --if deletion failed for any reason, raise an exception so that the job --can retry IF l_failure_count > 0 THEN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('FAILED TO REASSIGN CAs: ' || l_failure_count, MODULE_NAME); END IF; raise_application_error(MGMT_GLOBAL.REASSIGN_JOB_ERR, 'Failed to reassign ' || l_failure_count || ' ca(s).'); END IF; END; -- check if there are any ca owned by the user which can be reassigned to the -- new user PROCEDURE check_reassign_user_ca(user_name_in IN VARCHAR2, new_user_name_in IN VARCHAR2, user_objects_out OUT MGMT_USER_OBJECTS, type_in IN NUMBER) IS CURSOR c_ca IS SELECT job_id, job_status,job_name FROM MGMT_JOB WHERE job_owner=UPPER(user_name_in) AND job_status != JOB_STATUS_DELETE_PENDING AND is_corrective_action = 1 AND nested=0; has_operator_target NUMBER; i INTEGER := 0; l_bad_user_ca MGMT_USER_OBJECTS := MGMT_USER_OBJECTS(); BEGIN FOR c IN c_ca LOOP has_operator_target := has_priv_on_ca_targets(UPPER(new_user_name_in), c.job_id); IF has_operator_target != MGMT_USER.USER_HAS_PRIV THEN l_bad_user_ca.extend(1); i := i + 1; l_bad_user_ca(i) := MGMT_USER_OBJECT( MGMT_USER.USER_OBJECT_CA, c.job_name, null, MGMT_USER.ASYNC_DROP_OBJECT); END IF; END LOOP; user_objects_out := l_bad_user_ca; END; -- get_user_jobs_and_ca is user model callback -- user_name_in : the name of user which is being deleted -- user_objects_out : the list of jobs and ca owned by the user is returned -- type_in : type of user model callback PROCEDURE get_user_ca(user_name_in IN VARCHAR2, user_objects_out OUT MGMT_USER_OBJECTS, type_in IN NUMBER) IS i INTEGER := 0; l_user_ca MGMT_USER_OBJECTS := MGMT_USER_OBJECTS(); CURSOR c_ca IS SELECT job_name FROM MGMT_JOB WHERE job_owner=user_name_in AND job_status != JOB_STATUS_DELETE_PENDING AND nested = 0 AND is_corrective_action = 1; BEGIN -- we want all jobs to be dropped asynchronously since the drop/reassign -- procedures commit after every job FOR c IN c_ca LOOP l_user_ca.extend(1); i := i + 1; l_user_ca(i) := MGMT_USER_OBJECT(MGMT_USER.USER_OBJECT_CA, c.job_name, null, MGMT_USER.ASYNC_DROP_OBJECT); END LOOP; user_objects_out := l_user_ca; END; -- Procedures dealing with job queues -- Create a new queue with the specified name that is -- currently active and enabled, with a concurrency -- factor of 1 PROCEDURE create_job_queue(p_queue_name VARCHAR2) IS BEGIN INSERT INTO MGMT_JOB_QUEUES(queue_name, queue_id) VALUES (p_queue_name, SYS_GUID()); END; -- Return the queue id FUNCTION get_queue_id(p_queue_name VARCHAR2) RETURN RAW IS l_queue_id RAW(16); BEGIN SELECT queue_id INTO l_queue_id FROM MGMT_JOB_QUEUES WHERE queue_name=p_queue_name; END; FUNCTION lock_queue(p_queue_name VARCHAR2, p_nowait BOOLEAN DEFAULT false) RETURN RAW IS l_queue_id RAW(16); BEGIN IF p_nowait THEN SELECT queue_id INTO l_queue_id FROM MGMT_JOB_QUEUES WHERE queue_name=p_queue_name FOR UPDATE NOWAIT; ELSE SELECT queue_id INTO l_queue_id FROM MGMT_JOB_QUEUES WHERE queue_name=p_queue_name FOR UPDATE; END IF; RETURN l_queue_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Queue ' || p_queue_name || ' does not exist'); END; PROCEDURE lock_queue(p_queue_id RAW, p_nowait BOOLEAN DEFAULT false) IS l_dummy NUMBER; BEGIN IF p_nowait THEN SELECT 1 INTO l_dummy FROM MGMT_JOB_QUEUES WHERE queue_id=p_queue_id FOR UPDATE NOWAIT; ELSE SELECT 1 INTO l_dummy FROM MGMT_JOB_QUEUES WHERE queue_id=p_queue_id FOR UPDATE; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Specified Queue does not exist'); END; -- Enable/Disable the specified queue PROCEDURE enable_job_queue(p_queue_name VARCHAR2, p_enabled NUMBER) IS l_queue_id MGMT_JOB_QUEUES.queue_id%TYPE; BEGIN l_queue_id := lock_queue(p_queue_name); UPDATE MGMT_JOB_QUEUES SET enabled=p_enabled, active=p_enabled WHERE queue_name=p_queue_name; IF p_enabled=1 THEN schedule_executions_from_queue(l_queue_id, false); END IF; END; -- Cleanup all current executions in the job queue. If there are executions -- in the queue that are currently running and p_force_delete_execs -- is true, the executions are silently stopped and deleted. -- If there are running executions and p_force_delete is false, -- an exception is thrown. PROCEDURE cleanup_job_queue(p_queue_name VARCHAR2, p_force_delete_execs BOOLEAN DEFAULT false) IS l_queue_id MGMT_JOB_QUEUES.queue_id%TYPE; l_exec_ids MGMT_JOB_GUID_ARRAY; l_system_job_flags SMP_EMD_INTEGER_ARRAY; BEGIN l_queue_id := lock_queue(p_queue_name); SELECT execution_id, system_job BULK COLLECT INTO l_exec_ids, l_system_job_flags FROM MGMT_JOB j , MGMT_JOB_EXEC_SUMMARY e WHERE j.job_id = e.job_id AND queue_id=l_queue_id; FOR i IN 1..l_exec_ids.COUNT LOOP stop_execution(l_exec_ids(i), false, false, p_force_delete_execs); -- delete non system jobs only IF l_system_job_flags(i) = 0 THEN delete_job_execution(l_exec_ids(i)); END IF; END LOOP; END; -- Delete the specified queue and all the executions PROCEDURE delete_job_queue(p_queue_name VARCHAR2) IS l_queue_id MGMT_JOB_QUEUES.queue_id%TYPE; BEGIN l_queue_id := lock_queue(p_queue_name); cleanup_job_queue(p_queue_name, true); DELETE FROM MGMT_JOB_QUEUES WHERE queue_name=p_queue_name; END; -- Insert the specified execution into the named queue. -- It is assumed that the execution is already inserted. PROCEDURE insert_execution_into_queue(p_queue_name VARCHAR2, p_execution_id RAW) IS l_queue_id MGMT_JOB_QUEUES.queue_id%TYPE; l_max_queue_index NUMBER; l_active NUMBER; BEGIN l_queue_id := lock_queue(p_queue_name); UPDATE MGMT_JOB_QUEUES SET max_queue_index=max_queue_index+1, num_executions=num_executions+1 WHERE queue_id=l_queue_id RETURNING max_queue_index, active INTO l_max_queue_index, l_active; UPDATE MGMT_JOB_EXEC_SUMMARY SET queue_id=l_queue_id, queue_index=l_max_queue_index, status=QUEUED_STATUS WHERE execution_id=p_execution_id; -- Schedule the execution immediately if possible IF l_active=1 THEN schedule_executions_from_queue(l_queue_id, false); END IF; END; -- Schedule the next set of executions from the queue. -- Note: this currently scheduled just one execution at a time PROCEDURE schedule_executions_from_queue(p_queue_id RAW, p_nowait BOOLEAN) IS l_active MGMT_JOB_QUEUES.active%TYPE; l_num_scheduled_executions MGMT_JOB_QUEUES.num_scheduled_executions%TYPE; l_queue_index MGMT_JOB_EXEC_SUMMARY.queue_index%TYPE; l_job_id MGMT_JOB_EXEC_SUMMARY.job_id%TYPE; l_execution_id MGMT_JOB_EXEC_SUMMARY.execution_id%TYPE; l_target_list_index MGMT_JOB_EXEC_SUMMARY.target_list_index%TYPE; l_start_time MGMT_JOB_EXEC_SUMMARY.start_time%TYPE; l_step_status NUMBER; l_step_ids SMP_EMD_INTEGER_ARRAY; BEGIN lock_queue(p_queue_id); SELECT active, num_scheduled_executions INTO l_active, l_num_scheduled_executions FROM MGMT_JOB_QUEUES WHERE queue_id=p_queue_id; IF l_active=0 OR l_num_scheduled_executions > 0 THEN RETURN; END IF; SELECT MIN(queue_index) INTO l_queue_index FROM MGMT_JOB_EXEC_SUMMARY WHERE queue_id=p_queue_id AND status=QUEUED_STATUS; IF l_queue_index IS NULL THEN -- There are no waiting executions RETURN; END IF; SELECT job_id, execution_id, target_list_index, start_time INTO l_job_id, l_execution_id, l_target_list_index, l_start_time FROM MGMT_JOB_EXEC_SUMMARY WHERE queue_id=p_queue_id AND status=QUEUED_STATUS AND queue_index=l_queue_index; l_step_status := get_execution_status(l_job_id, l_execution_id, null, l_target_list_index, l_start_time, SCHEDULED_STATUS, QUEUED_STATUS); -- Set the status of each step in the execution to SCHEDULED SELECT step_id BULK COLLECT INTO l_step_ids FROM MGMT_JOB_EXECUTION WHERE execution_id=l_execution_id AND step_type IN (STEPTYPE_STEP, STEPTYPE_PARAMSRC, STEPTYPE_PARAMSRC_RETRY, STEPTYPE_PARAMSRC_RETRY_EXEC); -- Update the status of the execution to the step status UPDATE MGMT_JOB_EXEC_SUMMARY SET status=l_step_status WHERE execution_id=l_execution_id; -- Note that there is no need to lock the execution -- since we've already locked the queue FOR i IN 1..l_step_ids.COUNT LOOP update_step_status_nolock(l_step_ids(i), l_step_status, 0); END LOOP; UPDATE MGMT_JOB_QUEUES SET num_scheduled_executions=l_num_scheduled_executions+1 WHERE queue_id=p_queue_id; END; -- An execution scheduled from the queue is done, or needs to -- be removed from the queue PROCEDURE remove_execution_from_queue(p_queue_id VARCHAR2, p_execution_id VARCHAR2) IS l_execution_status MGMT_JOB_EXEC_SUMMARY.status%TYPE; BEGIN lock_queue(p_queue_id); SELECT status INTO l_execution_status FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id=p_execution_id; IF l_execution_status=QUEUED_STATUS THEN UPDATE MGMT_JOB_QUEUES SET num_executions=num_executions-1 WHERE queue_id=p_queue_id; ELSIF l_execution_status IN (COMPLETED_STATUS, ABORTED_STATUS, FAILED_STATUS, STOPPED_STATUS) THEN UPDATE MGMT_JOB_QUEUES SET num_executions=num_executions-1, num_scheduled_executions=num_scheduled_executions-1 WHERE queue_id=p_queue_id; ELSE -- Once scheduled, the execution cannot be removed until -- it is history raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Execution is currently active, cannot be removed from queue'); END IF; -- Schedule more executions if available schedule_executions_from_queue(p_queue_id, false); END; FUNCTION get_status_2_from_status(p_status IN NUMBER) RETURN NUMBER DETERMINISTIC IS BEGIN if p_status is null then raise_application_error(-20001,'INVALID PARAMETER'); end if; case p_status -- when SCHEDULED_STATUS then RETURN SCHEDULED_STATUS_2; when QUEUED_STATUS then RETURN QUEUED_STATUS_2; -- when EXECUTING_STATUS then RETURN EXECUTING_STATUS_2; when SUSPEND_PENDING_STATUS then RETURN SUSPEND_PENDING_STATUS_2; when STOP_PENDING_STATUS then RETURN STOP_PENDING_STATUS_2; -- when SUSPENDED_STATUS then RETURN SUSPENDED_STATUS_2; when AGENTDOWN_STATUS then RETURN AGENTDOWN_STATUS_2; when SUSPENDED_BLACKOUT_STATUS then RETURN SUSPENDED_BLACKOUT_STATUS_2; when SUSPENDED_CREDS_STATUS then RETURN SUSPENDED_CREDS_STATUS_2; when SUSPENDED_LOCK_STATUS then RETURN SUSPENDED_LOCK_STATUS_2; when SUSPENDED_EVENT_STATUS then RETURN SUSPENDED_EVENT_STATUS_2; -- when COMPLETED_STATUS then RETURN COMPLETED_STATUS_2; -- when ABORTED_STATUS then RETURN ABORTED_STATUS_2; when FAILED_STATUS then RETURN FAILED_STATUS_2; when STOPPED_STATUS then RETURN STOPPED_STATUS_2; when INACTIVE_STATUS then RETURN INACTIVE_STATUS_2; when FAILED_RETRIED_STATUS then RETURN FAILED_RETRIED_STATUS_2; when SKIPPED_STATUS then RETURN SKIPPED_STATUS_2; when REASSIGNED_STATUS then RETURN REASSIGNED_STATUS_2; -- else raise_application_error(-20001,'INVALID PARAMETER'); end case; END get_status_2_from_status; -- get status_2 value range for a given status or bucket -- p_status_in can be -- an old status value -- a new status value -- a status bucket PROCEDURE get_status_2_range(p_status_in IN NUMBER, p_status_2_min_out OUT NUMBER, p_status_2_max_out OUT NUMBER) IS BEGIN if p_status_in is null then raise_application_error(-20001,'INVALID PARAMETER'); elsif p_status_in > ALL_BUCKET_START then -- status is a single value p_status_2_min_out := p_status_in; p_status_2_max_out := p_status_in; elsif p_status_in > 0 then -- must be old style status value p_status_2_min_out := get_status_2_from_status(p_status_in); p_status_2_max_out := p_status_2_min_out; else -- p_status_in <= 0 and is a bucket case p_status_in when 0 then -- special case: treat 0 as 'all' -- todo: give an error? p_status_2_min_out := ALL_BUCKET_START; p_status_2_max_out := ALL_BUCKET_END; when STATUS_BUCKET_ALL then p_status_2_min_out := ALL_BUCKET_START; p_status_2_max_out := ALL_BUCKET_END; when STATUS_BUCKET_ACTIVE then p_status_2_min_out := ACTIVE_BUCKET_START; p_status_2_max_out := ACTIVE_BUCKET_END; when STATUS_BUCKET_SCHEDULED then p_status_2_min_out := SCHEDULED_BUCKET_START; p_status_2_max_out := SCHEDULED_BUCKET_END; when STATUS_BUCKET_RUNNING then p_status_2_min_out := RUNNING_BUCKET_START; p_status_2_max_out := RUNNING_BUCKET_END; when STATUS_BUCKET_SUSPENDED then p_status_2_min_out := SUSPENDED_BUCKET_START; p_status_2_max_out := SUSPENDED_BUCKET_END; when STATUS_BUCKET_OK then p_status_2_min_out := COMPLETED_BUCKET_START; p_status_2_max_out := COMPLETED_BUCKET_END; when STATUS_BUCKET_PROBLEM then p_status_2_min_out := PROBLEM_BUCKET_START; p_status_2_max_out := PROBLEM_BUCKET_END; -- else raise_application_error(-20001,'INVALID PARAMETER'); end case; end if; END get_status_2_range; -- TODO: Convert into a utility function -- Return the instance target type and its max type meta version for the passed -- target type. If the passed target type is not a cluster type, return the -- passed values unchanged. PROCEDURE load_instance_target_type(p_target_type VARCHAR2, p_target_type_meta_ver VARCHAR2, p_inst_target_type OUT VARCHAR2, p_inst_target_meta_ver OUT VARCHAR2) IS BEGIN BEGIN SELECT property_value INTO p_inst_target_type FROM MGMT_TYPE_PROPERTIES WHERE property_name = MGMT_GLOBAL.G_CLUSTER_MEMBER_TYPE_PROP AND target_type = p_target_type AND EXISTS (SELECT 1 FROM MGMT_TYPE_PROPERTIES WHERE property_name = MGMT_GLOBAL.G_IS_CLUSTER_PROP AND property_value = '1' AND target_type = p_target_type); SELECT max_type_meta_ver INTO p_inst_target_meta_ver FROM MGMT_TARGET_TYPES WHERE target_type = p_inst_target_type; EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS THEN -- no instance type, so this is the same as instance p_inst_target_type := p_target_type; p_inst_target_meta_ver := p_target_type_meta_ver; RETURN; END; END; PROCEDURE load_cred_data(p_target_type VARCHAR2, p_target_type_meta_ver VARCHAR2, p_base_type_name VARCHAR2, p_base_type_target_type VARCHAR2, p_base_target_type_meta_ver VARCHAR2, p_check_for_cluster BOOLEAN, p_der_type_name OUT VARCHAR2, p_der_type_target_type OUT VARCHAR2, p_der_target_type_meta_ver OUT VARCHAR2) IS l_inst_target_type VARCHAR2(64) := p_target_type; l_inst_target_type_meta_ver VARCHAR2(64) := p_target_type_meta_ver; BEGIN -- get the derived credential type of the credential source SELECT type_name, target_type, target_type_meta_ver INTO p_der_type_name, p_der_type_target_type, p_der_target_type_meta_ver FROM MGMT_CREDENTIAL_TYPE_REF WHERE target_type = p_target_type AND target_type_meta_ver = p_target_type_meta_ver START WITH ref_type_name = p_base_type_name AND ref_target_type = p_base_type_target_type CONNECT BY PRIOR ref_type_name = type_name AND PRIOR ref_target_type = target_type AND PRIOR ref_type_meta_ver = target_type_meta_ver; EXCEPTION WHEN NO_DATA_FOUND THEN IF p_check_for_cluster THEN load_instance_target_type(p_target_type, p_target_type_meta_ver, l_inst_target_type, l_inst_target_type_meta_ver); IF l_inst_target_type != p_target_type THEN -- the passed target type is a cluster type. Attempt to find a -- match with the instance target type load_cred_data(l_inst_target_type, l_inst_target_type_meta_ver, p_base_type_name, p_base_type_target_type, p_base_target_type_meta_ver, FALSE, p_der_type_name, p_der_type_target_type, p_der_target_type_meta_ver); -- DONE, so return RETURN; END IF; END IF; -- Default the derived credential type to the base credential type p_der_type_name := p_base_type_name; p_der_type_target_type := p_base_type_target_type; p_der_target_type_meta_ver := p_base_target_type_meta_ver; WHEN TOO_MANY_ROWS THEN -- ABORT- Not Supported raise_application_error(MGMT_GLOBAL.INVALID_JOB_CRED_ERR, 'Got more than one derived credential type for the base credential type'); END; --Name: get_job_credential_metadata --Package: mgmt_job_ui --Purpose: This call gets the job credential object that has -- all the credential meta data for a job type. A job -- type credential meta data can have a set of credential -- sources each of which is composed of a credential type -- and an array of credential sets. This procedure is -- is called for rendering the credentail page -- --IN Parameters: -- p_job_type: type of job, cannot be null -- p_target_type: type of target for the job -- --OUT Parameters: -- p_job_credential: job credential of job type --.............. --ERROR Codes: --invalid_job_cred : The credential metadata for specified job -- type is invalid in the repository. --Notes: -- -- PROCEDURE get_job_credential_metadata(p_job_type IN VARCHAR2, p_target_type IN VARCHAR2, p_job_credential OUT MGMT_JOB_CREDENTIAL_RECORD) IS -- column types l_set_name VARCHAR2(64); l_set_target_type VARCHAR2(64); -- for base cred type l_base_type_name VARCHAR2(64); l_base_type_target_type VARCHAR2(64); l_base_target_type_meta_ver VARCHAR2(64); l_cred_column_param VARCHAR2(64); -- for derived cred type l_der_type_name VARCHAR2(64); l_der_type_target_type VARCHAR2(64); l_der_target_type_meta_ver VARCHAR2(64); l_target_type_meta_ver VARCHAR2(64); l_source_target_type VARCHAR2(64); l_name_var NUMBER(1); l_target_type_var NUMBER(1); l_source_guid VARCHAR2(64); -- user defined types l_job_credential MGMT_JOB_CREDENTIAL_RECORD; l_job_cred_source_list MGMT_JOB_CRED_SOURCE_ARRAY; l_job_cred_source MGMT_JOB_CRED_SOURCE_RECORD; l_base_cred_type_cols MGMT_JOB_VECTOR_PARAMS; -- indexes l_index NUMBER := 0; -- cursors CURSOR c_source(p_job_type_id RAW) IS SELECT cred.credential_set_name, cred.credential_set_target_type, cred.base_cred_type_name, cred.base_cred_type_target_type, cred.base_cred_type_columns, cred.credential_columns_param, cred.source_id FROM mgmt_job_cred_params cred, mgmt_job_param_source param WHERE cred.source_id=param.source_id AND param.source_type='credentials' AND param.job_type_id=p_job_type_id ORDER BY param.source_index; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN SELECT job_type_id INTO l_job_type_id FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=p_job_type AND major_version = (SELECT MAX(major_version) FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=p_job_type); OPEN c_source(l_job_type_id); -- initializing with default is_preferred flag as 0 l_job_credential := MGMT_JOB_CREDENTIAL_RECORD(MGMT_JOB_CRED_SOURCE_ARRAY(), USE_PREFERRED_CREDENTIALS); FETCH c_source INTO l_set_name, l_set_target_type, l_base_type_name, l_base_type_target_type, l_base_cred_type_cols, l_cred_column_param, l_source_guid; WHILE c_source%FOUND LOOP -- if name or target type is starting with %, then 1 is returned otherwise 0 l_name_var := INSTR(l_set_name, '%'); l_target_type_var := INSTR(l_set_target_type, '%'); -- variable cred set should always start with %, otherwise raise error IF l_name_var > 1 OR l_target_type_var > 1 THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_CRED_ERR, 'The credential set name or credential set target type is invalid'); END IF; l_job_cred_source := MGMT_JOB_CRED_SOURCE_RECORD(TRIM(BOTH '%' FROM l_set_name), TRIM(BOTH '%' FROM l_set_target_type), l_name_var, l_target_type_var, null, null, l_base_cred_type_cols, null, l_cred_column_param, null, l_source_guid); BEGIN -- if cred set target type is variable then take the -- credential source target type as target type of job IF l_target_type_var = 0 THEN l_source_target_type := l_set_target_type; ELSE l_source_target_type := p_target_type; END IF; -- get the target type meta version for this target type -- TODO: The query for getting the max version to be corrected SELECT UNIQUE target_type_meta_ver INTO l_target_type_meta_ver FROM mgmt_credential_sets WHERE target_type = l_source_target_type AND TO_NUMBER(target_type_meta_ver) = (SELECT max(TO_NUMBER(target_type_meta_ver)) FROM mgmt_credential_sets WHERE target_type = l_source_target_type); -- Get the target type meta version from types table for base cred -- target type SELECT UNIQUE target_type_meta_ver INTO l_base_target_type_meta_ver FROM mgmt_credential_types WHERE target_type=l_base_type_target_type AND TO_NUMBER(target_type_meta_ver) = (SELECT max(TO_NUMBER(target_type_meta_ver)) FROM mgmt_credential_types WHERE target_type = l_base_type_target_type); EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_CRED_ERR, 'The target type is invalid'); END; load_cred_data(l_source_target_type, l_target_type_meta_ver, l_base_type_name, l_base_type_target_type, l_base_target_type_meta_ver, TRUE, l_der_type_name, l_der_type_target_type, l_der_target_type_meta_ver); -- first find the metadata for base credential type l_job_cred_source.base_cred_type := get_cred_type_metadata(l_base_type_name, l_base_type_target_type, l_base_target_type_meta_ver ); -- next find the matadata for derived credential type if it is different -- than base type IF l_der_type_name != l_base_type_name OR l_der_type_target_type != l_base_type_target_type THEN l_job_cred_source.derived_cred_type := get_cred_type_metadata(l_der_type_name, l_der_type_target_type, l_der_target_type_meta_ver ); ELSE l_job_cred_source.derived_cred_type := l_job_cred_source.base_cred_type; END IF; -- assign the credential set list to the credential source object l_job_cred_source.cred_set := get_cred_set_metadata(l_set_name, l_set_target_type, l_der_type_name, l_der_type_target_type, l_der_target_type_meta_ver ); l_job_credential.cred_source.extend(1); l_index := l_index+1; l_job_credential.cred_source(l_index) := l_job_cred_source; FETCH c_source INTO l_set_name, l_set_target_type, l_base_type_name, l_base_type_target_type, l_base_cred_type_cols, l_cred_column_param, l_source_guid; END LOOP; CLOSE c_source; p_job_credential := l_job_credential; END get_job_credential_metadata; --Name: get_cred_type_metadata --Package: mgmt_job_ui --Purpose: This call gets the job credential MGMT_JOB_CRED_TYPE_RECORD -- object that has credential type information. -- --IN Parameters: -- p_type_name: name of the credential type -- p_type_target_type: target type for the credential type -- p_target_type_meta_ver: meta version of the target type -- --.............. --ERROR Codes: --invalid_job_cred : The credential metadata for specified job -- type is invalid in the repository. --Notes: -- -- FUNCTION get_cred_type_metadata(p_type_name VARCHAR2, p_type_target_type VARCHAR2, p_target_type_meta_ver VARCHAR2 ) RETURN MGMT_JOB_CRED_TYPE_RECORD IS l_type_display_name VARCHAR2(64); l_type_nlsid VARCHAR2(64); l_type_col_val VARCHAR2(64); l_type_col_def_val NUMBER(1); l_type_col_val_nlsid VARCHAR2(64); l_base_type_col_name VARCHAR2(64); l_job_cred_type MGMT_JOB_CRED_TYPE_RECORD; l_cred_type_col_list MGMT_JOB_CRED_TYPE_COL_ARRAY; l_cred_col_val_list MGMT_JOB_CRED_COL_VAL_ARRAY; l_default_index NUMBER := 0; l_col_val_index NUMBER := 0; CURSOR c_type_col_vals( l_type_target_type varchar2, l_target_type_meta_ver varchar2, l_type_name varchar2, l_type_col_name varchar2 ) IS SELECT value,default_value,value_nlsid FROM mgmt_credential_type_col_vals WHERE target_type = l_type_target_type AND target_type_meta_ver = l_target_type_meta_ver AND type_name = l_type_name AND type_column_name = l_type_col_name ORDER BY default_value DESC, value; BEGIN l_job_cred_type := null; BEGIN SELECT type_display_name, type_display_nlsid INTO l_type_display_name, l_type_nlsid FROM mgmt_credential_types WHERE target_type = p_type_target_type AND type_name = p_type_name AND target_type_meta_ver = p_target_type_meta_ver; -- set the credential type for the source l_job_cred_type := MGMT_JOB_CRED_TYPE_RECORD(p_type_name, l_type_display_name, l_type_nlsid, p_type_target_type, p_target_type_meta_ver, null); -- now find the credential type columns -- by default, current value index set to -1 and current value as null -- default value index and allowable values is populated later SELECT MGMT_JOB_CRED_TYPE_COL_RECORD(type_column_name, type_column_display_name, ref_column_name, type_column_display_nlsid, null,-1,-1,key,null) BULK COLLECT INTO l_cred_type_col_list FROM mgmt_credential_type_columns WHERE target_type = p_type_target_type AND target_type_meta_ver = p_target_type_meta_ver AND type_name = p_type_name ORDER BY key DESC, type_column_name; l_default_index := -1; -- Loop through cred type columns list and populate the allowable values FOR i IN 1..l_cred_type_col_list.COUNT LOOP -- we need mapping of actual column name with that of base credential column -- The base column name is fetched recursively from the type ref table IF l_cred_type_col_list(i).base_type_column_name != null THEN BEGIN SELECT ref_column_name INTO l_base_type_col_name FROM MGMT_CREDENTIAL_TYPE_COLUMNS c, MGMT_CREDENTIAL_TYPE_REF ref WHERE c.target_type = ref.target_type AND c.target_type_meta_ver = ref.target_type_meta_ver AND c.type_name = ref.type_name START WITH c.type_name = p_type_name AND c.target_type_meta_ver = p_target_type_meta_ver AND c.target_type = p_type_target_type AND c.type_column_name = l_cred_type_col_list(i).type_column_name CONNECT BY PRIOR ref.type_name=ref.ref_name AND PRIOR ref.ref_target_type=ref.target_type AND PRIOR ref.ref_type_meta_ver=ref.target_type_meta_ver; EXCEPTION -- do nothing, keep the base type column name as it is WHEN NO_DATA_FOUND THEN NULL; END; -- if above query fetches value, assign it to base type column name IF l_base_type_col_name != null THEN l_cred_type_col_list(i).base_type_column_name := l_base_type_col_name; END IF; ELSE -- if base type column name is null, means there is no reference of -- this column to other type. Hence this type is itself an base type column. l_cred_type_col_list(i).base_type_column_name := l_cred_type_col_list(i).type_column_name; END IF; -- Open curser to fetch the type coloumns allowable values OPEN c_type_col_vals(p_type_target_type, p_target_type_meta_ver, p_type_name, l_cred_type_col_list(i).type_column_name); l_cred_col_val_list := MGMT_JOB_CRED_COL_VAL_ARRAY(); -- Loop through all allowable values and also find default value index LOOP FETCH c_type_col_vals INTO l_type_col_val, l_type_col_def_val, l_type_col_val_nlsid; EXIT WHEN c_type_col_vals%NOTFOUND; l_cred_col_val_list.extend(1); l_col_val_index := l_col_val_index+1; l_cred_col_val_list(l_col_val_index) := MGMT_JOB_CRED_COL_VAL_RECORD(l_type_col_val, l_type_col_val_nlsid); IF l_type_col_def_val = 1 THEN l_default_index := l_col_val_index-1; END IF; END LOOP; l_col_val_index := 0; l_cred_type_col_list(i).allowable_values := l_cred_col_val_list; l_cred_type_col_list(i).default_value_index := l_default_index; l_default_index := -1; CLOSE c_type_col_vals; END LOOP; l_job_cred_type.cred_type_cols := l_cred_type_col_list; EXCEPTION WHEN NO_DATA_FOUND THEN IF c_type_col_vals%ISOPEN THEN CLOSE c_type_col_vals; END IF; raise_application_error(MGMT_GLOBAL.INVALID_JOB_CRED_ERR, 'The credential type data for the job type and target type is invalid'); END; return l_job_cred_type; END get_cred_type_metadata; --Name: get_cred_set_metadata --Package: mgmt_job_ui --Purpose: This call gets the job credential MGMT_JOB_CRED_SET_ARRAY -- object that has array of credential set if credential set -- is variable else returns the metadata of specific set. -- --IN Parameters: -- p_set_name: name of the credential set -- p_set_target_type: target type for the credential set -- p_type_name: name of the credential type to which set belongs -- p_type_target_type: target type of the credential type -- p_target_type_meta_ver: meta version of the target type -- --.............. --ERROR Codes: --invalid_job_cred : The credential metadata for specified job -- type is invalid in the repository. --Notes: -- -- FUNCTION get_cred_set_metadata(p_set_name VARCHAR2, p_set_target_type VARCHAR2, p_type_name VARCHAR2, p_type_target_type VARCHAR2, p_target_type_meta_ver VARCHAR2 ) RETURN MGMT_JOB_CRED_SET_ARRAY IS l_set_col_val VARCHAR2(64); l_set_col_def_val VARCHAR2(64); l_set_col_val_nlsid VARCHAR2(64); l_name_var NUMBER(1); l_target_type_var NUMBER(1); l_cred_set_list MGMT_JOB_CRED_SET_ARRAY; l_cred_set MGMT_JOB_CRED_SET_RECORD; l_cred_set_col_list MGMT_JOB_CRED_SET_COL_ARRAY; l_cred_set_col_val_list MGMT_JOB_CRED_COL_VAL_ARRAY; l_default_index NUMBER := 0; l_col_val_index NUMBER := 0; CURSOR c_set_col_vals( l_set_target_type varchar2, l_target_type_meta_ver varchar2, l_set_name varchar2, l_set_col_name varchar2 ) IS SELECT value,default_value,value_nlsid FROM mgmt_credential_set_col_vals WHERE target_type = l_set_target_type AND target_type_meta_ver = l_target_type_meta_ver AND set_name = l_set_name AND set_column_name = l_set_col_name ORDER BY default_value DESC, value; BEGIN l_cred_set_list := null; -- if name or target type is starting with %, then 1 is returned otherwise 0 l_name_var := INSTR(p_set_name, '%'); l_target_type_var := INSTR(p_set_target_type, '%'); -- variable cred set should always start with %, otherwise raise error IF l_name_var > 1 OR l_target_type_var > 1 THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_CRED_ERR, 'The credential set name or credential set target type is invalid'); END IF; BEGIN -- Case1: if credential set name and credential set target type is fixed IF l_name_var = 0 AND l_target_type_var = 0 THEN SELECT MGMT_JOB_CRED_SET_RECORD(set_name, set_display_name, set_display_nlsid, target_type, target_type_meta_ver, null) BULK COLLECT INTO l_cred_set_list FROM mgmt_credential_sets WHERE target_type = p_set_target_type AND target_type_meta_ver = p_target_type_meta_ver AND set_name = p_set_name; -- if set name is variable then use derived credential type -- to find the credential sets. ELSE -- populate cred set list object from credential sets table SELECT MGMT_JOB_CRED_SET_RECORD(set_name, set_display_name, set_display_nlsid, target_type, target_type_meta_ver, null) BULK COLLECT INTO l_cred_set_list FROM mgmt_credential_sets WHERE target_type = p_type_target_type AND credential_type_name = p_type_name AND set_usage = MGMT_CREDENTIAL.PREFCRED_SET_USAGE AND set_context_type = MGMT_CREDENTIAL.TARGET_SET_CONTEXT_TYPE AND target_type_meta_ver = p_target_type_meta_ver; END IF; -- Case1 -- loop through all credential sets and populate cred set columns FOR i IN 1..l_cred_set_list.COUNT LOOP -- now find the credential set columns -- no need for order by, since we are not using this for display -- Also select type column name SELECT MGMT_JOB_CRED_SET_COL_RECORD(set_column_name,set_column_display_name, type_column_name,-1,-1,null) BULK COLLECT INTO l_cred_set_col_list FROM mgmt_credential_set_columns WHERE target_type = l_cred_set_list(i).target_type AND target_type_meta_ver = l_cred_set_list(i).target_type_meta_ver AND set_name = l_cred_set_list(i).set_name; l_default_index := -1; l_col_val_index := 0; -- Loop thru cred set columns list and populate the allowable values FOR j IN 1..l_cred_set_col_list.COUNT LOOP -- Open curser to fetch the type coloumns allowable values OPEN c_set_col_vals(l_cred_set_list(i).target_type, l_cred_set_list(i).target_type_meta_ver, l_cred_set_list(i).set_name, l_cred_set_col_list(j).set_column_name); l_cred_set_col_val_list := MGMT_JOB_CRED_COL_VAL_ARRAY(); -- Loop thru all allowable values and also find default value index LOOP FETCH c_set_col_vals INTO l_set_col_val, l_set_col_def_val, l_set_col_val_nlsid; EXIT WHEN c_set_col_vals%NOTFOUND; l_cred_set_col_val_list.extend(1); l_col_val_index := l_col_val_index+1; l_cred_set_col_val_list(l_col_val_index) := MGMT_JOB_CRED_COL_VAL_RECORD (l_set_col_val, l_set_col_val_nlsid); IF l_set_col_def_val = 1 THEN l_default_index := l_col_val_index-1; END IF; END LOOP; l_col_val_index := 0; l_cred_set_col_list(j).allowable_values := l_cred_set_col_val_list; l_cred_set_col_list(j).default_value_index := l_default_index; l_default_index := -1; CLOSE c_set_col_vals; END LOOP; l_cred_set_list(i).cred_set_cols := l_cred_set_col_list; END LOOP; EXCEPTION WHEN NO_DATA_FOUND THEN IF c_set_col_vals%ISOPEN THEN CLOSE c_set_col_vals; END IF; raise_application_error(MGMT_GLOBAL.INVALID_JOB_CRED_ERR, 'The credential set data for the job type and target type is invalid'); END; return l_cred_set_list; END get_cred_set_metadata; procedure get_target_type_data(target_type_name IN VARCHAR2, prop_arr out MGMT_TARGET_TYPE_PROP_ARRAY ) IS --TARGET TYPE PEROPERTY property_name varchar2(32); property_display_name VARCHAR2(255); property_display_NLS_ID VARCHAR2(255); PROPERTY_ARRAY MGMT_TARGET_TYPE_PROP_ARRAY ; L_PROP_COUNTER number(3) ; CURSOR C_TARGET_PROPERTY_INFO (target_type_name VARCHAR2) IS SELECT property_name, property_display_name, property_display_nlsid FROM MGMT_TARGET_PROP_DEFS WHERE TYPE_META_VER = (SELECT MAX(to_number(TYPE_META_VER)) FROM MGMT_TARGET_PROP_DEFS WHERE target_type=target_type_name) AND target_type=target_type_name AND credential_flag=0; BEGIN ---- FETCHING TARGETINFO FIRST L_PROP_COUNTER := 1; OPEN C_TARGET_PROPERTY_INFO(target_type_name) ; PROPERTY_ARRAY := MGMT_TARGET_TYPE_PROP_ARRAY(); LOOP fetch C_TARGET_PROPERTY_INFO into property_name ,property_display_name ,property_display_NLS_ID ; exit when C_TARGET_PROPERTY_INFO%NOTFOUND; PROPERTY_ARRAY.extend(1); PROPERTY_ARRAY(L_PROP_COUNTER) := MGMT_TARGET_TYPE_PROP_RECORD(property_name , property_display_name , property_display_NLS_ID ); L_PROP_COUNTER := L_PROP_COUNTER +1; END LOOP ; close C_TARGET_PROPERTY_INFO ; --- END FETCHING PROPERY DATA prop_arr := PROPERTY_ARRAY ; end get_target_type_data ; PROCEDURE get_job_type_metadata( job_type_name IN VARCHAR2 , l_parameter_metaData out mgmt_job_parameter_metaData) IS job_type VARCHAR2(32); is_default NUMBER(1); option_text_nlsid VARCHAR2(32); option_text_default VARCHAR2(256); parameter_name VARCHAR2(32); show_in_create NUMBER(1); show_in_results NUMBER(1); label_nlsid VARCHAR2(32); label_default VARCHAR2(256); hint_nlsid VARCHAR2(32); hint_default VARCHAR2(256); display_mode NUMBER(2); num_lines NUMBER(2); default_text VARCHAR2(4000); default_nlsid VARCHAR2(32); param_order number(3); uri VARCHAR2(4000); class VARCHAR2(4000); online_help_topic VARCHAR2(4000); task_online_help_topic VARCHAR2(4000); uri_use NUMBER(2); uri_uri_info VARCHAR2(4000); is_jsp NUMBER(1); class_uri_info VARCHAR2(4000); l_uri_info_counter NUMBER(3); l_mgmt_job_uri_info_array MGMT_JOB_TYPE_URI_INFO_ARRAY; parameter_metaData MGMT_JOB_PARAMETER_METADATA ; parameter_uri_metadata MGMT_JOB_PRMTER_URI_INF_RECORD ; job_parameter_array MGMT_JOB_PARAMETER_ARRAY; prmeter_drpdn_array MGMT_JOB_PRMETER_DRPDN_ARRAY; l_required_params MGMT_JOB_VECTOR_PARAMS; l_current_param_name MGMT_JOB_PARAMETER.parameter_name%TYPE; l_parameter_required number(1) default 0; l_parameter_encrypted number(1) default 0; l_dropdown_counter number(4); option_value varchar2(32) ; option_param_order number(3); l_showTargetProperty number(1); CURSOR c_uri_info_metaData(p_job_type_id RAW) IS SELECT job_type, uri_use, uri, is_jsp, class FROM MGMT_JOB_TYPE_URI_INFO ui, MGMT_JOB_TYPE_INFO ji WHERE ui.job_type_id = p_job_type_id AND ji.job_type_id=ui.job_type_id; --rem we really do not need cursor for this it will always resturn 0 or 1 record CURSOR c_parameteruri_info(p_job_type_id RAW) IS SELECT job_type, uri, class, help_topic, task_help_topic,show_target_properties FROM MGMT_JOB_TYPE_PARAM_URI_INFO pui, MGMT_JOB_TYPE_INFO ji WHERE pui.job_type_id=p_job_type_id AND pui.job_type_id=ji.job_type_id; CURSOR c_parameter_meta_info(p_job_type_id RAW) IS SELECT job_type, parameter_name, show_in_create, show_in_results, label_nlsid, label_default, hint_nlsid, hint_default, display_mode, num_lines, default_text, default_nlsid, param_order FROM MGMT_JOB_TYPE_PARAM_DSPLY_INFO di, MGMT_JOB_TYPE_INFO ji WHERE di.job_type_id=ji.job_type_id AND di.job_type_id=p_job_type_id AND di.show_in_create=1; CURSOR c_parameter_dropdown(p_job_type_id RAW, l_parameter_name varchar2) is select is_default,option_value ,option_text_nlsid ,option_text_default , param_order from MGMT_JOB_TYPE_PARAM_DROPDOWNS where job_type_id = p_job_type_id and parameter_name = l_parameter_name order by parameter_name,param_order ; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_major_version MGMT_JOB_TYPE_INFO.job_type_id%TYPE; begin SELECT job_type_id INTO l_job_type_id FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=job_type_name AND major_version = (SELECT MAX(major_version) FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=job_type_name); OPEN c_uri_info_metaData(l_job_type_id); l_mgmt_job_uri_info_array := MGMT_JOB_TYPE_URI_INFO_ARRAY(); l_uri_info_counter := 1; LOOP fetch c_uri_info_metaData into job_type ,uri_use,uri_uri_info,is_jsp ,class_uri_info ; exit when c_uri_info_metaData%NOTFOUND; l_mgmt_job_uri_info_array.extend(1); l_mgmt_job_uri_info_array(l_uri_info_counter) := MGMT_JOB_TYPE_URI_INFO_record( job_type ,uri_use,uri_uri_info, is_jsp ,class_uri_info ); END LOOP ; close c_uri_info_metaData ; OPEN c_parameteruri_info(l_job_type_id); l_uri_info_counter := 1; LOOP fetch c_parameteruri_info into job_type,uri , class, online_help_topic,task_online_help_topic,l_showTargetProperty ; exit when c_parameteruri_info%NOTFOUND; parameter_uri_metadata := MGMT_JOB_PRMTER_URI_INF_RECORD( job_type,uri ,class, online_help_topic, task_online_help_topic ,l_showTargetProperty); l_uri_info_counter := l_uri_info_counter +1; END LOOP; close c_parameteruri_info; OPEN c_parameter_meta_info(l_job_type_id); job_parameter_array := MGMT_JOB_PARAMETER_ARRAY(); l_uri_info_counter :=0; LOOP l_uri_info_counter := l_uri_info_counter + 1; fetch c_parameter_meta_info into job_type,parameter_name , show_in_create , show_in_results , label_nlsid , label_default , hint_nlsid , hint_default ,display_mode ,num_lines , default_text , default_nlsid, param_order ; exit when c_parameter_meta_info%NOTFOUND; job_parameter_array.extend(1); ---starting fetching required and encrypted parameter information FOR crec IN (SELECT required,encrypted,parameter_names FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=l_job_type_id AND (encrypted = 1 or required =1)) LOOP l_required_params := crec.parameter_names; -- Go through each one of these parameters, locate them -- in your data structure, and set required true FOR i IN 1..l_required_params.COUNT LOOP l_current_param_name := l_required_params(i); if l_current_param_name = parameter_name then if crec.required = 1 then l_parameter_required := 1 ; end if ; if crec.encrypted = 1 then l_parameter_encrypted := 1 ; end if ; end if; END LOOP; END LOOP; IF display_mode = 2 THEN prmeter_drpdn_array := MGMT_JOB_PRMETER_DRPDN_ARRAY() ; open c_parameter_dropdown(l_job_type_id,parameter_name ) ; l_dropdown_counter := 1; LOOP fetch c_parameter_dropdown into is_default ,option_value, option_text_nlsid ,option_text_default, option_param_order ; EXIT WHEN c_parameter_dropdown%NOTFOUND; prmeter_drpdn_array.extend(1); prmeter_drpdn_array(l_dropdown_counter) := MGMT_JOB_PRMETER_DRPDN_RECORD(is_default ,option_value ,option_text_nlsid ,option_text_default,l_dropdown_counter ) ; l_dropdown_counter :=l_dropdown_counter + 1; end loop ; close c_parameter_dropdown ; job_parameter_array(l_uri_info_counter) := MGMT_JOB_PARAMETER_RECORD( job_type,parameter_name , show_in_create , show_in_results , label_nlsid , label_default , hint_nlsid , hint_default , display_mode ,num_lines , default_text ,default_nlsid, param_order ,prmeter_drpdn_array ,l_parameter_required ,l_parameter_encrypted ); ELSE job_parameter_array(l_uri_info_counter) := MGMT_JOB_PARAMETER_RECORD( job_type,parameter_name , show_in_create , show_in_results , label_nlsid , label_default, hint_nlsid ,hint_default , display_mode , num_lines , default_text , default_nlsid, param_order , null ,l_parameter_required,l_parameter_encrypted); END IF; -- after seting the required flag to the parameter reset the flags before next iteration starts l_parameter_required := 0; l_parameter_encrypted := 0; END LOOP ; parameter_metaData := MGMT_JOB_PARAMETER_METADATA(l_mgmt_job_uri_info_array , parameter_uri_metadata , job_parameter_array); l_parameter_metaData := parameter_metaData ; end get_job_type_metadata ; PROCEDURE handle_http_session_expiry(p_object_type VARCHAR2, p_http_session VARCHAR2) IS BEGIN FOR crec IN (SELECT object_guid FROM MGMT_HTTP_SESSION_OBJECTS WHERE object_type=p_object_type AND session_id=p_http_session ORDER BY object_guid) LOOP -- If currently running, stop it. Note there's a chance -- this could fail, for certain system jobs BEGIN stop_all_executions_with_id(crec.object_guid, true); delete_job(crec.object_guid); EXCEPTION -- If the job has gone away already, ignore it WHEN OTHERS THEN NULL; END; END LOOP; END; -- Increase the number of count by one if the number of metric thresholds -- or policy rules on the scoped object with which the corrective action is associated PROCEDURE increment_ca_ref_count(p_job_id RAW) IS l_is_ca MGMT_JOB.is_corrective_action%TYPE; l_ca_reference_count NUMBER; l_lock NUMBER; BEGIN -- Lock the job for updating reference count l_lock := lock_job(p_job_id); EM_CHECK.check_not_null(p_job_id, 'CA ID'); BEGIN SELECT is_corrective_action INTO l_is_ca FROM MGMT_JOB WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Not a valid Corrective action'); END; IF l_is_ca=1 THEN BEGIN SELECT ca_reference_count INTO l_ca_reference_count FROM MGMT_CORRECTIVE_ACTION WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.JOB_PARAM_MISSING_ERR, 'Refernce count should not be null'); END; UPDATE MGMT_CORRECTIVE_ACTION SET ca_reference_count = l_ca_reference_count+1 WHERE job_id=p_job_id; END IF; END; -- Decrease the number of count by one if the number of metric thresholds -- or policy rules on the scoped object with which the corrective action is associated is removed. PROCEDURE decrement_ca_ref_count(p_job_id RAW)IS l_is_correctiveaction MGMT_JOB.is_corrective_action%TYPE; l_count NUMBER; l_commit NUMBER := 0; l_lock NUMBER; BEGIN -- Lock the job for updating reference count l_lock := lock_job(p_job_id); EM_CHECK.check_not_null(p_job_id, 'CA ID'); BEGIN SELECT is_corrective_action INTO l_is_correctiveaction FROM MGMT_JOB WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Not a valid Corrective action'); END; IF l_is_correctiveaction=1 THEN BEGIN SELECT ca_reference_count INTO l_count FROM MGMT_CORRECTIVE_ACTION WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.JOB_PARAM_MISSING_ERR, 'Refernce count should not be null'); END; IF l_count>0 THEN l_count := l_count-1; IF l_count=0 THEN MGMT_JOB_ENGINE.delete_ca(p_job_id=>p_job_id); ELSE UPDATE MGMT_CORRECTIVE_ACTION SET ca_reference_count = l_count WHERE job_id=p_job_id; END IF; END IF; END IF; END; -- Lock CAs for object PROCEDURE lock_cas_for_object(p_object_guid IN RAW, p_ca_scope IN NUMBER, p_target_type IN VARCHAR2 DEFAULT NULL) IS l_ignore NUMBER; BEGIN IF p_ca_scope=MGMT_CA.CA_SCOPE_TARGET THEN FOR job_rec IN (SELECT job_id FROM mgmt_corrective_action WHERE ca_target_guid = p_object_guid ORDER BY job_id) LOOP l_ignore := lock_job(job_rec.job_id); END LOOP; ELSIF p_ca_scope IN (MGMT_CA.CA_SCOPE_TEMPLATE, MGMT_CA.CA_SCOPE_TEMPLATE_COPY) THEN FOR job_rec IN (SELECT job_id FROM mgmt_corrective_action WHERE ca_template_guid = p_object_guid ORDER BY job_id) LOOP l_ignore := lock_job(job_rec.job_id); END LOOP; ELSIF p_ca_scope = MGMT_CA.CA_SCOPE_TARGET_TYPE THEN EM_CHECK.CHECK_NOT_NULL(p_target_type,' p_target type ') ; FOR job_rec in ( SELECT j.job_id FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE c.job_id = j.job_id AND j.target_type = p_target_type AND c.ca_scope = MGMT_CA.CA_SCOPE_TARGET_TYPE AND j.is_corrective_action = 1 AND j.nested=0 ORDER BY job_id) LOOP l_ignore := lock_job(job_rec.job_id); END LOOP; ELSE raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Invalid scope for lock_cas_for_object'); END IF; END lock_cas_for_object; -- Reset the reference count for all CAs associated with the object PROCEDURE reset_ca_refctr(p_object_guid IN RAW, p_ca_scope IN NUMBER) IS BEGIN IF p_ca_scope=MGMT_CA.CA_SCOPE_TARGET THEN UPDATE MGMT_CORRECTIVE_ACTION SET ca_reference_count = 0 WHERE ca_target_guid = p_object_guid; ELSIF p_ca_scope IN (MGMT_CA.CA_SCOPE_TEMPLATE, MGMT_CA.CA_SCOPE_TEMPLATE_COPY) THEN UPDATE MGMT_CORRECTIVE_ACTION SET ca_reference_count = 0 WHERE ca_template_guid = p_object_guid; ELSE raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Invalid scope for reset_ca_refctr'); END IF; END reset_ca_refctr; -- Delete all CAs corresponding to a template or template copy PROCEDURE delete_template_cas(p_template_guid IN RAW, p_template_copy BOOLEAN DEFAULT false) IS l_ca_guids MGMT_USER_GUID_ARRAY; l_ca_scope MGMT_CORRECTIVE_ACTION.ca_scope%TYPE; BEGIN IF p_template_copy THEN l_ca_scope := CA_SCOPE_TEMPLATE_COPY; ELSE l_ca_scope := CA_SCOPE_TEMPLATE; END IF; SELECT job_id BULK COLLECT INTO l_ca_guids FROM MGMT_CORRECTIVE_ACTION WHERE ca_template_guid=p_template_guid AND ca_scope=l_ca_scope ORDER BY job_id; IF l_ca_guids IS NOT NULL AND l_ca_guids.COUNT > 0 THEN FOR i IN 1..l_ca_guids.COUNT LOOP delete_ca(l_ca_guids(i)); END LOOP; END IF; END; -- Delete CAs with no references for a given target/template PROCEDURE delete_noref_cas(p_object_guid IN RAW, p_ca_scope IN NUMBER) IS BEGIN IF p_ca_scope=MGMT_CA.CA_SCOPE_TARGET THEN -- Can use BULK collect FOR ca_rec IN (SELECT job_id FROM MGMT_CORRECTIVE_ACTION WHERE ca_target_guid = p_object_guid AND ca_reference_count = 0 ORDER BY job_id) LOOP MGMT_JOB_ENGINE.delete_ca(p_job_id=>ca_rec.job_id); END LOOP; ELSIF p_ca_scope IN (MGMT_CA.CA_SCOPE_TEMPLATE, MGMT_CA.CA_SCOPE_TEMPLATE_COPY) THEN -- Can use BULK collect FOR ca_rec IN (SELECT job_id FROM MGMT_CORRECTIVE_ACTION WHERE ca_template_guid = p_object_guid AND ca_reference_count = 0 ORDER BY job_id) LOOP MGMT_JOB_ENGINE.delete_ca(p_job_id=>ca_rec.job_id); END LOOP; ELSE raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Invalid scope for delete_noref_cas'); END IF; END delete_noref_cas; -- This procudure is responsible to associate a corrective action configured -- target with policy PROCEDURE set_target_policy_assoc_ca(p_target_type IN VARCHAR2, p_target_name IN VARCHAR2, p_policy_name IN VARCHAR2, p_coll_name IN VARCHAR2, p_key_value IN VARCHAR2, p_key_operator IN NUMBER DEFAULT 0, p_violation_level IN NUMBER, p_ca_name IN VARCHAR2 ) IS l_job_id RAW(16); BEGIN EM_CHECK.check_not_null(p_target_type, 'p_target_type'); EM_CHECK.check_not_null(p_target_name, 'p_target_name'); EM_CHECK.check_not_null(p_ca_name, 'p_ca_name'); l_job_id:= MGMT_CA.get_target_scoped_job_id(p_ca_name, p_target_name, p_target_type); MGMT_POLICY.set_target_policy_ca(p_target_type=>p_target_type, p_target_name=>p_target_name, p_policy_name=>p_policy_name, p_coll_name=>p_coll_name, p_key_value=>p_key_value, p_key_operator=>p_key_operator, p_violation_level=> p_violation_level, p_job_id=>l_job_id); increment_ca_ref_count(l_job_id); END; -- This procudure is responsible to associate a corrective action configured -- target with Metric PROCEDURE set_target_metric_assoc_ca(p_target_type IN VARCHAR2, p_target_name IN VARCHAR2, p_metric_name IN VARCHAR2, p_metric_column IN VARCHAR2, p_coll_name IN VARCHAR2, p_key_value IN VARCHAR2, p_key_operator IN NUMBER DEFAULT 0, p_violation_level IN NUMBER, p_ca_name IN VARCHAR2 ) IS l_job_id RAW(16); BEGIN EM_CHECK.check_not_null(p_target_type, 'p_target_type'); EM_CHECK.check_not_null(p_target_name, 'p_target_name'); EM_CHECK.check_not_null(p_ca_name, 'p_ca_name'); l_job_id:= MGMT_CA.get_target_scoped_job_id(p_ca_name, p_target_name, p_target_type); MGMT_POLICY.set_target_metric_ca(p_target_type=>p_target_type, p_target_name=>p_target_name, p_metric_name=>p_metric_name, p_metric_column=>p_metric_column, p_coll_name=>p_coll_name, p_key_value=>p_key_value, p_key_operator=>p_key_operator, p_violation_level=> p_violation_level, p_job_id=>l_job_id); increment_ca_ref_count(l_job_id); END; -- This procudure is responsible to set a target configured Corrective action -- for a particular severity level, on a particular agent fixit association. PROCEDURE set_target_metric_agent_fixit(p_target_type IN VARCHAR2, p_target_name IN VARCHAR2, p_metric_name IN VARCHAR2, p_metric_column IN VARCHAR2, p_coll_name IN VARCHAR2, p_key_value IN VARCHAR2, p_key_operator IN NUMBER DEFAULT 0, p_agent_fixit IN VARCHAR2) IS BEGIN MGMT_POLICY.set_target_metric_fixit_job(p_target_type=>p_target_type, p_target_name=>p_target_name, p_metric_name=>p_metric_name, p_metric_column=>p_metric_column, p_coll_name=>p_coll_name, p_key_value=> p_key_value, p_key_operator=> p_key_operator, p_agent_fixit=> p_agent_fixit); END; -- This procudure is responsible to associate a corrective action configured -- template with policy PROCEDURE set_template_policy_assoc_ca(p_target_type IN VARCHAR2, p_template_name IN VARCHAR2, p_policy_name IN VARCHAR2, p_coll_name IN VARCHAR2, p_key_value IN VARCHAR2, p_key_operator IN NUMBER DEFAULT 0, p_violation_level IN NUMBER, p_ca_name IN VARCHAR2 ) IS l_job_id RAW(16); BEGIN EM_CHECK.check_not_null(p_target_type, 'p_target_type'); EM_CHECK.check_not_null(p_template_name, 'p_template_name'); EM_CHECK.check_not_null(p_ca_name, 'p_ca_name'); l_job_id:= MGMT_CA.get_template_scoped_job_id(p_ca_name, p_template_name, p_target_type); MGMT_POLICY.set_template_policy_ca(p_target_type=>p_target_type, p_template_name=>p_template_name, p_policy_name=>p_policy_name, p_coll_name=>p_coll_name, p_key_value=>p_key_value, p_key_operator=>p_key_operator, p_violation_level=> p_violation_level, p_job_id=>l_job_id); increment_ca_ref_count(l_job_id); END; -- This procudure is responsible to associate a corrective action configured -- template with Metric PROCEDURE set_template_metric_assoc_ca(p_target_type IN VARCHAR2, p_template_name IN VARCHAR2, p_metric_name IN VARCHAR2, p_metric_column IN VARCHAR2, p_coll_name IN VARCHAR2, p_key_value IN VARCHAR2, p_key_operator IN NUMBER DEFAULT 0, p_violation_level IN NUMBER, p_ca_name IN VARCHAR2 ) IS l_job_id RAW(16); BEGIN EM_CHECK.check_not_null(p_target_type, 'p_target_type'); EM_CHECK.check_not_null(p_template_name, 'p_template_name'); EM_CHECK.check_not_null(p_ca_name, 'p_ca_name'); l_job_id:= MGMT_CA.get_template_scoped_job_id(p_ca_name, p_template_name, p_target_type); MGMT_POLICY.set_template_metric_ca(p_target_type=>p_target_type, p_template_name=>p_template_name, p_metric_name=>p_metric_name, p_metric_column=>p_metric_column, p_coll_name=>p_coll_name, p_key_value=>p_key_value, p_key_operator=>p_key_operator, p_violation_level=> p_violation_level, p_job_id=>l_job_id); increment_ca_ref_count(l_job_id); END; -- This procudure is responsible to set a target configured Corrective action -- for a particular severity level, on a particular agent fixit association. PROCEDURE set_template_metric_fixit(p_target_type IN VARCHAR2, p_template_name IN VARCHAR2, p_metric_name IN VARCHAR2, p_metric_column IN VARCHAR2, p_coll_name IN VARCHAR2, p_key_value IN VARCHAR2, p_key_operator IN NUMBER DEFAULT 0, p_agent_fixit IN VARCHAR2) IS BEGIN MGMT_POLICY.set_template_metric_fixit_job(p_target_type=>p_target_type, p_template_name=>p_template_name, p_metric_name=>p_metric_name, p_metric_column=>p_metric_column, p_coll_name=>p_coll_name, p_key_value=> p_key_value, p_key_operator=> p_key_operator, p_agent_fixit=> p_agent_fixit); END; -- Insert parameters for a corrective action/Job. -- Also encrypt the values as necessary PROCEDURE insert_job_parameters(p_job_id IN RAW, p_job_type_id IN RAW, p_job_params IN MGMT_JOB_PARAM_LIST) IS l_encrypted NUMBER; BEGIN FOR i IN 1..p_job_params.count LOOP -- If any param source needs the param to be encrypted, we encrypt it -- Note: Here we look into ANY paramsource that encrypts the parameter -- although it may not be encountered at all. But -- encrypt_job_level_params too did the same, so this should be fine. SELECT COUNT(1) INTO l_encrypted FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id = p_job_type_id AND NVL(encrypted, 0) = 1 AND p_job_params(i).param_name IN (SELECT * FROM TABLE(parameter_names)); IF l_encrypted != 0 THEN l_encrypted := 1; END IF; IF p_job_params(i).param_type = PARAM_TYPE_SCALAR THEN IF p_job_params(i).scalar_value IS NULL THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAM_ERR, 'Incorrectly specified parameter ' || p_job_params(i).param_name); ELSE INSERT INTO MGMT_JOB_PARAMETER ( job_id, execution_id, parameter_name, parameter_type, encrypted, scalar_value, created_at_submit) VALUES (p_job_id, MGMT_JOB_ENGINE.NO_EXECUTION, p_job_params(i).param_name, p_job_params(i).param_type, l_encrypted, encrypt_scalar(l_encrypted, p_job_params(i).scalar_value), 1); END IF; ELSE IF p_job_params(i).param_type = PARAM_TYPE_VECTOR THEN IF p_job_params(i).vector_value IS NULL THEN raise_application_error(mgmt_global.INVALID_PARAM_ERR, 'Incorrectly specified parameter ' || p_job_params(i).param_name); END IF; ELSIF p_job_params(i).param_type != PARAM_TYPE_LARGE THEN raise_application_error(mgmt_global.INVALID_PARAM_ERR, 'Incorrectly specified parameter ' || p_job_params(i).param_name); END IF; INSERT INTO MGMT_JOB_PARAMETER ( job_id, execution_id, parameter_name, parameter_type, encrypted, vector_value, created_at_submit) VALUES (p_job_id, MGMT_JOB_ENGINE.NO_EXECUTION, p_job_params(i).param_name, p_job_params(i).param_type, l_encrypted, encrypt_vector(l_encrypted, p_job_params(i).vector_value), 1); END IF; END LOOP; END; -- Insert all the schema entries common to jobs and CAs PROCEDURE insert_job(p_job_name IN VARCHAR2, p_job_owner IN VARCHAR2, p_is_library IN NUMBER, p_job_target_type VARCHAR2, p_description IN VARCHAR2, p_job_type IN VARCHAR2, p_job_params IN MGMT_JOB_PARAM_LIST, p_schedule MGMT_JOB_SCHEDULE_RECORD, p_job_creds MGMT_JOB_CRED_ARRAY, p_notify_states SMP_EMD_INTEGER_ARRAY, p_execution_timeout NUMBER, p_max_target_list_index NUMBER, p_system_job NUMBER, p_is_corrective_action NUMBER, p_ca_scope NUMBER, p_ca_target_guid RAW, p_ca_template_guid RAW, p_job_id_out OUT RAW) IS l_is_corrective_action NUMBER := 1; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_major_version MGMT_JOB_TYPE_INFO.major_version%TYPE; l_schedule_id MGMT_JOB_SCHEDULE.schedule_id%TYPE; l_editable MGMT_JOB_TYPE_INFO.editable%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_creds_set NUMBER; BEGIN -- Get the job type id and the most recent version of the job type -- Also validates the job type MGMT_JOB_ENGINE.get_max_versions(p_job_type, l_major_version, l_job_type_id); IF p_is_library=1 THEN -- Only editable job types can be saved to lib SELECT editable INTO l_editable FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; IF l_editable != 1 THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAM_ERR, 'Job type is not editable: cannot save to library'); END IF; END IF; IF p_schedule IS NOT NULL THEN -- Insert the schedule INSERT INTO MGMT_JOB_SCHEDULE(frequency_code, start_time, end_time, start_grace_period, execution_hours, execution_minutes, interval, months, days, timezone_info, timezone_target_index, timezone_offset, timezone_region) VALUES (p_schedule.frequency_code, p_schedule.start_time, p_schedule.end_time, p_schedule.start_grace_period, p_schedule.execution_hours, p_schedule.execution_minutes, p_schedule.interval, p_schedule.months, p_schedule.days, p_schedule.timezone_info, p_schedule.timezone_target_index, p_schedule.timezone_offset, p_schedule.timezone_region) RETURNING schedule_id INTO l_schedule_id; END IF; -- Insert the job itself into the job table INSERT into MGMT_JOB ( job_name, job_owner, is_library, job_description, job_type, job_type_major_version, schedule_id, execution_timeout, max_target_list_index, system_job, target_type, is_corrective_action) VALUES (upper(p_job_name), upper(p_job_owner), p_is_library, p_description, p_job_type, l_major_version, l_schedule_id, p_execution_timeout, p_max_target_list_index, p_system_job, p_job_target_type, p_is_corrective_action) RETURNING job_id INTO p_job_id_out; IF p_is_corrective_action=1 THEN -- Add the CA entry INSERT INTO MGMT_CORRECTIVE_ACTION (job_id, ca_scope, ca_target_guid, ca_template_guid, ca_reference_count) VALUES (p_job_id_out, p_ca_scope, p_ca_target_guid, p_ca_template_guid, 0); MGMT_USER.grant_ca_privs(p_job_id_out, p_ca_scope, p_ca_target_guid, p_ca_template_guid, p_job_owner); ELSE -- Grant the owner FULL on the job, so that he can see the job MGMT_USER.grant_full_job_to_owner(p_job_id_out); END IF; -- Insert the parameters for the job IF p_job_params IS NOT NULL THEN insert_job_parameters(p_job_id_out, l_job_type_id, p_job_params); END IF; -- Insert overridden job credentials, if provided. IF p_job_creds IS NOT NULL THEN MGMT_CREDENTIAL.set_job_credentials(p_job_id_out, p_job_creds); END IF; -- Insert notify states IF p_notify_states IS NOT NULL AND p_notify_states.COUNT > 0 THEN FOR i IN 1..p_notify_states.COUNT LOOP INSERT INTO MGMT_JOB_NOTIFY_STATES(job_id, notify_state) VALUES (p_job_id_out, p_notify_states(i)); END LOOP; END IF; -- For multitask corrective actions, compute credential set info IF p_is_corrective_action=1 AND p_ca_scope=MGMT_CA.CA_SCOPE_TARGET THEN SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN compute_mtjob_cred_info(p_job_id_out, NO_EXECUTION, l_job_type_id); ELSE -- Process all the credential entries that the CA needs -- (this is needed to later mark the CA as broken) l_creds_set := compute_cred_info(p_job_id_out, NO_EXECUTION, null, 1, 0); l_creds_set := compute_cred_info(p_job_id_out, NO_EXECUTION, null, 0, 0); END IF; END IF; END; -- Restart Job/CA execution FUNCTION restart_job_execution(p_exec_id IN RAW) RETURN RAW IS l_num_restarted_execs INTEGER := 0; l_num_deleted_targets INTEGER; l_execution_status INTEGER; l_job_id RAW(16); l_target_list_index INTEGER; l_new_exec_id RAW(16); l_queue_id RAW(16); l_queue_name MGMT_JOB_QUEUES.queue_name%TYPE; BEGIN IF EMDW_LOG.p_is_debug_set THEN EMDW_LOG.debug('Attempting restart of execution:' || p_exec_id, MODULE_NAME); END IF; -- get execution info SELECT status, job_id, queue_id, target_list_index INTO l_execution_status, l_job_id, l_queue_id, l_target_list_index FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id = p_exec_id AND status != DELETE_PENDING_STATUS; check_modify_job(l_job_id, false); -- Check if the execution's targets are valid SELECT COUNT(*) INTO l_num_deleted_targets FROM MGMT_JOB_EXT_TARGETS ex WHERE execution_id = p_exec_id AND NOT EXISTS (SELECT 1 FROM MGMT_TARGETS WHERE target_guid = ex.target_guid); IF l_num_deleted_targets > 0 THEN raise_application_error(MGMT_GLOBAL.RESTART_DELETED_TARGETS_ERR, 'Cannot restart execution: one or more targets have been deleted'); END IF; IF NOT MGMT_JOB_ENGINE.is_restartable(l_job_id) THEN raise_application_error(MGMT_GLOBAL.RESTART_INVALID_ERR, 'Cannot restart execution: job type marked non-restartable'); END IF; IF l_execution_status != ABORTED_STATUS AND l_execution_status != FAILED_STATUS THEN raise_application_error(MGMT_GLOBAL.RESTART_INVALID_JOB_ERR, 'The specified job execution is not failed or stopped'); END IF; -- check if this execution has already been restarted before SELECT COUNT(1) INTO l_num_restarted_execs FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id <> source_execution_id AND source_execution_id=p_exec_id; IF l_num_restarted_execs > 0 THEN raise_application_error(MGMT_GLOBAL.RESTART_RESTART_FAILED_JOB_ERR, 'The specified job execution has already been restarted'); END IF; IF l_queue_id IS NOT NULL THEN BEGIN SELECT queue_name INTO l_queue_name FROM MGMT_JOB_QUEUES WHERE queue_id=l_queue_id; EXCEPTION WHEN NO_DATA_FOUND THEN l_queue_name := null; END; END IF; l_new_exec_id := schedule_execution(l_job_id, l_queue_name, null, p_exec_id, null, l_target_list_index, null, null); UPDATE MGMT_JOB SET job_status= JOB_STATUS_ACTIVE WHERE job_id=l_job_id; RETURN l_new_exec_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.RESTART_INVALID_JOB_ERR, 'The specified job execution does not exist or is not failed or stopped'); END; -- This function return execution id for the corrective action using job_id -- Which is the job id for target scoped corrective action,execution parameters -- which allow the execution to receive parameters from violation, severity GUID --, simultaneous_actions and other_job_id as input attributes. FUNCTION insert_ca_execution(p_job_id IN RAW, p_exec_params IN MGMT_JOB_PARAM_LIST, p_severity_guid IN RAW, p_simultaneous_actions NUMBER, p_other_job_id IN RAW) RETURN RAW IS l_execution_id RAW(16); l_target_guid RAW(16); l_ca_exec_count NUMBER; l_corrective_action_scope MGMT_CORRECTIVE_ACTION.ca_scope%TYPE; l_job_name MGMT_JOB.job_name%TYPE; l_ca_scope MGMT_CORRECTIVE_ACTION.ca_scope%TYPE; l_broken MGMT_JOB.broken%TYPE; l_other_job_name MGMT_JOB.job_name%TYPE; BEGIN -- Check for not null of job id EM_CHECK.check_not_null(p_job_id, 'Job Id'); -- Check whether This job_id is a target scoped corrective action's ID BEGIN SELECT job_name, ca_scope, ca_target_guid, broken INTO l_job_name, l_ca_scope, l_target_guid, l_broken FROM MGMT_CORRECTIVE_ACTION ca, MGMT_JOB j WHERE j.job_id=p_job_id AND j.job_id=ca.job_id AND j.job_status != JOB_STATUS_DELETE_PENDING; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Invalid corrective action'); END; IF l_ca_scope!=MGMT_CA.CA_SCOPE_TARGET THEN EMD_SCHEMA.add_severity_annotation(p_severity_guid, 'Execution creation failed for CA ' || l_job_name || ': the CA is not a target-scoped CA', v_annotation_type => 'CORRECTIVE_ACTION' ); raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'This is not a target scoped corrective action'); END IF; IF l_broken !=0 THEN EMD_SCHEMA.add_severity_annotation(p_severity_guid, 'Execution creation for Corrective Action ' || l_job_name || ' failed: the CA is currently marked broken. Please visit the Broken Corrective Action pages for more details.', v_annotation_type => 'CORRECTIVE_ACTION'); raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Execution cannot be created for credential disabled CA'); END IF; IF p_simultaneous_actions=1 THEN SELECT COUNT(*) INTO l_ca_exec_count FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_job_id AND status IN (SCHEDULED_STATUS, EXECUTING_STATUS); -- We didn't specify well the semantics of the simultaneous_actions flag, -- and the UI treats 1 as "Allow only one corrective action for this -- metric to run at any given time", that is, *prevent* simultaneous -- executions of the CAs on the PolicyConfig. IF l_ca_exec_count > 0 THEN EMD_SCHEMA.add_severity_annotation(p_severity_guid, 'Corrective Action '||l_job_name||' not run since there is an existing running execution', v_annotation_type => 'CORRECTIVE_ACTION'); -- This is not an error, so do not throw an exception RETURN null; END IF; IF p_other_job_id IS NOT NULL THEN SELECT COUNT(*) INTO l_ca_exec_count FROM MGMT_JOB_EXEC_SUMMARY WHERE job_id=p_other_job_id AND status IN (SCHEDULED_STATUS, EXECUTING_STATUS); IF l_ca_exec_count > 0 THEN BEGIN SELECT job_name INTO l_other_job_name FROM MGMT_JOB WHERE job_id=p_other_job_id; EXCEPTION -- Unlikely, but the other CA may have run and deleted -- by the user WHEN NO_DATA_FOUND THEN NULL; END; EMD_SCHEMA.add_severity_annotation(p_severity_guid, 'Another Corrective Action '||l_job_name||' not run since there is an existing running execution of CA ' || l_other_job_name, v_annotation_type => 'CORRECTIVE_ACTION'); -- Do not raise an exception as this is expected behavior RETURN null; END IF; END IF; END IF; l_execution_id:= schedule_execution(p_job_id => p_job_id, p_queue_name => null, p_execution_id => null, p_source_exec_id => null, p_schedule => null, p_target_list_index=>1, p_target_guid =>l_target_guid, p_triggering_severity => p_severity_guid); -- Add parameters from the violation update_job_parameters(p_job_id, l_execution_id, p_exec_params, false, false, true); --Add a record in violation's annotation list for the successful -- creation of Execution EMD_SCHEMA.add_severity_annotation(p_severity_guid, 'Execution created for Corrective Action '|| l_job_name, v_annotation_type => 'CORRECTIVE_ACTION'); RETURN l_execution_id; END; -- In the ca insertion case, we can't update the notifications system in -- the MGMT_JOB_EXEC_SUMMARY trigger job_summ_ins_trigger2, as we would for -- job inserts, or job or CA updates. This is because we need access to -- both the new rows inserted into MGMT_JOB_EXEC_SUMMARY and MGMT_VIOLATION -- to do the update, and we'd be running in stacked triggers on both tables, -- which doesn't allow us this access. So, instead, after inserting to -- MGMT_JOB_EXEC_SUMMARY (we've exited that trigger, but are still in the -- context of the em_violation_checks trigger on mgmt_violations), we select -- the just-inserted execution and use the passed-in violation, and update -- notifications. PROCEDURE insert_ca_state_change(p_job_id IN RAW, p_execution_id IN RAW, p_violation IN MGMT_VIOLATIONS%ROWTYPE) IS l_change_guid RAW(16) := SYS_GUID(); l_now DATE := SYSDATE; l_exec_summary MGMT_JOB_EXEC_SUMMARY%ROWTYPE; l_change MGMT_JOB_STATE_CHANGES%ROWTYPE; l_source_type NUMBER := EMD_NOTIFICATION.METRIC_CA_STATE_CHANGE; BEGIN IF p_violation.violation_type = MGMT_GLOBAL.G_SEVERITY_TYPE_POLICY THEN l_source_type := EMD_NOTIFICATION.POLICY_CA_STATE_CHANGE; END IF; SELECT * INTO l_exec_summary FROM MGMT_JOB_EXEC_SUMMARY WHERE execution_id = p_execution_id; INSERT INTO MGMT_JOB_STATE_CHANGES(state_change_guid, job_id, execution_id, step_id, logged, occurred, newstate, status_bucket, type, violation_guid) VALUES (l_change_guid, p_job_id, p_execution_id, -1, l_now, l_now, l_exec_summary.status, l_exec_summary.status_bucket, l_source_type, p_violation.violation_guid); -- Add the state change to the notification queue for stand alone mode IF (EMD_MAINTENANCE.IS_CENTRAL_MODE = 0) THEN l_change.state_change_guid := l_change_guid; l_change.job_id := p_job_id; l_change.execution_id := p_execution_id; l_change.step_id := -1; l_change.logged := l_now; l_change.occurred := l_now; l_change.newstate := l_exec_summary.status; l_change.status_bucket := l_exec_summary.status_bucket; l_change.type := l_source_type; l_change.violation_guid := p_violation.violation_guid; EMD_NOTIFICATION.QUEUE_METRIC_NOTIFICATIONS(p_violation, l_change); END IF; END; FUNCTION get_ca_params(p_violation IN MGMT_VIOLATIONS%ROWTYPE) RETURN MGMT_JOB_PARAM_LIST IS l_params MGMT_JOB_PARAM_LIST := MGMT_JOB_PARAM_LIST(); n INTEGER := 0; l_severity_name VARCHAR2(32) := '0'; l_target_type MGMT_TARGETS.target_type%TYPE; l_type_meta_ver MGMT_TARGETS.type_meta_ver%TYPE; l_category_prop_1 MGMT_TARGETS.category_prop_1%TYPE; l_category_prop_2 MGMT_TARGETS.category_prop_2%TYPE; l_category_prop_3 MGMT_TARGETS.category_prop_3%TYPE; l_category_prop_4 MGMT_TARGETS.category_prop_4%TYPE; l_category_prop_5 MGMT_TARGETS.category_prop_5%TYPE; l_policy_name MGMT_POLICIES.policy_name%TYPE; l_metric_guid MGMT_POLICIES.metric_guid%TYPE; l_column_label MGMT_METRICS.column_label%TYPE; l_metric_name MGMT_METRICS.metric_name%TYPE; l_metric_column MGMT_METRICS.metric_column%TYPE; l_metric_type MGMT_METRICS.metric_type%TYPE; l_key_column MGMT_METRICS.key_column%TYPE; l_value1 MGMT_METRICS_COMPOSITE_KEYS.key_part1_value%TYPE; l_value2 MGMT_METRICS_COMPOSITE_KEYS.key_part2_value%TYPE; l_value3 MGMT_METRICS_COMPOSITE_KEYS.key_part3_value%TYPE; l_value4 MGMT_METRICS_COMPOSITE_KEYS.key_part4_value%TYPE; l_value5 MGMT_METRICS_COMPOSITE_KEYS.key_part5_value%TYPE; l_column1 MGMT_METRICS_COMPOSITE_KEYS.key_part1_value%TYPE; l_column2 MGMT_METRICS_COMPOSITE_KEYS.key_part2_value%TYPE; l_column3 MGMT_METRICS_COMPOSITE_KEYS.key_part3_value%TYPE; l_column4 MGMT_METRICS_COMPOSITE_KEYS.key_part4_value%TYPE; l_column5 MGMT_METRICS_COMPOSITE_KEYS.key_part5_value%TYPE; l_ctxt_names SMP_EMD_STRING_ARRAY; l_ctxt_types SMP_EMD_INTEGER_ARRAY; l_ctxt_values SMP_EMD_INTEGER_ARRAY; l_ctxt_str_values SMP_EMD_STRING_ARRAY; BEGIN IF p_violation.violation_level = MGMT_GLOBAL.G_SEVERITY_CRITICAL THEN l_severity_name := 'Critical'; ELSIF p_violation.violation_level = MGMT_GLOBAL.G_SEVERITY_WARNING THEN l_severity_name := 'Warning'; ELSIF p_violation.violation_level = MGMT_GLOBAL.G_SEVERITY_INFORMATIONAL THEN l_severity_name := 'Informational'; END IF; n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('severity', PARAM_TYPE_SCALAR, l_severity_name, null); n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('timestamp', PARAM_TYPE_SCALAR, to_char(p_violation.collection_timestamp, 'DD-MON-YY HH24:MI'), null); BEGIN SELECT target_type, type_meta_ver, category_prop_1, category_prop_2, category_prop_3, category_prop_4, category_prop_5 INTO l_target_type, l_type_meta_ver, l_category_prop_1, l_category_prop_2, l_category_prop_3, l_category_prop_4, l_category_prop_5 FROM mgmt_targets WHERE target_guid = p_violation.target_guid; EXCEPTION WHEN NO_DATA_FOUND THEN return l_params; END; IF p_violation.violation_type = MGMT_GLOBAL.G_SEVERITY_TYPE_POLICY THEN BEGIN SELECT p.policy_name, p.metric_guid INTO l_policy_name, l_metric_guid FROM mgmt_policies p WHERE policy_guid = p_violation.policy_guid; EXCEPTION WHEN NO_DATA_FOUND THEN return l_params; END; ELSE l_metric_guid := p_violation.policy_guid; END IF; BEGIN SELECT m.column_label, m.metric_name, metric_column, metric_type,key_column INTO l_column_label, l_metric_name, l_metric_column, l_metric_type, l_key_column FROM mgmt_metrics m WHERE metric_guid = l_metric_guid AND type_meta_ver = l_type_meta_ver AND (category_prop_1 = l_category_prop_1 OR category_prop_1 = ' ') AND (category_prop_2 = l_category_prop_2 OR category_prop_2 = ' ') AND (category_prop_3 = l_category_prop_3 OR category_prop_3 = ' ') AND (category_prop_4 = l_category_prop_4 OR category_prop_4 = ' ') AND (category_prop_5 = l_category_prop_5 OR category_prop_5 = ' ') AND rownum = 1; EXCEPTION WHEN NO_DATA_FOUND THEN return l_params; END; IF l_column_label IS NOT NULL AND l_column_label != ' ' THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('metric', PARAM_TYPE_SCALAR, l_column_label, null); END IF; IF l_metric_name IS NOT NULL AND l_metric_name != ' ' THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('metric_internal_name', PARAM_TYPE_SCALAR, l_metric_name, null); END IF; IF l_metric_column IS NOT NULL AND l_metric_column != ' ' THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('metric_column_internal_name', PARAM_TYPE_SCALAR, l_metric_column, null); END IF; IF l_policy_name IS NOT NULL AND l_policy_name != ' ' THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('policy', PARAM_TYPE_SCALAR, l_policy_name, null); END IF; IF l_metric_type = MGMT_GLOBAL.G_METRIC_TYPE_NUMBER THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('metric_value', PARAM_TYPE_SCALAR, p_violation.value, null); ELSIF l_metric_type = MGMT_GLOBAL.G_METRIC_TYPE_STRING THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('metric_value', PARAM_TYPE_SCALAR, p_violation.string_value, null); END IF; BEGIN -- Check it this is a composite key SELECT key_part1_value, key_part2_value, key_part3_value, key_part4_value, key_part5_value INTO l_value1, l_value2, l_value3, l_value4, l_value5 FROM MGMT_METRICS_COMPOSITE_KEYS WHERE composite_key = p_violation.key_value AND target_guid = p_violation.target_guid; EXCEPTION WHEN NO_DATA_FOUND THEN --no composite key, so it's either a single column or none l_value1 := p_violation.key_value; END; BEGIN -- Check it this is a composite key SELECT key_part1_value, key_part2_value, key_part3_value, key_part4_value, key_part5_value INTO l_column1, l_column2, l_column3, l_column4, l_column5 FROM MGMT_METRICS_COMPOSITE_KEYS WHERE composite_key = l_key_column AND target_guid = p_violation.target_guid; EXCEPTION WHEN NO_DATA_FOUND THEN --no composite key, so it's either a single column or none l_column1 := l_key_column; END; IF l_column1 IS NOT NULL AND l_value1 IS NOT NULL THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('key_value_' || l_column1, PARAM_TYPE_SCALAR, l_value1, null); END IF; IF l_column2 IS NOT NULL AND l_value2 IS NOT NULL THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('key_value_' || l_column2, PARAM_TYPE_SCALAR, l_value2, null); END IF; IF l_column3 IS NOT NULL AND l_value3 IS NOT NULL THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('key_value_' || l_column3, PARAM_TYPE_SCALAR, l_value3, null); END IF; IF l_column4 IS NOT NULL AND l_value4 IS NOT NULL THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('key_value_' || l_column4, PARAM_TYPE_SCALAR, l_value4, null); END IF; IF l_column5 IS NOT NULL AND l_value5 IS NOT NULL THEN n := n + 1; l_params.extend(1); l_params(n) := MGMT_JOB_PARAM_RECORD('key_value_' || l_column5, PARAM_TYPE_SCALAR, l_value5, null); END IF; -- Violation context SELECT column_name, column_type, column_value, column_str_value BULK COLLECT INTO l_ctxt_names, l_ctxt_types, l_ctxt_values, l_ctxt_str_values FROM MGMT_VIOLATION_CONTEXT WHERE target_guid = p_violation.target_guid AND policy_guid = p_violation.policy_guid AND key_value = p_violation.key_value AND collection_timestamp = p_violation.collection_timestamp; IF l_ctxt_names IS NOT NULL THEN FOR i IN 1..l_ctxt_names.COUNT LOOP n := n + 1; l_params.extend(1); IF l_ctxt_types(i) = MGMT_GLOBAL.G_METRIC_TYPE_STRING THEN l_params(n) := MGMT_JOB_PARAM_RECORD('context_value_' || l_ctxt_names(i), PARAM_TYPE_SCALAR, l_ctxt_str_values(i), null); ELSE l_params(n) := MGMT_JOB_PARAM_RECORD('context_value_' || l_ctxt_names(i), PARAM_TYPE_SCALAR, l_ctxt_values(i), null); END IF; END LOOP; END IF; return l_params; END; -- Obtain and trigger the appropriate corrective action when -- a severity arrives PROCEDURE trigger_ca(p_cfg_target_guid IN RAW, p_cfg_policy_guid IN RAW, p_cfg_coll_name IN VARCHAR2, p_cfg_key_value IN VARCHAR2, p_cfg_key_oper IN NUMBER, p_severity_guid IN RAW, p_violation_level IN NUMBER, p_violation IN MGMT_VIOLATIONS%ROWTYPE) IS l_job_id RAW(16):=NULL; l_crit_job_id RAW(16):=NULL; l_warn_job_id RAW(16):=NULL; l_info_job_id RAW(16):=NULL; l_other_job_id RAW(16):=NULL; l_simultaneous_actions MGMT_POLICY_ASSOC_CFG.simultaneous_actions%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_execution_id RAW(16); BEGIN BEGIN SELECT crit_action_job_id, warn_action_job_id, info_action_job_id, simultaneous_actions INTO l_crit_job_id, l_warn_job_id, l_info_job_id, l_simultaneous_actions FROM MGMT_POLICY_ASSOC_CFG WHERE object_guid = p_cfg_target_guid AND policy_guid = p_cfg_policy_guid AND coll_name = p_cfg_coll_name AND key_value = p_cfg_key_value AND key_operator = p_cfg_key_oper; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.SEVERITY_CFG_MISSING_ERR, 'Severity has missing configuration'); END; IF p_violation_level = MGMT_GLOBAL.G_SEVERITY_CRITICAL THEN l_job_id:=l_crit_job_id; l_other_job_id:=l_warn_job_id; ELSIF p_violation_level = MGMT_GLOBAL.G_SEVERITY_WARNING THEN l_job_id:=l_warn_job_id; l_other_job_id:=l_crit_job_id; ELSIF p_violation_level = MGMT_GLOBAL.G_SEVERITY_INFORMATIONAL THEN l_job_id:=l_info_job_id; l_other_job_id:=null; END IF; IF l_job_id IS NULL THEN RETURN; END IF; l_job_params := get_ca_params(p_violation); l_execution_id:= insert_ca_execution(l_job_id, l_job_params, p_severity_guid, l_simultaneous_actions, l_other_job_id); IF l_execution_id IS NOT NULL THEN insert_ca_state_change(l_job_id, l_execution_id, p_violation); END IF; END; -- The following Procedure takes the input argruments from mgmt_severity -- row while inserting a new severity and trigger a corrective action -- associated with the target. PROCEDURE trigger_ca_for_violation(p_object_guid IN RAW, p_policy_guid IN RAW, p_key_value IN VARCHAR2, p_severity_code IN NUMBER, p_severity_guid IN RAW, p_violation IN MGMT_VIOLATIONS%ROWTYPE, p_cfg_coll_name IN VARCHAR2, p_cfg_key_value IN VARCHAR2, p_cfg_key_operator IN NUMBER) IS BEGIN trigger_ca(p_object_guid, p_policy_guid, p_cfg_coll_name, p_cfg_key_value, p_cfg_key_operator, p_severity_guid, p_severity_code, p_violation); END; PROCEDURE get_multi_task_job_info(p_job_id RAW, p_job_name_out OUT VARCHAR2, p_job_owner_out OUT VARCHAR2, p_description_out OUT VARCHAR2, p_job_type_out OUT VARCHAR2, p_is_single_target OUT NUMBER, p_is_library_out OUT NUMBER, p_is_ca_out OUT NUMBER, p_targets_out OUT SYS_REFCURSOR, p_schedule_out OUT MGMT_JOB_SCHEDULE_RECORD, p_notify_states_out OUT SYS_REFCURSOR, p_access_list_out OUT SYS_REFCURSOR) IS BEGIN IF p_job_id IS NULL THEN RETURN; END IF; -- Job info BEGIN SELECT job_name, job_owner, job_description, job_type, is_library, is_corrective_action INTO p_job_name_out, p_job_owner_out, p_description_out, p_job_type_out, p_is_library_out, p_is_ca_out FROM MGMT_JOB WHERE job_id=p_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified job does not exist'); END; -- Job targets OPEN p_targets_out FOR SELECT t.target_name, t.target_type FROM MGMT_TARGETS t, MGMT_JOB_TARGET jt WHERE jt.target_guid = t.target_guid AND jt.job_id = p_job_id AND jt.execution_id = MGMT_JOB_ENGINE.NO_EXECUTION; -- Schedule IF p_is_ca_out = 0 THEN SELECT MGMT_JOB_SCHEDULE_RECORD(frequency_code, start_time, end_time, start_grace_period, execution_hours, execution_minutes, interval, months, days, timezone_info, timezone_target_index, timezone_offset, timezone_region) INTO p_schedule_out FROM MGMT_JOB_SCHEDULE s, MGMT_JOB j WHERE s.schedule_id=j.schedule_id AND j.job_id=p_job_id; ELSE p_schedule_out := null; END IF; -- Notify states OPEN p_notify_states_out FOR SELECT notify_state FROM MGMT_JOB_NOTIFY_STATES WHERE job_id = p_job_id; -- Job access information MGMT_JOBS.get_job_grants(p_job_id, p_job_owner_out, p_access_list_out); END get_multi_task_job_info; PROCEDURE get_multi_task_job_info(p_job_name VARCHAR2, p_job_owner VARCHAR2, p_description_out OUT VARCHAR2, p_job_type_out OUT VARCHAR2, p_is_single_target OUT NUMBER, p_is_library_out OUT NUMBER, p_is_ca_out OUT NUMBER, p_targets_out OUT SYS_REFCURSOR, p_schedule_out OUT MGMT_JOB_SCHEDULE_RECORD, p_notify_states_out OUT SYS_REFCURSOR, p_access_list_out OUT SYS_REFCURSOR) IS l_job_id MGMT_JOB.job_id%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; BEGIN IF p_job_name IS NULL OR p_job_owner IS NULL THEN return; END IF; -- Job info SELECT job_id, job_description, job_type, is_library, is_corrective_action INTO l_job_id, p_description_out, p_job_type_out, p_is_library_out, p_is_ca_out FROM MGMT_JOB WHERE job_name = p_job_name AND job_owner = p_job_owner; -- Job targets OPEN p_targets_out FOR SELECT t.target_name, t.target_type FROM MGMT_TARGETS t, MGMT_JOB_TARGET jt WHERE jt.target_guid = t.target_guid AND jt.job_id = l_job_id AND jt.execution_id = MGMT_JOB_ENGINE.NO_EXECUTION; -- Schedule IF p_is_ca_out = 0 THEN SELECT MGMT_JOB_SCHEDULE_RECORD(frequency_code, start_time, end_time, start_grace_period, execution_hours, execution_minutes, interval, months, days, timezone_info, timezone_target_index, timezone_offset, timezone_region) INTO p_schedule_out FROM MGMT_JOB_SCHEDULE s, MGMT_JOB j WHERE s.schedule_id=j.schedule_id AND j.job_id=l_job_id; ELSE p_schedule_out := null; END IF; -- Notify states OPEN p_notify_states_out FOR SELECT notify_state FROM MGMT_JOB_NOTIFY_STATES WHERE job_id = l_job_id; -- Job access information -- Note. l_job_owner's value is ignored. it is only declared -- because get_job_grants needs this out parameter. MGMT_JOBS.get_job_grants(l_job_id, l_job_owner, p_access_list_out); END get_multi_task_job_info; -- Get information about the jobtype underlying a MultiTaskJob. PROCEDURE get_multi_task_job_type_info(p_job_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_job_type IN OUT VARCHAR2, p_job_type_info_out OUT SYS_REFCURSOR, p_job_type_execplan_out OUT SYS_REFCURSOR) IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_id MGMT_JOB.job_id%TYPE := p_job_id; BEGIN IF l_job_id IS NULL THEN SELECT job_id INTO l_job_id FROM MGMT_JOB WHERE job_name=p_job_name AND job_owner=p_job_owner; END IF; l_job_type_id := get_job_type_id(l_job_id); SELECT job_type INTO p_job_type FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=l_job_type_id; -- job type info OPEN p_job_type_info_out FOR SELECT job_type, version, job_type_id, major_version, minor_version1, minor_version2, agent_bound, single_target, default_target_type, suspendable, editable, restartable, job_type_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_job_type_id; -- job type exec plan OPEN p_job_type_execplan_out FOR SELECT level, step_name, step_type, nested_job_type, nested_job_target_type, incoming_edge_type, origin_step_name, origin_step_type, iterate_param, stepset_name, stepset_status, command_name, all_targets, flattened_targets FROM MGMT_JOB_EXECPLAN START WITH (job_type_id=l_job_type_id AND origin_step_name is NULL) CONNECT BY PRIOR step_name = origin_step_name AND PRIOR step_type = origin_step_type AND job_type_id=l_job_type_id ORDER SIBLINGS by decode(incoming_edge_type, ETYPE_SERIAL_BEGIN, ETYPE_SERIAL_BEGIN, ETYPE_SERIAL_END, ETYPE_ITERATIVEP_BEGIN, ETYPE_SWITCH_BEGIN, ETYPE_SERIAL_END, ETYPE_PARALLEL_BEGIN, ETYPE_SWITCH_BEGIN, ETYPE_ITERATIVES_BEGIN, ETYPE_PARALLEL_BEGIN, ETYPE_ITERATIVEP_BEGIN, ETYPE_ITERATIVES_BEGIN, ETYPE_SUCCESSOF_END, ETYPE_SUCCESSOF_END, ETYPE_FAILUREOF_END, ETYPE_FAILUREOF_END, ETYPE_ABORTOF_END, ETYPE_FAILUREOF_END); END get_multi_task_job_type_info; -- Get the target names and types for a nested job (task) -- within a MultiTaskJob. This is used for single execution -- MultiTaskJobs and the target names and types are found -- in certain job level parameters. PROCEDURE get_nested_job_targets(p_job_id RAW, p_job_name VARCHAR2, p_job_owner VARCHAR2, p_job_type VARCHAR2, p_nested_job_name VARCHAR2, p_target_names_out OUT MGMT_JOB_VECTOR_PARAMS, p_target_types_out OUT MGMT_JOB_VECTOR_PARAMS) IS l_target_names_param MGMT_JOB_PARAMETER.parameter_name%TYPE := p_nested_job_name || '_target_names'; l_target_types_param MGMT_JOB_PARAMETER.parameter_name%TYPE := p_nested_job_name || '_target_types'; l_temp_target_names_out MGMT_JOB_VECTOR_PARAMS := NULL; l_temp_target_types_out MGMT_JOB_VECTOR_PARAMS := NULL; l_target_record MGMT_JOB_TARGET_RECORD := NULL; l_target_list MGMT_JOB_TARGET_LIST := NULL; l_job_id MGMT_JOB.job_id%TYPE := p_job_id; BEGIN IF l_job_id IS NULL THEN SELECT job_id INTO l_job_id FROM MGMT_JOB WHERE job_name = p_job_name AND job_owner = p_job_owner; END IF; BEGIN SELECT vector_value INTO l_temp_target_names_out FROM MGMT_JOB_PARAMETER WHERE job_id = l_job_id AND execution_id = NO_EXECUTION AND parameter_name = l_target_names_param; EXCEPTION WHEN NO_DATA_FOUND THEN l_temp_target_names_out := NULL; END; BEGIN SELECT vector_value INTO l_temp_target_types_out FROM MGMT_JOB_PARAMETER WHERE job_id = l_job_id AND execution_id = NO_EXECUTION AND parameter_name = l_target_types_param; EXCEPTION WHEN NO_DATA_FOUND THEN l_temp_target_types_out := NULL; END; --join with MGMT_TARGETS to enforce VPD. IF l_temp_target_names_out IS NOT NULL AND l_temp_target_types_out IS NOT NULL THEN l_target_list := MGMT_JOB_TARGET_LIST(); FOR i in 1..l_temp_target_names_out.COUNT LOOP l_target_record := MGMT_JOB_TARGET_RECORD(l_temp_target_names_out(i), l_temp_target_types_out(i)); l_target_list.extend(1); l_target_list(i) := l_target_record; END LOOP; SELECT t.target_name, t.target_type BULK COLLECT INTO p_target_names_out, p_target_types_out FROM MGMT_TARGETS t, TABLE(CAST(l_target_list AS MGMT_JOB_TARGET_LIST)) njt WHERE t.target_name = njt.target_name AND t.target_type = njt.target_type; END IF; END get_nested_job_targets; -- -- Clone a multitask job type and return the cloned job type -- -- This is used when submitting a multitask job from the -- library, as well as when copying a template-scoped -- corrective action to a copy, and from the copy to a -- target. -- Note that this routine does not perform a complete clone -- of a job type: for example, the securityInfo and lockInfo -- sections are not currently copied. It only clones the -- job type elements that we currently use for multitask jobs. -- FUNCTION clone_multitask_job_type(p_job_id RAW) RETURN VARCHAR2 IS l_source_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_source_id MGMT_JOB_PARAM_SOURCE.source_id%TYPE; l_target_name MGMT_TARGETS.target_name%TYPE; l_credentials MGMT_CRED_RECORD := null; l_cred_rows MGMT_CRED_ROW_ARRAY := MGMT_CRED_ROW_ARRAY(); l_job_creds MGMT_JOB_CRED_ARRAY := MGMT_JOB_CRED_ARRAY(); l_job_type MGMT_JOB_TYPE_INFO.job_type%TYPE := SYS_GUID(); l_dest_large_value_id RAW(16); l_dest_large_value_clob CLOB; BEGIN l_source_job_type_id := get_job_type_id(p_job_id); l_job_type_id := SYS_GUID(); -- job type info INSERT INTO MGMT_JOB_TYPE_INFO(job_type, version, job_type_id, major_version, minor_version1, minor_version2, agent_bound, single_target, default_target_type, suspendable, editable, restartable, job_type_category) SELECT l_job_type, '1.0', l_job_type_id, 1, 0, 1, agent_bound, single_target, default_target_type, suspendable, editable, restartable, job_type_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id = l_source_job_type_id; -- job type exec plan INSERT INTO MGMT_JOB_EXECPLAN(job_type_id, step_name, step_type, nested_job_type, nested_job_target_type, incoming_edge_type, origin_step_name, origin_step_type, iterate_param, num_children, stepset_name, stepset_status, command_name, restart_mode, all_params, all_targets, halt_on_failure) SELECT l_job_type_id, step_name, step_type, nested_job_type, nested_job_target_type, incoming_edge_type, origin_step_name, origin_step_type, iterate_param, num_children, stepset_name, stepset_status, command_name, restart_mode, all_params, all_targets, halt_on_failure FROM MGMT_JOB_EXECPLAN WHERE job_type_id=l_source_job_type_id; -- job step params INSERT INTO MGMT_JOB_STEP_PARAMS(job_type_id, step_name, param_name, parameter_type, encrypted, scalar_value, vector_value, value_of) SELECT l_job_type_id, step_name, param_name, parameter_type, encrypted, scalar_value, vector_value, value_of FROM MGMT_JOB_STEP_PARAMS WHERE job_type_id=l_source_job_type_id AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR); -- Copy over large parameters FOR crec IN (SELECT param_name, lp.param_value, step_name FROM MGMT_JOB_STEP_PARAMS p, MGMT_JOB_LARGE_PARAMS lp WHERE job_type_id=l_source_job_type_id AND parameter_type=PARAM_TYPE_LARGE AND p.large_value=lp.param_id) LOOP BEGIN INSERT INTO MGMT_JOB_STEP_PARAMS(job_type_id, step_name, encrypted, param_name, parameter_type, large_value) VALUES (l_job_type_id, crec.step_name, 0, crec.param_name, PARAM_TYPE_LARGE, SYS_GUID()) RETURNING large_value INTO l_dest_large_value_id; INSERT INTO MGMT_JOB_LARGE_PARAMS(param_id, param_value) VALUES (l_dest_large_value_id, empty_clob()) RETURNING param_value INTO l_dest_large_value_clob; END; IF DBMS_LOB.getlength(crec.param_value) > 0 THEN DBMS_LOB.copy(l_dest_large_value_clob, crec.param_value, DBMS_LOB.getlength(crec.param_value)); END IF; END LOOP; -- Single-target types INSERT INTO MGMT_JOB_SINGLE_TARGET_TYPES(job_type_id, single_target_type) SELECT l_job_type_id, single_target_type FROM MGMT_JOB_SINGLE_TARGET_TYPES WHERE job_type_id=l_source_job_type_id; -- Nested job targets INSERT INTO MGMT_NESTED_JOB_TARGETS(job_type_id, step_name, step_type, target_name, target_type) SELECT l_job_type_id, step_name, step_type, target_name, target_type FROM MGMT_NESTED_JOB_TARGETS WHERE job_type_id=l_source_job_type_id; l_job_creds.extend(1); -- Nested job credentials FOR crec IN (SELECT nested_job_name, target_name, target_type, container_location, credential_set_name, credential_guid FROM MGMT_NESTED_JOB_CRED_INFO WHERE job_type_id=l_source_job_type_id) LOOP l_target_name := crec.target_name; IF l_credentials IS NOT NULL THEN l_cred_rows.DELETE; END IF; SELECT MGMT_CRED_ROW_RECORD(credential_set_column, decode(credential_value, null, null, decrypt(credential_value))) BULK COLLECT INTO l_cred_rows FROM MGMT_CREDENTIALS2 WHERE credential_guid=crec.credential_guid; l_credentials := MGMT_CRED_RECORD.NEW(null, crec.credential_set_name, l_cred_rows); l_job_creds(1) := MGMT_JOB_CRED_RECORD.NEW(l_target_name, crec.target_type, crec.container_location, l_credentials); -- Set nested job credentials for this job type MGMT_CREDENTIAL.set_nested_job_cred_info(l_job_type_id, crec.nested_job_name, l_job_creds); END LOOP; -- Clone all the parameter sources FOR crec IN (SELECT source_id, source_type FROM MGMT_JOB_PARAM_SOURCE WHERE job_type_id=l_job_type_id) LOOP l_source_id := SYS_GUID(); INSERT INTO MGMT_JOB_PARAM_SOURCE(source_id, job_type_id, apply_at_submission, apply_on_retry, step_name, step_type, source_type, source_index, source_data, override_user, required, parameter_names) SELECT l_source_id, l_job_type_id, apply_at_submission, apply_on_retry, step_name, step_type, source_type, source_index, source_data, override_user, required, parameter_names FROM MGMT_JOB_PARAM_SOURCE WHERE source_id=crec.source_id; -- Clone all the source param tables IF crec.source_type=SOURCE_TYPE_SQL THEN INSERT INTO MGMT_JOB_SQL_PARAMS(source_id, vector_params, out_proc, out_param_type) SELECT l_source_id, vector_params, out_proc, out_param_type FROM MGMT_JOB_SQL_PARAMS WHERE source_id=crec.source_id; ELSIF crec.source_type=SOURCE_TYPE_CRED THEN INSERT INTO MGMT_JOB_CRED_PARAMS(source_id, credential_set_name, credential_set_target_type, base_cred_type_name, base_cred_type_target_type, base_cred_type_columns, credential_columns, credential_columns_param, target_names, target_types, container_paths, target_names_param, target_types_param, container_paths_param, set_override) SELECT l_source_id, credential_set_name, credential_set_target_type, base_cred_type_name, base_cred_type_target_type, base_cred_type_columns, credential_columns, credential_columns_param, target_names, target_types, container_paths, target_names_param, target_types_param, container_paths_param,set_override FROM MGMT_JOB_CRED_PARAMS WHERE source_id=crec.source_id; ELSIF crec.source_type=SOURCE_TYPE_PROP THEN INSERT INTO MGMT_JOB_PROP_PARAMS (source_id, property_names, property_names_param, target_names, target_types, target_names_param, target_types_param) SELECT l_source_id, property_names, property_names_param, target_names, target_types, target_names_param, target_types_param FROM MGMT_JOB_PROP_PARAMS WHERE source_id=crec.source_id; ELSIF crec.source_type=SOURCE_TYPE_USER THEN INSERT INTO MGMT_JOB_USER_PARAMS (source_id, target_name_params, target_type_params) SELECT l_source_id, target_name_params, target_type_params FROM MGMT_JOB_USER_PARAMS WHERE source_id=crec.source_id; ELSIF crec.source_type=SOURCE_TYPE_INLINE OR crec.source_type=SOURCE_TYPE_CHECKVALUES THEN INSERT INTO MGMT_JOB_VALUE_PARAMS(source_id, param_values, action) SELECT l_source_id, param_values, action FROM MGMT_JOB_VALUE_PARAMS WHERE source_id=crec.source_id; ELSIF crec.source_type=SOURCE_TYPE_SUBSTVALUES THEN INSERT INTO MGMT_JOB_SUBST_PARAMS(source_id, source_params) SELECT l_source_id, source_params FROM MGMT_JOB_SUBST_PARAMS WHERE source_id=crec.source_id; END IF; END LOOP; RETURN l_job_type; END clone_multitask_job_type; -- This procedure creates the template copy scoped -- CA using the template scoped CA PROCEDURE create_template_copy_ca ( p_template_copy_guid IN RAW, p_template_guid IN VARCHAR2, p_ca_name IN VARCHAR2, p_ca_creds IN MGMT_JOB_CRED_ARRAY DEFAULT NULL, p_job_id OUT RAW ) IS l_job_id RAW(16); l_target_type MGMT_JOB.target_type%TYPE; l_description MGMT_JOB.job_description%TYPE; l_job_type MGMT_JOB.job_type%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_lock_handle VARCHAR2(256); l_lock_status NUMBER; BEGIN l_lock_handle := mgmt_user.get_read_lock(MGMT_GLOBAL.JOB_CREATE_FAILED_ERR) ; BEGIN SELECT j.job_id, j.target_type, j.job_description, j.job_type INTO l_job_id, l_target_type, l_description, l_job_type FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND c.ca_scope=MGMT_CA.CA_SCOPE_TEMPLATE AND j.is_corrective_action=1 AND j.nested=0 AND c.ca_template_guid=p_template_guid AND j.job_name=p_ca_name; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Template scoped Corrective Action does not exist'); END; -- If the CA is a multitask CA, then we need to clone the job type -- as well SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO i, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id=l_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND i.job_type_id=mv.job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN l_job_type := clone_multitask_job_type(l_job_id); END IF; -- Insert the schema entries for the CA MGMT_JOB_ENGINE.insert_job(p_ca_name, MGMT_USER.GET_CURRENT_EM_USER, 0, l_target_type, l_description, l_job_type, null, null, p_ca_creds, null, null, 1, 0, 1, CA_SCOPE_TEMPLATE_COPY, null, p_template_copy_guid, p_job_id); -- We insert the parameters separately, to account -- for large parameters copy_ca_params(l_job_id, p_job_id); l_lock_status := MGMT_LOCK_UTIL.release_lock(l_lock_handle); EXCEPTION WHEN OTHERS THEN l_lock_status := MGMT_LOCK_UTIL.release_lock(l_lock_handle); RAISE; END; -- This procedure creates the template copy scoped -- CA using the template copy scoped CA PROCEDURE create_temp_cp_ca_from_temp_cp ( p_dest_template_copy_guid IN RAW, p_src_template_copy_guid IN VARCHAR2, p_src_ca_name IN VARCHAR2, p_dest_ca_name IN VARCHAR2, p_ca_creds IN MGMT_JOB_CRED_ARRAY DEFAULT NULL, p_job_id OUT RAW ) IS l_job_id RAW(16); l_target_type MGMT_JOB.target_type%TYPE; l_description MGMT_JOB.job_description%TYPE; l_job_type MGMT_JOB.job_type%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_owner MGMT_JOB.job_owner%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_lock_handle VARCHAR2(256); l_lock_status NUMBER; BEGIN l_lock_handle := mgmt_user.get_read_lock(MGMT_GLOBAL.JOB_CREATE_FAILED_ERR) ; BEGIN SELECT j.job_id, j.target_type, j.job_description, j.job_type, j.job_owner INTO l_job_id, l_target_type, l_description, l_job_type, l_owner FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND c.ca_scope=MGMT_CA.CA_SCOPE_TEMPLATE_COPY AND j.is_corrective_action=1 AND j.nested=0 AND c.ca_template_guid=p_src_template_copy_guid AND j.job_name=p_src_ca_name; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Template copy scoped Corrective Action does not exist'); END; -- If the CA is a multitask CA, then we need to clone the job type -- as well SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO i, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id=l_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND i.job_type_id=mv.job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN l_job_type := clone_multitask_job_type(l_job_id); END IF; -- Insert the schema entries for the CA MGMT_JOB_ENGINE.insert_job(p_dest_ca_name, l_owner, 0, l_target_type, l_description, l_job_type, null, null, p_ca_creds, null, null, 1, 0, 1, CA_SCOPE_TEMPLATE_COPY, null, p_dest_template_copy_guid, p_job_id); -- We insert the parameters separately, to account -- for large parameters copy_ca_params(l_job_id, p_job_id); l_lock_status := MGMT_LOCK_UTIL.release_lock(l_lock_handle); EXCEPTION WHEN OTHERS THEN l_lock_status := MGMT_LOCK_UTIL.release_lock(l_lock_handle); RAISE; END; -- This procedure creates the template copy scoped -- CA using the target scoped CA PROCEDURE create_temp_cp_ca_from_target ( p_template_copy_guid IN RAW, p_target_guid IN VARCHAR2, p_ca_name IN VARCHAR2, p_ca_creds IN MGMT_JOB_CRED_ARRAY DEFAULT NULL, p_job_id OUT RAW ) IS l_job_id RAW(16); l_target_type MGMT_JOB.target_type%TYPE; l_description MGMT_JOB.job_description%TYPE; l_job_type MGMT_JOB.job_type%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_owner MGMT_JOB.job_owner%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_lock_handle VARCHAR2(256); l_lock_status NUMBER; BEGIN l_lock_handle := mgmt_user.get_read_lock(MGMT_GLOBAL.JOB_CREATE_FAILED_ERR) ; BEGIN SELECT j.job_id, j.target_type, j.job_description, j.job_type, j.job_owner INTO l_job_id, l_target_type, l_description, l_job_type, l_owner FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND c.ca_scope=MGMT_CA.CA_SCOPE_TARGET AND j.is_corrective_action=1 AND j.nested=0 AND c.ca_target_guid=p_target_guid AND j.job_name=p_ca_name; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Target scoped Corrective Action does not exist'); END; -- If the CA is a multitask CA, then we need to clone the job type -- as well SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO i, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id=l_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND i.job_type_id=mv.job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN l_job_type := clone_multitask_job_type(l_job_id); END IF; -- Insert the schema entries for the CA MGMT_JOB_ENGINE.insert_job(p_ca_name, l_owner, 0, l_target_type, l_description, l_job_type, null, null, p_ca_creds, null, null, 1, 0, 1, CA_SCOPE_TEMPLATE_COPY, null, p_template_copy_guid, p_job_id); -- We insert the parameters separately, to account -- for large parameters copy_ca_params(l_job_id, p_job_id); l_lock_status := MGMT_LOCK_UTIL.release_lock(l_lock_handle); EXCEPTION WHEN OTHERS THEN l_lock_status := MGMT_LOCK_UTIL.release_lock(l_lock_handle); RAISE; END; -- Creates a target scoped CA using the template-copy scoped CA PROCEDURE create_target_ca_from_template ( p_target_guid IN RAW, p_ca_id IN RAW, p_job_id_out OUT RAW ) IS l_job_id RAW(16); l_description MGMT_JOB.job_description%TYPE; l_job_type MGMT_JOB.job_type%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_ca_creds MGMT_JOB_CRED_ARRAY; l_owner MGMT_JOB.job_owner%TYPE; l_ca_name MGMT_JOB.job_name%TYPE; l_target_name MGMT_TARGETS.target_name%TYPE; l_target_type MGMT_TARGETS.target_type%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; BEGIN BEGIN SELECT target_name, target_type INTO l_target_name, l_target_type FROM MGMT_TARGETS WHERE target_guid=p_target_guid; EXCEPTION WHEN OTHERS THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'The specified target does not exist'); END; BEGIN SELECT j.job_id, j.job_description, j.job_name, j.job_type, j.job_owner INTO l_job_id, l_description, l_ca_name, l_job_type, l_owner FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND j.job_id=p_ca_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Template scoped Corrective Action does not exist'); END; l_ca_creds:= MGMT_CREDENTIAL.get_job_credentials(l_job_id); -- If the CA is a multitask CA, then we need to clone the job type -- as well SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO i, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id=l_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND i.job_type_id=mv.job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN l_job_type := clone_multitask_job_type(l_job_id); END IF; -- Calling the procedure create_target_ca to create new -- target scoped CA from template scoped corrective action MGMT_CA.create_target_ca( p_ca_name => l_ca_name, p_target => l_target_name, p_target_type => l_target_type, p_description => l_description, p_job_type => l_job_type, p_job_params => l_job_params, p_owner => l_owner, p_job_creds => l_ca_creds, p_job_id_out => p_job_id_out); -- We insert the parameters separately, to account -- for large parameters copy_ca_params(l_job_id, p_job_id_out); END; -- Create a new template CA given the specified target CA. PROCEDURE create_templ_ca_from_target_ca(p_template_name VARCHAR2, p_target_type VARCHAR2, p_source_ca_id IN RAW, p_ca_job_id_out OUT RAW) IS l_job_id RAW(16); l_description MGMT_JOB.job_description%TYPE; l_job_type MGMT_JOB.job_type%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_ca_creds MGMT_JOB_CRED_ARRAY; l_owner MGMT_JOB.job_owner%TYPE; l_ca_name MGMT_JOB.job_name%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; BEGIN -- select the target-scoped CA BEGIN SELECT j.job_id, j.job_description, j.job_name, j.job_type, j.job_owner INTO l_job_id, l_description, l_ca_name, l_job_type, l_owner FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND j.job_id=p_source_ca_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Target-type scoped Corrective Action does not exist'); END; -- No overridden credentials for template CAs l_ca_creds:= null; -- If the CA is a multitask CA, then we need to clone the job type -- as well SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO i, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id=l_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND i.job_type_id=mv.job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN l_job_type := clone_multitask_job_type(l_job_id); END IF; -- Calling the procedure create_target_ca to create new -- target scoped CA from template scoped corrective action MGMT_CA.create_template_ca( p_ca_name => l_ca_name, p_template => p_template_name, p_target_type => p_target_type, p_description => l_description, p_job_type => l_job_type, p_job_params => l_job_params, p_owner => l_owner, p_job_creds => l_ca_creds, p_job_id_out => p_ca_job_id_out); -- We insert the parameters separately, to account -- for large parameters copy_ca_params(l_job_id, p_ca_job_id_out); END; -- Creates a target scoped CA from a "default" (target-type-scoped) CA PROCEDURE create_target_ca_from_default ( p_target_type_guid IN RAW, p_target_guid IN RAW, p_source_ca_id IN RAW, p_ca_job_id_out OUT RAW ) IS l_job_id RAW(16); l_description MGMT_JOB.job_description%TYPE; l_job_type MGMT_JOB.job_type%TYPE; l_job_params MGMT_JOB_PARAM_LIST; l_ca_creds MGMT_JOB_CRED_ARRAY; l_ca_name MGMT_JOB.job_name%TYPE; l_target_name MGMT_TARGETS.target_name%TYPE; l_target_type MGMT_TARGETS.target_type%TYPE; l_source_target_type MGMT_TARGETS.target_type%TYPE; l_job_type_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; BEGIN BEGIN SELECT target_type INTO l_source_target_type FROM MGMT_TARGET_TYPES WHERE target_type_guid=p_target_type_guid; EXCEPTION WHEN OTHERS THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'The source target type does not exist'); END; BEGIN SELECT target_name, target_type INTO l_target_name, l_target_type FROM MGMT_TARGETS WHERE target_guid=p_target_guid; EXCEPTION WHEN OTHERS THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'The specified target does not exist'); END; IF l_source_target_type != l_target_type THEN raise_application_error(MGMT_GLOBAL.INVALID_TARGET_ERR, 'Target is not of source target type'); END IF; -- select the target-type scoped CA BEGIN SELECT j.job_id, j.job_description, j.job_name, j.job_type INTO l_job_id, l_description, l_ca_name, l_job_type FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND j.job_id=p_source_ca_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Target-type scoped Corrective Action does not exist'); END; l_ca_creds:= MGMT_CREDENTIAL.get_job_credentials(l_job_id); -- If the CA is a multitask CA, then we need to clone the job type -- as well SELECT job_type_category INTO l_job_type_category FROM MGMT_JOB_TYPE_INFO i, MGMT_JOB j, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id=l_job_id AND j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND i.job_type_id=mv.job_type_id; IF l_job_type_category=JOBTYPE_CATEGORY_HIDDEN THEN l_job_type := clone_multitask_job_type(l_job_id); END IF; -- Calling the procedure create_target_ca to create new -- target scoped CA from template scoped corrective action MGMT_CA.create_target_ca( p_ca_name => l_ca_name, p_target => l_target_name, p_target_type => l_target_type, p_description => l_description, p_job_type => l_job_type, p_job_params => l_job_params, p_owner => MGMT_USER.GET_CURRENT_EM_USER, p_job_creds => l_ca_creds, p_job_id_out => p_ca_job_id_out); -- We insert the parameters separately, to account -- for large parameters copy_ca_params(l_job_id, p_ca_job_id_out); END; -- This function returns the corrective action id -- for corrective action name, scope and owner. -- This is valid only for corrective actions scoped to an object -- (target, template, tempalte copy) FUNCTION get_ca_id(p_object_guid IN RAW, p_ca_name IN VARCHAR2, p_ca_scope IN NUMBER) RETURN RAW IS l_job_id RAW(16); l_target_type MGMT_TARGET_TYPES.target_type%TYPE; BEGIN IF p_ca_scope=MGMT_CA.CA_SCOPE_TARGET THEN SELECT j.job_id INTO l_job_id FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND j.is_corrective_action=1 AND j.nested=0 AND j.job_name = p_ca_name AND c.ca_scope=p_ca_scope AND ca_target_guid=p_object_guid; ELSIF p_ca_scope IN (MGMT_CA.CA_SCOPE_TEMPLATE, MGMT_CA.CA_SCOPE_TEMPLATE_COPY) THEN SELECT j.job_id INTO l_job_id FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id=c.job_id AND j.is_corrective_action=1 AND j.nested=0 AND j.job_name = p_ca_name AND c.ca_scope=p_ca_scope AND ca_template_guid=p_object_guid; ELSIF p_ca_scope=MGMT_CA.CA_SCOPE_TARGET_TYPE THEN -- For target type CAs, first select the target-type name -- based on GUID, then select the CA's ID. SELECT target_type INTO l_target_type FROM MGMT_TARGET_TYPES WHERE target_type_guid = p_object_guid; SELECT j.job_id INTO l_job_id FROM MGMT_JOB j, MGMT_CORRECTIVE_ACTION c WHERE j.job_id = c.job_id AND j.is_corrective_action = 1 AND j.nested=0 AND j.job_name = p_ca_name AND c.ca_scope = p_ca_scope AND j.target_type = l_target_type; ELSE raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, 'Invalid scope for get_ca_id'); END IF; RETURN l_job_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'The specified Corrective Action does not exist'); END; FUNCTION compare_string(string_1 IN VARCHAR, string_2 IN VARCHAR) RETURN BOOLEAN IS BEGIN IF (string_1 is null) AND (string_2 is null) THEN return true; ELSIF (string_1 is null) OR (string_2 is null) THEN return false; ELSE return (string_1 = string_2); END IF; END; FUNCTION are_cas_equivalent(p_job_id_1 IN RAW, p_job_id_2 IN RAW) RETURN BOOLEAN IS l_job_name_1 MGMT_JOB.job_name%TYPE; l_job_type_1 MGMT_JOB.job_type%TYPE; l_target_type_1 MGMT_JOB.target_type%TYPE; l_description_1 MGMT_JOB.job_description%TYPE; l_is_corrective_action_1 MGMT_JOB.is_corrective_action%TYPE; l_job_type_category_1 MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_params_1 MGMT_JOB_PARAM_LIST; l_job_name_2 MGMT_JOB.job_name%TYPE; l_job_type_2 MGMT_JOB.job_type%TYPE; l_target_type_2 MGMT_JOB.target_type%TYPE; l_description_2 MGMT_JOB.job_description%TYPE; l_is_corrective_action_2 MGMT_JOB.is_corrective_action%TYPE; l_job_type_category_2 MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_params_2 MGMT_JOB_PARAM_LIST; BEGIN BEGIN SELECT j.job_name, j.job_type, i.job_type_category, j.target_type, j.job_description, j.is_corrective_action INTO l_job_name_1, l_job_type_1, l_job_type_category_1, l_target_type_1, l_description_1, l_is_corrective_action_1 FROM MGMT_JOB j, MGMT_JOB_TYPE_INFO i, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id = p_job_id_1 AND j.job_type = mv.job_type AND j.job_type_major_version = mv.major_version AND i.job_type_id = mv.job_type_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Corrective Action does not exist'); END; BEGIN SELECT j.job_name, j.job_type, i.job_type_category, j.target_type, j.job_description, j.is_corrective_action INTO l_job_name_2, l_job_type_2, l_job_type_category_2, l_target_type_2, l_description_2, l_is_corrective_action_2 FROM MGMT_JOB j, MGMT_JOB_TYPE_INFO i, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_id = p_job_id_2 AND j.job_type = mv.job_type AND j.job_type_major_version = mv.major_version AND i.job_type_id = mv.job_type_id; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(MGMT_GLOBAL.INVALID_JOB_ERR, 'Corrective Action does not exist'); END; -- non-cas are not equivalent (in the CA sense) IF (l_is_corrective_action_1 = 0) OR (l_is_corrective_action_2 = 0) THEN return false; END IF; -- At least for 10gR2, multitask CAs are never equivalent IF (l_job_type_category_1 = JOBTYPE_CATEGORY_HIDDEN) OR (l_job_type_category_2 = JOBTYPE_CATEGORY_HIDDEN) THEN return false; END IF; -- if names, descriptions, job-types, or target-types are different, -- the CAs are not equivalent IF NOT compare_string(l_job_name_1, l_job_name_2) OR NOT compare_string(l_job_type_1, l_job_type_2) OR NOT compare_string(l_target_type_1, l_target_type_2) OR NOT compare_string (l_description_1, l_description_2) THEN return false; END IF; -- If we haven't excluded the match so far, we have to compare the params SELECT MGMT_JOB_PARAM_RECORD(parameter_name, parameter_type, MGMT_JOB_ENGINE.decrypt_scalar(encrypted, scalar_value), MGMT_JOB_ENGINE.decrypt_vector(encrypted, vector_value)) BULK COLLECT INTO l_params_1 FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id_1 AND execution_id=NO_EXECUTION AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR) ORDER BY parameter_name; SELECT MGMT_JOB_PARAM_RECORD(parameter_name, parameter_type, MGMT_JOB_ENGINE.decrypt_scalar(encrypted, scalar_value), MGMT_JOB_ENGINE.decrypt_vector(encrypted, vector_value)) BULK COLLECT INTO l_params_2 FROM MGMT_JOB_PARAMETER WHERE job_id=p_job_id_2 AND execution_id=NO_EXECUTION AND parameter_type IN (PARAM_TYPE_SCALAR, PARAM_TYPE_VECTOR) ORDER BY parameter_name; -- First, the number of params must be equal IF l_params_1.COUNT != l_params_2.COUNT THEN return false; END IF; -- Next, be sure that each parameter has the same name, type, and value FOR i IN 1..l_params_1.COUNT LOOP IF NOT compare_string(l_params_1(i).param_name, l_params_2(i).param_name) OR (l_params_1(i).param_type != l_params_2(i).param_type) OR ((l_params_1(i).param_type = PARAM_TYPE_SCALAR) AND NOT compare_string(l_params_1(i).scalar_value, l_params_2(i).scalar_value)) THEN return false; END IF; IF (l_params_1(i).param_type = PARAM_TYPE_VECTOR) THEN IF l_params_1(i).vector_value.COUNT != l_params_2(i).vector_value.COUNT THEN return false; END IF; FOR j in 1..l_params_1(i).vector_value.COUNT LOOP IF NOT compare_string(l_params_1(i).vector_value(j), l_params_2(i).vector_value(j)) THEN return false; END IF; END LOOP; END IF; END LOOP; -- If none of these is true, then the two CAs are equivalent return true; END; -- The two procedures below are used when deleting -- rows from MGMT_JOB_TYPE_INFO to workaround the -- mutating trigger error -- "Register" the job type row being deleted PROCEDURE add_deleted_job_type(p_job_type VARCHAR2, p_major_version NUMBER, p_job_type_id RAW) IS l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; BEGIN -- Check whether the entry in MAX_VERSIONS needs to be -- updated SELECT job_type_id INTO l_job_type_id FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=p_job_type AND major_version=p_major_version; IF l_job_type_id != p_job_type_id THEN RETURN; END IF; IF s_job_types IS NULL THEN s_job_types := SMP_EMD_STRING_ARRAY(); s_major_versions := SMP_EMD_INTEGER_ARRAY(); END IF; s_job_types.extend(1); s_major_versions.extend(1); s_job_types(s_job_types.COUNT) := p_job_type; s_major_versions(s_major_versions.COUNT) := p_major_version; END; -- Update the max versions table as a result of -- various deletes PROCEDURE update_max_versions IS l_count NUMBER; l_job_type_id MGMT_JOB_TYPE_INFO.job_type_id%TYPE; l_minor_version1 MGMT_JOB_TYPE_INFO.minor_version1%TYPE; l_minor_version2 MGMT_JOB_TYPE_INFO.minor_version2%TYPE; BEGIN IF s_job_types IS NOT NULL THEN FOR i IN 1..s_job_types.COUNT LOOP SELECT COUNT(1) INTO l_count FROM MGMT_JOB_TYPE_INFO WHERE job_type=s_job_types(i) AND major_version=s_major_versions(i); IF l_count=0 THEN DELETE FROM MGMT_JOB_TYPE_MAX_VERSIONS WHERE job_type=s_job_types(i) AND major_version=s_major_versions(i); ELSE -- Update the information in the max versions table SELECT MAX(minor_version1) INTO l_minor_version1 FROM MGMT_JOB_TYPE_INFO WHERE job_type=s_job_types(i) AND major_version=s_major_versions(i); SELECT MAX(minor_version2) INTO l_minor_version2 FROM MGMT_JOB_TYPE_INFO WHERE job_type=s_job_types(i) AND major_version=s_major_versions(i) AND minor_version1=l_minor_version1; SELECT job_type_id INTO l_job_type_id FROM MGMT_JOB_TYPE_INFO WHERE job_type=s_job_types(i) AND major_version=s_major_versions(i) AND minor_version1=l_minor_version1 AND minor_version2=l_minor_version2; UPDATE MGMT_JOB_TYPE_MAX_VERSIONS SET minor_version1=l_minor_version1, minor_version2=l_minor_version2, job_type_id=l_job_type_id WHERE job_type=s_job_types(i) AND major_version=s_major_versions(i); END IF; END LOOP; s_job_types.DELETE; s_major_versions.DELETE; s_job_types := null; s_major_versions := null; END IF; END; PROCEDURE insert_host_cred_target_types(p_job_type_id RAW) IS l_target_types SMP_EMD_STRING_ARRAY; BEGIN SELECT DISTINCT s.target_type BULK COLLECT INTO l_target_types FROM MGMT_CREDENTIAL_SETS s, MGMT_CREDENTIAL_TYPE_REF r WHERE s.credential_type_name=r.type_name AND s.target_type=r.target_type AND s.target_type_meta_ver=r.target_type_meta_ver AND r.ref_target_type=MGMT_GLOBAL.G_HOST_TARGET_TYPE AND r.ref_type_name=EM_CREDENTIAL.HOST_CREDS; IF l_target_types IS NOT NULL THEN FORALL i IN 1..l_target_types.COUNT INSERT INTO MGMT_JOB_SINGLE_TARGET_TYPES(job_type_id, single_target_type) VALUES (p_job_type_id, l_target_types(i)); END IF; -- Add an entry for the host itself BEGIN INSERT INTO MGMT_JOB_SINGLE_TARGET_TYPES(job_type_id, single_target_type) VALUES (p_job_type_id, MGMT_GLOBAL.G_HOST_TARGET_TYPE); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN NULL; END; END; -- Update the job type definition with the appropriate default NLS -- information. This provides backward compatibility for the old -- NLS mechanism that used generated NLS IDs. PROCEDURE update_job_type_nls_info(p_job_type_id RAW, p_job_type_name VARCHAR2) IS l_nls_bundle MGMT_JOB_TYPE_DISPLAY_INFO.nls_bundle%TYPE; l_nls_id VARCHAR2(32); l_nls_default VARCHAR2(256); BEGIN -- See whether an NLS bundle has been defined for the job type BEGIN SELECT nls_bundle INTO l_nls_bundle FROM MGMT_JOB_TYPE_DISPLAY_INFO WHERE job_type_id=p_job_type_id; EXCEPTION -- If no NLS bundle defined, proceed no further WHEN OTHERS THEN RETURN; END; -- Generate an entry for the job type's NLS info, if not defined SELECT job_type_nlsid, job_type_default INTO l_nls_id, l_nls_default FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=p_job_type_id; IF l_nls_id IS NULL THEN UPDATE MGMT_JOB_TYPE_INFO SET job_type_nlsid=(p_job_type_name || '_' || p_job_type_name), job_type_default=p_job_type_name WHERE job_type_id=p_job_type_id; END IF; -- Generate an NLS entry for each step FOR crec IN (SELECT step_name, step_nlsid, step_default FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND step_type=STEPTYPE_STEP) LOOP IF crec.step_nlsid IS NULL THEN UPDATE MGMT_JOB_EXECPLAN SET step_nlsid=(p_job_type_name || '_' || crec.step_name), step_default=crec.step_name WHERE job_type_id=p_job_type_id AND step_name=crec.step_name; END IF; END LOOP; -- To-do: need to generate default entries for parameters. How?? END; PROCEDURE add_job_callback(p_job_type_id IN RAW, p_callback_type IN NUMBER, p_callback_name IN VARCHAR2) IS l_error_message VARCHAR2(2000); BEGIN IF NOT EM_CHECK.is_valid_signature( p_callback_name, mgmt_short_string_array('NUMBER', 'NUMBER', 'RAW', 'RAW'), l_error_message) THEN raise_application_error(MGMT_GLOBAL.INVALID_PARAMS_ERR, l_error_message); END IF; INSERT INTO MGMT_JOB_CALLBACKS(job_type_id, callback_type, callback_name) VALUES (p_job_type_id, p_callback_type, p_callback_name); END; ----------------------------- CREDENTIAL CALLBACKS ---------------------- -- Resume all suspended credential executions that do not -- have any waiting rows PROCEDURE resume_cred_execs IS l_execs_to_resume MGMT_USER_GUID_ARRAY; l_job_ids MGMT_USER_GUID_ARRAY; l_start_grace_period MGMT_JOB_SCHEDULE.start_grace_period%TYPE; BEGIN SELECT job_id, execution_id BULK COLLECT INTO l_job_ids, l_execs_to_resume FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_CREDS_STATUS AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_CRED_INFO WHERE execution_id=e.execution_id AND credentials_set=0) ORDER BY execution_id; FOR i IN 1..l_execs_to_resume.COUNT LOOP l_start_grace_period := get_start_grace_period(l_job_ids(i)); resume_job_execution(l_execs_to_resume(i), SUSPENDED_CREDS_STATUS, l_start_grace_period); END LOOP; -- Cleanup broken CAs if any UPDATE MGMT_JOB j SET broken=0, broken_reason=null WHERE is_corrective_action=1 AND broken_reason=BROKEN_NO_CREDS AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id=j.job_id AND credentials_set=0); END; -- Suspend all scheduled credential executions are blocked on -- at least one non-available credential set PROCEDURE suspend_cred_execs IS l_execs_to_suspend MGMT_USER_GUID_ARRAY; BEGIN SELECT execution_id BULK COLLECT INTO l_execs_to_suspend FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SCHEDULED_STATUS AND EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_CRED_INFO WHERE execution_id=e.execution_id AND credentials_set=0) ORDER BY execution_id; FOR i IN 1..l_execs_to_suspend.COUNT LOOP suspend_job_execution(l_execs_to_suspend(i), SUSPENDED_CREDS_STATUS, 0); END LOOP; -- Process CAs that may now be broken UPDATE MGMT_JOB j SET broken=1, broken_reason=BROKEN_NO_CREDS WHERE is_corrective_action=1 AND broken=0 AND nested=0 AND EXISTS (SELECT 1 FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id=j.job_id AND credentials_set=0); END; -- Process all applicable suspended/creds executions when container -- credentials are set PROCEDURE container_creds_added(p_target_guid RAW, p_container_location VARCHAR2, p_credential_set_name VARCHAR2, p_target_type VARCHAR2, p_user_name VARCHAR2) IS l_emd_url MGMT_TARGETS.emd_url%TYPE; BEGIN IF p_target_guid IS NOT NULL AND p_container_location IS NOT NULL THEN -- For jobs UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status=SUSPENDED_CREDS_STATUS AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name)) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND nested=0 AND job_owner=UPPER(p_user_name)) ) AND target_guid=p_target_guid AND container_location=p_container_location AND credential_set_name=p_credential_set_name; ELSIF p_target_guid IS NOT NULL AND p_container_location IS NULL THEN -- Affects all containers on that host UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status=SUSPENDED_CREDS_STATUS AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name)) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND nested=0 AND job_owner=UPPER(p_user_name)) ) AND credential_set_name=p_credential_set_name AND target_guid=p_target_guid; ELSE -- Affects all hosts, all containers UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status=SUSPENDED_CREDS_STATUS AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name)) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND nested=0 AND job_owner=UPPER(p_user_name)) ) AND credential_set_name=p_credential_set_name AND target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE target_type=p_target_type); END IF; END; -- Resume all applicable suspended/creds executions when target -- credentials are set PROCEDURE target_creds_added(p_target_guid RAW, p_host_guid RAW, p_credential_set_name VARCHAR2, p_target_type VARCHAR2, p_user_name VARCHAR2) IS l_emd_url MGMT_TARGETS.emd_url%TYPE; BEGIN IF p_target_guid IS NOT NULL THEN -- Resume any execution waiting on credentials for -- that specific target UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status=SUSPENDED_CREDS_STATUS AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name)) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND nested=0 AND job_owner=UPPER(p_user_name)) ) AND target_guid=p_target_guid AND container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER AND credential_set_name=p_credential_set_name; ELSIF p_host_guid IS NOT NULL THEN -- Affects all targets on that host SELECT emd_url INTO l_emd_url FROM MGMT_TARGETS WHERE target_guid=p_host_guid; UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status=SUSPENDED_CREDS_STATUS AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name)) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND nested=0 AND job_owner=UPPER(p_user_name)) ) AND container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER AND credential_set_name=p_credential_set_name AND target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE emd_url=l_emd_url); ELSE -- Affects all targets of that type UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status=SUSPENDED_CREDS_STATUS AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name)) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND nested=0 AND job_owner=UPPER(p_user_name)) ) AND credential_set_name=p_credential_set_name AND target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE target_type=p_target_type); END IF; END; -- Process executions when credentials are set for a specific -- target/container location PROCEDURE override_creds_added(p_job_id RAW, p_target_guid RAW, p_container_location VARCHAR2, p_credential_set_name VARCHAR2, p_set_usage VARCHAR2, p_set_context_type VARCHAR2, p_target_type VARCHAR2) IS l_container_location MGMT_JOB_CREDENTIALS.container_location%TYPE; BEGIN IF p_set_context_type=MGMT_CREDENTIAL.TARGET_SET_CONTEXT_TYPE THEN IF p_target_guid IS NOT NULL THEN UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_CREDS_STATUS AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND job_id=p_job_id) ) AND target_guid=p_target_guid AND container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER AND credential_set_name=p_credential_set_name; ELSE -- Affects all targets UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_CREDS_STATUS AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND job_id=p_job_id) ) AND container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER AND credential_set_name=p_credential_set_name AND target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE target_type=p_target_type); END IF; ELSIF p_set_context_type=MGMT_CREDENTIAL.CONTAINER_SET_CONTEXT_TYPE THEN IF p_target_guid IS NOT NULL AND p_container_location IS NOT NULL THEN UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_CREDS_STATUS AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND job_id=p_job_id) ) AND target_guid=p_target_guid AND container_location=p_container_location AND credential_set_name=p_credential_set_name; ELSIF p_target_guid IS NOT NULL AND p_container_location IS NULL THEN -- Affects all containers on a host UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_CREDS_STATUS AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND job_id=p_job_id) ) AND target_guid=p_target_guid AND credential_set_name=p_credential_set_name; ELSIF p_target_guid IS NULL AND p_container_location IS NULL THEN -- Affects any container on any host UPDATE MGMT_JOB_EXEC_CRED_INFO SET credentials_set=1 WHERE (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status=SUSPENDED_CREDS_STATUS AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB WHERE is_corrective_action=1 AND job_id=p_job_id) ) AND credential_set_name=p_credential_set_name AND target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE target_type=p_target_type); END IF; END IF; END; -- Process all applicable suspended/creds executions when overridden -- creds are set PROCEDURE override_creds_added(p_job_id RAW, p_creds MGMT_JOB_CRED_ARRAY) IS l_usage MGMT_CREDENTIAL_SETS.set_usage%TYPE; l_context_type MGMT_CREDENTIAL_SETS.set_context_type%TYPE; l_target_guid MGMT_TARGETS.target_guid%TYPE; BEGIN -- Loop over the credentials, waking up all executions in the -- process FOR i IN 1..p_creds.COUNT LOOP -- We need to check the actual credential set context type -- and usage to determine whether it is a target or container -- credential set BEGIN SELECT set_usage, set_context_type INTO l_usage, l_context_type FROM MGMT_CREDENTIAL_SETS WHERE target_type=p_creds(i).target_type AND set_name=p_creds(i).credential.credential_set_name AND ROWNUM=1; EXCEPTION WHEN NO_DATA_FOUND THEN MGMT_LOG.log_error(MODULE_NAME, MGMT_GLOBAL.INVALID_CRED_SET_ERR, 'Could not find credential set info for set ' || p_creds(i).credential.credential_set_name); GOTO next_iteration; END; IF l_usage=MGMT_CREDENTIAL.PREFCRED_SET_USAGE THEN IF p_creds(i).target_name IS NOT NULL THEN BEGIN SELECT target_guid INTO l_target_guid FROM MGMT_TARGETS WHERE target_name=p_creds(i).target_name AND target_type=p_creds(i).target_type; EXCEPTION WHEN NO_DATA_FOUND THEN --TODO Log an error here GOTO next_iteration; END; ELSE l_target_guid := null; END IF; override_creds_added(p_job_id, l_target_guid, p_creds(i).container_location, p_creds(i).credential.credential_set_name, l_usage, l_context_type, p_creds(i).target_type); END IF; <> NULL; END LOOP; END; -- Process executions, if any, when job credentials are deleted PROCEDURE override_creds_deleted(p_job_id RAW, p_target_guid RAW, p_container_location VARCHAR2, p_credential_set_name VARCHAR2, p_target_type VARCHAR2) IS l_emd_url MGMT_TARGETS.emd_url%TYPE; l_set_usage MGMT_CREDENTIAL_SETS.set_usage%TYPE; l_set_context_type MGMT_CREDENTIAL_SETS.set_context_type%TYPE; l_job_owner MGMT_JOB.job_owner%TYPE; BEGIN -- We need to figure out the set usage and context type since -- credentials are stored in different tables depending on that SELECT set_usage, set_context_type INTO l_set_usage, l_set_context_type FROM MGMT_CREDENTIAL_SETS WHERE target_type=p_target_type AND set_name=p_credential_set_name AND ROWNUM=1; SELECT job_owner INTO l_job_owner FROM MGMT_JOB WHERE job_id=p_job_id; IF p_target_guid IS NOT NULL THEN IF l_set_context_type=MGMT_CREDENTIAL.TARGET_SET_CONTEXT_TYPE THEN -- Suspend all executions on this job that have at least -- one missing credential set for the specific target UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND target_guid=p_target_guid AND (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS, REASSIGNED_STATUS) AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_id=p_job_id AND is_corrective_action=1) ) AND NOT EXISTS (SELECT 1 FROM MGMT_TARGET_CREDENTIALS WHERE target_guid=ci.target_guid AND credential_set_name=p_credential_set_name AND user_name=UPPER(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS WHERE credential_set_name=p_credential_set_name AND user_name=UPPER(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_HOST_CREDENTIALS hc, MGMT_TARGETS t, MGMT_TARGETS th WHERE hc.host_guid=th.target_guid AND th.emd_url=t.emd_url AND t.target_guid=ci.target_guid AND hc.target_type=p_target_type AND hc.credential_set_name=p_credential_set_name AND hc.user_name=UPPER(l_job_owner)); ELSE -- For container credentials, check whether credentials -- were stored for all homes or a specific home IF p_container_location IS NOT NULL THEN -- Suspend all executions on this job that have at least -- one missing credential set for the specific -- (target, home) combination UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND target_guid=p_target_guid AND container_location=p_container_location AND (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS, REASSIGNED_STATUS) AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_id=p_job_id AND is_corrective_action=1) ) AND NOT EXISTS (SELECT 1 FROM MGMT_CONTAINER_CREDENTIALS WHERE target_guid=ci.target_guid AND container_location=ci.container_location AND credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS WHERE credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)); ELSE -- Suspend all executions on this job that have at least -- one missing credential set for any home on the target UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND target_guid=p_target_guid AND (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS, REASSIGNED_STATUS) AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_id=p_job_id AND is_corrective_action=1) ) AND NOT EXISTS (SELECT 1 FROM MGMT_CONTAINER_CREDENTIALS WHERE target_guid=ci.target_guid AND container_location=ci.container_location AND credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS WHERE credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)); END IF; END IF; ELSE -- NULL target GUID IF l_set_context_type=MGMT_CREDENTIAL.TARGET_SET_CONTEXT_TYPE THEN -- Suspend all executions on this job that have at least -- one missing credential set for any target UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS, REASSIGNED_STATUS) AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_id=p_job_id AND is_corrective_action=1) ) AND NOT EXISTS (SELECT 1 FROM MGMT_TARGET_CREDENTIALS WHERE target_guid=ci.target_guid AND credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS WHERE credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_HOST_CREDENTIALS hc, MGMT_TARGETS t, MGMT_TARGETS th WHERE hc.host_guid=th.target_guid AND th.emd_url=t.emd_url AND t.target_guid=ci.target_guid AND hc.target_type=p_target_type AND hc.credential_set_name=p_credential_set_name AND hc.user_name=UPPER(l_job_owner)); ELSE -- Container credentials were stored for any -- (target, container) combination for this job. -- Suspend all executions on this job that have at least -- one missing credential set for any (target,home) UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND (execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, SUSPENDED_CREDS_STATUS, REASSIGNED_STATUS) AND job_id=p_job_id) OR job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_id=p_job_id AND is_corrective_action=1) ) AND NOT EXISTS (SELECT 1 FROM MGMT_CONTAINER_CREDENTIALS WHERE target_guid=ci.target_guid AND container_location=ci.container_location AND credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS WHERE credential_set_name=p_credential_set_name AND user_name=upper(l_job_owner)); END IF; END IF; END; -- Process any applicable executions when container credentials -- are deleted PROCEDURE container_creds_deleted_ca(p_target_guid RAW, p_container_location VARCHAR2, p_credential_set_name VARCHAR2, p_target_type VARCHAR2, p_user_name VARCHAR2) IS BEGIN IF p_target_guid IS NULL AND p_container_location IS NULL THEN -- Credentials at the enterprise level got deleted -- Suspend all executions for which credentials do not -- exist at the job and target/container level UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_owner=UPPER(p_user_name) AND is_corrective_action=1 AND nested=0 AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND ji.job_type_id=mv.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME AND (njc.container_location IS NULL OR njc.container_location=ci.container_location) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS WHERE job_id=j.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND (container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER OR container_location=ci.container_location) AND credential_set_name=p_credential_set_name AND target_type=p_target_type ) ) AND NOT EXISTS (SELECT 1 FROM MGMT_CONTAINER_CREDENTIALS WHERE target_guid=ci.target_guid AND container_location=ci.container_location AND credential_set_name=p_credential_set_name AND user_name=upper(p_user_name)); ELSIF p_target_guid IS NOT NULL AND p_container_location IS NOT NULL THEN -- Credentials at the (host,container) level got deleted -- Suspend all executions for which credentials do not -- exist at any levels for the specific (host,container,set) -- combination UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE target_guid=p_target_guid AND container_location=p_container_location AND credential_set_name=p_credential_set_name AND job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_owner=UPPER(p_user_name) AND is_corrective_action=1 AND nested=0 AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND ji.job_type_id=mv.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME AND (njc.container_location IS NULL OR njc.container_location=ci.container_location) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS WHERE job_id=j.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND (container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER OR container_location=ci.container_location) AND credential_set_name=p_credential_set_name AND target_type=p_target_type ) ) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS ec WHERE target_type=p_target_type AND credential_set_name=p_credential_set_name AND user_name=UPPER(p_user_name)); END IF; END; -- Process any applicable executions when container credentials -- are deleted PROCEDURE container_creds_deleted(p_target_guid RAW, p_container_location VARCHAR2, p_credential_set_name VARCHAR2, p_target_type VARCHAR2, p_user_name VARCHAR2) IS BEGIN IF p_target_guid IS NULL AND p_container_location IS NULL THEN -- Credentials at the enterprise level got deleted -- Suspend all executions for which credentials do not -- exist at the job and target/container level UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS) AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc WHERE ji.job_type_id=e.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME AND (njc.container_location IS NULL OR njc.container_location=ci.container_location) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS j WHERE j.job_id=e.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND (container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER OR j.container_location=ci.container_location) AND credential_set_name=p_credential_set_name AND target_type=p_target_type) ) AND NOT EXISTS (SELECT 1 FROM MGMT_CONTAINER_CREDENTIALS WHERE target_guid=ci.target_guid AND container_location=ci.container_location AND credential_set_name=p_credential_set_name AND user_name=upper(p_user_name)); ELSIF p_target_guid IS NOT NULL AND p_container_location IS NOT NULL THEN -- Credentials at the (host,container) level got deleted -- Suspend all executions for which credentials do not -- exist at any levels for the specific (host,container,set) -- combination UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE target_guid=p_target_guid AND container_location=p_container_location AND credential_set_name=p_credential_set_name AND execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS) AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc WHERE ji.job_type_id=e.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME AND (njc.container_location IS NULL OR njc.container_location=ci.container_location) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS j WHERE j.job_id=e.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND (container_location=MGMT_CREDENTIAL.DEFAULT_CONTAINER OR j.container_location=ci.container_location) AND credential_set_name=p_credential_set_name AND target_type=p_target_type) ) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS ec WHERE target_type=p_target_type AND credential_set_name=p_credential_set_name AND user_name=UPPER(p_user_name)); END IF; -- Process corrective actions container_creds_deleted_ca(p_target_guid, p_container_location, p_credential_set_name, p_target_type, p_user_name); END; -- Process any applicable CAs when target credentials -- are deleted PROCEDURE target_creds_deleted_ca(p_target_guid RAW, p_target_name VARCHAR2, p_host_guid RAW, p_credential_set_name VARCHAR2, p_target_type VARCHAR2, p_user_name VARCHAR2) IS l_emd_url MGMT_TARGETS.emd_url%TYPE; BEGIN IF p_target_guid IS NULL AND p_host_guid IS NULL THEN -- Credentials at the enterprise level got deleted -- Suspend all executions for which credentials do not -- exist at the target or host level UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_owner=UPPER(p_user_name) AND is_corrective_action=1 AND nested=0 AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND ji.job_type_id=mv.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS WHERE job_id=j.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND credential_set_name=p_credential_set_name AND target_type=p_target_type ) ) AND NOT EXISTS (SELECT 1 FROM MGMT_TARGET_CREDENTIALS WHERE target_guid=ci.target_guid AND credential_set_name=p_credential_set_name AND user_name=upper(p_user_name)) AND NOT EXISTS (SELECT 1 FROM MGMT_HOST_CREDENTIALS hc, MGMT_TARGETS t, MGMT_TARGETS th WHERE hc.host_guid=th.target_guid AND th.emd_url=t.emd_url AND t.target_guid=ci.target_guid AND hc.target_type=p_target_type AND hc.credential_set_name=p_credential_set_name AND hc.user_name=UPPER(p_user_name)); ELSIF p_target_guid IS NOT NULL THEN -- Credentials at the target level got deleted -- Suspend all executions for which credentials do not -- exist at any levels for the specific (target, set) combination UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE target_guid=p_target_guid AND credential_set_name=p_credential_set_name AND job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_owner=UPPER(p_user_name) AND is_corrective_action=1 AND nested=0 AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND ji.job_type_id=mv.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND (njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME OR njc.target_name=p_target_name) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS WHERE job_id=j.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND credential_set_name=p_credential_set_name AND target_type=p_target_type ) ) AND NOT EXISTS (SELECT 1 FROM MGMT_HOST_CREDENTIALS hc, MGMT_TARGETS t, MGMT_TARGETS th WHERE hc.host_guid=th.target_guid AND th.emd_url=t.emd_url AND t.target_guid=p_target_guid AND hc.target_type=p_target_type AND hc.credential_set_name=p_credential_set_name AND hc.user_name=UPPER(p_user_name)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS ec WHERE target_type=p_target_type AND credential_set_name=p_credential_set_name AND user_name=UPPER(p_user_name)); ELSIF p_host_guid IS NOT NULL THEN -- Credentials were removed at host level. Suspend all -- executions on targets on that host, if credentials are not -- set at either the job, enterprise or target levels SELECT emd_url INTO l_emd_url FROM MGMT_TARGETS WHERE target_guid=p_host_guid; UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE emd_url=l_emd_url) AND credential_set_name=p_credential_set_name AND job_id IN (SELECT job_id FROM MGMT_JOB j WHERE job_owner=UPPER(p_user_name) AND is_corrective_action=1 AND nested=0 AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc, MGMT_JOB_TYPE_MAX_VERSIONS mv WHERE j.job_type=mv.job_type AND j.job_type_major_version=mv.major_version AND ji.job_type_id=mv.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND (njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME OR njc.target_name=p_target_name) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS WHERE job_id=j.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND credential_set_name=p_credential_set_name AND target_type=p_target_type ) ) AND NOT EXISTS (SELECT 1 FROM MGMT_TARGET_CREDENTIALS WHERE target_guid=ci.target_guid AND credential_set_name=p_credential_set_name AND user_name=upper(p_user_name)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS ec WHERE target_type=p_target_type AND credential_set_name=p_credential_set_name AND user_name=UPPER(p_user_name)); END IF; END; -- Process any applicable executions when target credentials -- are deleted PROCEDURE target_creds_deleted(p_target_guid RAW, p_host_guid RAW, p_credential_set_name VARCHAR2, p_target_type VARCHAR2, p_user_name VARCHAR2) IS l_emd_url MGMT_TARGETS.emd_url%TYPE; l_target_name MGMT_TARGETS.target_name%TYPE; BEGIN IF p_target_guid IS NULL AND p_host_guid IS NULL THEN -- Credentials at the enterprise level got deleted -- Suspend all executions for which credentials do not -- exist at the target or host level. -- Note this code assumes that override credentials -- for multitask jobs can only apply to all targets, -- not specific targets UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE credential_set_name=p_credential_set_name AND execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS) AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc WHERE ji.job_type_id=e.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS j WHERE j.job_id=e.job_id AND (target_guid=ci.target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND credential_set_name=p_credential_set_name AND target_type=p_target_type) ) AND NOT EXISTS (SELECT 1 FROM MGMT_TARGET_CREDENTIALS WHERE target_guid=ci.target_guid AND credential_set_name=p_credential_set_name AND user_name=upper(p_user_name)) AND NOT EXISTS (SELECT 1 FROM MGMT_HOST_CREDENTIALS hc, MGMT_TARGETS t, MGMT_TARGETS th WHERE hc.host_guid=th.target_guid AND th.emd_url=t.emd_url AND t.target_guid=ci.target_guid AND hc.target_type=p_target_type AND hc.credential_set_name=p_credential_set_name AND hc.user_name=UPPER(p_user_name)); ELSIF p_target_guid IS NOT NULL THEN -- Credentials at the target level got deleted -- Suspend all executions for which credentials do not -- exist at any levels for the specific (target, set) combination SELECT target_name INTO l_target_name FROM MGMT_TARGETS WHERE target_guid=p_target_guid; UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE target_guid=p_target_guid AND credential_set_name=p_credential_set_name AND execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS) AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc WHERE ji.job_type_id=e.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND (njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME OR njc.target_name=l_target_name) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS j WHERE j.job_id=e.job_id AND (target_guid=p_target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND credential_set_name=p_credential_set_name AND target_type=p_target_type) ) AND NOT EXISTS (SELECT 1 FROM MGMT_HOST_CREDENTIALS hc, MGMT_TARGETS t, MGMT_TARGETS th WHERE hc.host_guid=th.target_guid AND th.emd_url=t.emd_url AND t.target_guid=p_target_guid AND hc.target_type=p_target_type AND hc.credential_set_name=p_credential_set_name AND hc.user_name=UPPER(p_user_name)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS ec WHERE target_type=p_target_type AND credential_set_name=p_credential_set_name AND user_name=UPPER(p_user_name)); ELSIF p_host_guid IS NOT NULL THEN -- Credentials were removed at host level. Suspend all -- executions on targets on that host, if credentials are not -- set at either the job, enterprise or target levels SELECT emd_url INTO l_emd_url FROM MGMT_TARGETS WHERE target_guid=p_host_guid; UPDATE MGMT_JOB_EXEC_CRED_INFO ci SET credentials_set=0 WHERE target_guid IN (SELECT target_guid FROM MGMT_TARGETS WHERE emd_url=l_emd_url) AND credential_set_name=p_credential_set_name AND execution_id IN (SELECT execution_id FROM MGMT_JOB_EXEC_SUMMARY e, MGMT_JOB j WHERE status IN (SCHEDULED_STATUS, SUSPENDED_STATUS, SUSPENDED_BLACKOUT_STATUS, AGENTDOWN_STATUS, SUSPENDED_LOCK_STATUS, SUSPENDED_BLACKOUT_STATUS, REASSIGNED_STATUS, SUSPENDED_CREDS_STATUS) AND j.job_id=e.job_id AND j.job_owner=UPPER(p_user_name) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_TYPE_INFO ji, MGMT_NESTED_JOB_CRED_INFO njc WHERE ji.job_type_id=e.job_type_id AND ji.job_type_category=JOBTYPE_CATEGORY_HIDDEN AND ji.job_type_id=njc.job_type_id AND (njc.target_name=MGMT_CREDENTIAL.DEFAULT_TARGET_NAME OR njc.target_name=l_target_name) AND njc.target_type=p_target_type AND njc.credential_set_name=p_credential_set_name AND njc.nested_job_name=ci.task_name ) AND NOT EXISTS (SELECT 1 FROM MGMT_JOB_CREDENTIALS j WHERE j.job_id=e.job_id AND (target_guid=p_target_guid OR target_guid=MGMT_CREDENTIAL.DEFAULT_GUID) AND credential_set_name=p_credential_set_name AND target_type=p_target_type) ) AND NOT EXISTS (SELECT 1 FROM MGMT_TARGET_CREDENTIALS WHERE target_guid=ci.target_guid AND credential_set_name=p_credential_set_name AND user_name=upper(p_user_name)) AND NOT EXISTS (SELECT 1 FROM MGMT_ENTERPRISE_CREDENTIALS ec WHERE target_type=p_target_type AND credential_set_name=p_credential_set_name AND user_name=UPPER(p_user_name)); END IF; -- Process CAs target_creds_deleted_ca(p_target_guid, l_target_name, p_host_guid, p_credential_set_name, p_target_type, p_user_name); END; -- Compute execution-credential information for a MT job PROCEDURE compute_mtjob_cred_info(p_job_id RAW, p_execution_id RAW, p_job_type_id RAW) IS l_task_names SMP_EMD_STRING_ARRAY; l_creds_set NUMBER; BEGIN DELETE FROM MGMT_JOB_EXEC_CRED_INFO WHERE job_id=p_job_id AND execution_id=p_execution_id; -- Go thru all the tasks SELECT step_name BULK COLLECT INTO l_task_names FROM MGMT_JOB_EXECPLAN WHERE job_type_id=p_job_type_id AND step_type=STEPTYPE_JOB; IF l_task_names IS NOT NULL AND l_task_names.COUNT>0 THEN FOR i IN 1..l_task_names.COUNT LOOP IF EMDW_LOG.p_is_info_set THEN EMDW_LOG.info('Processing task ' || l_task_names(i), MODULE_NAME); END IF; -- Run credential sources for each of the tasks, -- updating credential information as we go along l_creds_set := compute_cred_info(p_job_id, p_execution_id, l_task_names(i), 1, 0); l_creds_set := compute_cred_info(p_job_id, p_execution_id, l_task_names(i), 0, 0); END LOOP; END IF; END; ----------------------- END CREDENTIAL CALLBACKS ------------------------ -- Group membership change callback PROCEDURE group_change_callback(p_assoc_def_name VARCHAR2, p_source_target_name VARCHAR2, p_source_target_type VARCHAR2, p_assoc_target_name VARCHAR2, p_assoc_target_type VARCHAR2, p_scope_target_name VARCHAR2, p_scope_target_type VARCHAR2) IS BEGIN -- The container group is the source target handle_membership_change(MGMT_TARGET.get_target_guid(p_source_target_name, p_source_target_type)); END; PROCEDURE insert_single_exec_ca_targets(p_job_name VARCHAR2, p_target VARCHAR2, p_target_type VARCHAR2, p_job_targets MGMT_JOB_TARGET_LIST, p_create NUMBER) IS l_job_id MGMT_JOB.job_id%TYPE; l_target_list_array MGMT_JOB_TARGET_LIST_ARRAY; l_target_list MGMT_JOB_TARGET_LIST := p_job_targets; BEGIN l_job_id := MGMT_CA.get_target_scoped_job_id(p_job_name, p_target, p_target_type); IF l_target_list IS NULL THEN l_target_list := MGMT_JOB_TARGET_LIST(); END IF; l_target_list.extend(1); l_target_list(l_target_list.COUNT) := mgmt_job_target_record(p_target,p_target_type); IF p_create = 1 THEN insert_target_list(l_job_id, l_target_list, 1); ELSE l_target_list_array := MGMT_JOB_TARGET_LIST_ARRAY(l_target_list); upsert_ca_target_lists(l_job_id, l_target_list_array, NULL); END IF; END; PROCEDURE insert_cluster_target_types(p_job_type_id IN RAW) IS l_already_registered NUMBER; l_category MGMT_JOB_TYPE_INFO.job_type_category%TYPE; l_target_types SMP_EMD_STRING_ARRAY; BEGIN -- Don't change the single-target-type list for user job types SELECT job_type_category INTO l_category FROM MGMT_JOB_TYPE_INFO WHERE job_type_id=p_job_type_id; IF l_category != JOBTYPE_CATEGORY_HIDDEN THEN SELECT single_target_type BULK COLLECT INTO l_target_types FROM MGMT_JOB_SINGLE_TARGET_TYPES WHERE job_type_id=p_job_type_id; -- If there's only one target-type, it acts implicitly as the -- default target type (see validate_single_target_list, above). -- Since we may be adding an additional target-type below, we -- first need to make it *explicitly* the default target type. IF l_target_types.count = 1 THEN IF l_target_types(1) != ALL_TARGET_TYPES THEN UPDATE MGMT_JOB_TYPE_INFO SET default_target_type = l_target_types(1) WHERE job_type_id=p_job_type_id AND default_target_type is null; END IF; END IF; FOR crec IN (SELECT tp.target_type cluster_target_type FROM MGMT_TYPE_PROPERTIES tp, MGMT_JOB_SINGLE_TARGET_TYPES jstt WHERE jstt.job_type_id = p_job_type_id AND tp.property_value = jstt.single_target_type AND tp.property_name = MGMT_GLOBAL.G_CLUSTER_MEMBER_TYPE_PROP) LOOP BEGIN SELECT COUNT(*) INTO l_already_registered FROM MGMT_JOB_SINGLE_TARGET_TYPES WHERE job_type_id = p_job_type_id AND single_target_type = crec.cluster_target_type; IF l_already_registered = 0 THEN BEGIN INSERT INTO MGMT_JOB_SINGLE_TARGET_TYPES(job_type_id, single_target_type) VALUES (p_job_type_id, crec.cluster_target_type); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN NULL; END; END IF; END; END LOOP; END IF; END; /** * Decrease the reference count for large parameter values. Called when a * reference to a large param with id p_old_id is removed or changed to p_new_id. * If the reference count is zero after decrement, the row is also removed. */ PROCEDURE decr_large_param_ref_count(p_old_id RAW, p_new_id RAW DEFAULT NULL) IS BEGIN IF p_old_id IS NOT NULL AND (p_new_id IS NULL OR p_old_id != p_new_id) THEN DELETE FROM MGMT_JOB_LARGE_PARAMS WHERE param_id = p_old_id AND reference_count = 1; IF SQL%NOTFOUND THEN UPDATE MGMT_JOB_LARGE_PARAMS SET reference_count = reference_count - 1 WHERE param_id = p_old_id; END IF; END IF; IF p_new_id IS NOT NULL THEN UPDATE MGMT_JOB_LARGE_PARAMS SET reference_count = reference_count + 1 WHERE param_id = p_new_id; END IF; END; END MGMT_JOB_ENGINE; / show errors;