Rem
Rem $Header: emcore/source/oracle/sysman/emdrep/sql/core/latest/ecm/ecm_ct_pkgbody.sql /st_emcore_10.2.0.4.2db11.2/1 2009/06/19 18:17:46 vkhizder Exp $
Rem
Rem ecm_ct_pkgbody.sql
Rem
Rem Copyright (c) 2002, 2009, Oracle and/or its affiliates. 
Rem All rights reserved. 
Rem
Rem    NAME
Rem      ecm_ct_pkgbody.sql - <one-line expansion of the name>
Rem
Rem    DESCRIPTION
Rem      <short description of component this file declares/defines>
Rem
Rem    NOTES
Rem      <other useful comments, qualifications, etc.>
Rem
Rem    MODIFIED   (MM/DD/YY)
Rem     vkhizder   06/18/09 - XbranchMerge vkhizder_bug6503115_10g from
Rem                           st_emcore_10.2.0.1.0
Rem     rrawat     06/26/07 - Bug-5997153
Rem     rrawat     07/23/07 - Backport rrawat_bug-5977153 from main
Rem     agor       09/07/06 - Backport agor_bug-5478815 from
Rem                           st_emcore_10.2.0.1.0
Rem     rrawat     07/11/06 - Backport rrawat_bug-5211702 from main 
Rem     rrawat     06/20/06 - Bug-5211702
Rem     vkhizder   08/09/05 - fixing error handling due to agent fix for bug 
Rem                           4313253 
Rem     mgoodric   07/20/05 - add additional parameter to after_load proc 
Rem     agor       05/18/05 - catch exceptions 
Rem     shuberma   04/21/05 - Modifying handle_orphaned_snapshots to avoid 
Rem                           infinite loop. 
Rem     shuberma   04/15/05 - Remove deletes from post load procedures. 
Rem     akskumar   04/07/05 - copy_snapshot to return saved config guid 
Rem     asaraswa   04/07/05 - modify post-load procedure for host configs to 
Rem                           update the patches column in mgmt_hc_os_summary
Rem     mgoodric   04/06/05 - fix 10.2GC Beta upgrade bug 
Rem     asaraswa   02/25/05 - adding code to populate mgmt_inv_summary 
Rem     agor       02/16/05 - fix 4189156 
Rem     vkhizder   12/03/04 - delete performance improvements 
Rem     mningomb   12/10/04 - Cutover Critical patch advisories policy to 10.2 
Rem                           policy framework 
Rem     vkhizder   11/24/04 - adding comments 
Rem     akskumar   11/10/04 - saving configurations for composite targets 
Rem     shuberma   09/27/04 - Remove call to old policy fw. 
Rem     shuberma   09/03/04 - Adding procedure for running on-demand metrics 
Rem                           that are dependent upon snapshots. 
Rem     vkhizder   07/27/04 - host config loading changes 
Rem     rpinnama   06/12/04 - Remove set ECHO 
Rem     vkhizder   03/03/04 - 
Rem     vkhizder   02/17/04 - making csa clients non-targets 
Rem     vkhizder   12/29/03 - fixing null comparinson 
Rem     mgoodric   10/21/03 - check mgmt_current_metric_errors for errors
Rem     vkhizder   10/17/03 - commenting on effects of after-load procedure 
Rem     shuberma   08/12/03 - Moving policy check until after commit and 
Rem                           delete of old snapshot. THis is because the 
Rem                           policy checker sees the old data 
Rem     vkhizder   07/23/03 - only loader metadata tables/views are relevant 
Rem                           for deletion/copy
Rem     mgoodric   06/13/03 - added COPY_SNAPSHOT
Rem     vkhizder   06/03/03 - handing target deletion
Rem     shuberma   06/02/03 - Adding call to run policy checks on refresh
Rem     vkhizder   05/19/03 - adding oui platform
Rem     vkhizder   03/25/03 - more framework improvements
Rem     mgoodric   03/15/03 - use exceptions to raise errors
Rem     mgoodric   03/14/03 - add PRELOAD_CALLBACK and POSTLOAD_CALLBACK
Rem     agor       01/02/03 - add clonable
Rem     vkhizder   12/18/02 - adding some generic snapshot related procedures
Rem     rpinnama   05/15/02 - rpinnama_reorg_rep_scripts
Rem     rpinnama   05/15/02 - Created
Rem


CREATE OR REPLACE PACKAGE BODY ECM_CT
IS

-- a list of host config snapshot ids
TYPE T_SNAP_GUID_TABLE IS TABLE OF MGMT_ECM_SNAPSHOT.SNAPSHOT_GUID%TYPE;

-- Private constant for the number of retries of snapshot copy operation
-- when snapshot changes while the copy operation is in progress
NUM_OF_SNAPSHOT_COPY_RETRIES CONSTANT NUMBER := 3;

SAVE_COMPOSITE_FAIL constant NUMBER:=0;
SAVE_COMPOSITE_SUC constant NUMBER:=1;
SAVE_COMPOSITE_HOST constant NUMBER:=2;

-- *********************************************************************

PROCEDURE INSERT_SNAPSHOT_INFO (v_type VARCHAR2, v_type_version VARCHAR2,
    v_target_name VARCHAR2, v_target_type VARCHAR2,
    v_display_target_name VARCHAR2, v_display_target_type VARCHAR2,
    v_description VARCHAR2, v_start_timestamp DATE,
    v_elapsed_time NUMBER, v_current VARCHAR2,
    v_collection_error_stream VARCHAR2, v_guid OUT RAW) IS

BEGIN

  INSERT INTO MGMT_ECM_SNAPSHOT (snapshot_type, snapshot_type_version,
      target_name, target_type, display_target_name, display_target_type,
      description, start_timestamp, elapsed_time, is_current,
      collection_error_stream)
  VALUES (v_type, v_type_version,
      v_target_name, v_target_type, v_display_target_name,
      v_display_target_type, v_description, v_start_timestamp,
      v_elapsed_time, v_current, v_collection_error_stream)
  RETURNING snapshot_guid INTO v_guid;

END INSERT_SNAPSHOT_INFO;

-- *********************************************************************

PROCEDURE INSERT_CONTAINER (v_snapshot_guid RAW, v_type VARCHAR2,
    v_name VARCHAR2, v_location VARCHAR2, v_oui_platform NUMBER,
    v_clonable VARCHAR2, v_guid OUT RAW) IS

BEGIN

  INSERT INTO MGMT_INV_CONTAINER (container_type, container_name,
      snapshot_guid, container_location, oui_platform, is_clonable)
  VALUES (v_type, v_name, v_snapshot_guid, v_location,
      v_oui_platform, v_clonable)
  RETURNING container_guid INTO v_guid;

END INSERT_CONTAINER;

-- *********************************************************************

PROCEDURE INSERT_COMPONENT (v_name VARCHAR2, v_version VARCHAR2,
    v_container_guid RAW, v_desription VARCHAR2,
    v_external_name VARCHAR2, v_languages VARCHAR2,
    v_installed_location VARCHAR2, v_installer_version VARCHAR2,
    v_min_deinstaller_version VARCHAR2, v_isTopLevel VARCHAR2,
    v_timestamp STRING, v_guid OUT RAW) IS

BEGIN

      INSERT INTO MGMT_INV_COMPONENT (name, version, container_guid,
             description, external_name, languages, installed_location,
             installer_version, min_deinstaller_version,is_top_level, timestamp)
             VALUES(v_name, v_version, v_container_guid, v_desription,
                   v_external_name, v_languages, v_installed_location,
                   v_installer_version, v_min_deinstaller_version, v_isTopLevel,
                   to_date(v_timestamp,'yyyy-mm-dd hh24:mi:ss'))
  RETURNING component_guid INTO v_guid;

END INSERT_COMPONENT;

-- *********************************************************************

PROCEDURE INSERT_PATCH (v_id VARCHAR2, v_container_guid RAW,
    v_timestamp STRING, 
    v_description VARCHAR2, v_isRollbackable VARCHAR2,
    v_guid OUT RAW) IS

BEGIN

  INSERT INTO MGMT_INV_PATCH (id, container_guid, timestamp,
      description, is_rollbackable)
  VALUES (v_id, v_container_guid, 
          to_date(v_timestamp,'yyyy-mm-dd hh24:mi:ss'),
           v_description, v_isRollbackable)
  RETURNING patch_guid INTO v_guid;

END INSERT_PATCH;

-- *********************************************************************

PROCEDURE INSERT_PATCHSET (v_name VARCHAR2, v_version VARCHAR2,
    v_container_guid RAW, v_description VARCHAR2,
    v_external_name VARCHAR2, v_installer_version VARCHAR2,
    v_min_deinstaller_version VARCHAR2, v_timestamp STRING, 
    v_guid OUT RAW) IS
BEGIN

  INSERT INTO MGMT_INV_PATCHSET (name, version, container_guid, description,
      external_name, installer_version, min_deinstaller_version, timestamp)
  VALUES (v_name, v_version, v_container_guid, v_description,
      v_external_name, v_installer_version,  v_min_deinstaller_version,
      to_date(v_timestamp,'yyyy-mm-dd hh24:mi:ss'))
  RETURNING patchset_guid INTO v_guid;

END INSERT_PATCHSET;

-- *********************************************************************

PROCEDURE INSERT_GEN_SNAPSHOT_INFO (v_snapshot_type VARCHAR2,
    v_target_id VARCHAR2, v_target_name VARCHAR2, v_target_type VARCHAR2,
    v_display_target_name VARCHAR2, v_display_target_type VARCHAR2,
    v_description VARCHAR2, v_start_timestamp DATE,
    v_elapsed_time NUMBER, v_current VARCHAR2,
    v_status VARCHAR2, v_message VARCHAR2, v_creator VARCHAR2,
    v_guid OUT VARCHAR2) IS
BEGIN

  INSERT INTO MGMT_ECM_GEN_SNAPSHOT (snapshot_type, target_guid,
      target_name, target_type, display_target_name, display_target_type,
      description, start_timestamp, elapsed_time, is_current,
      status, message, creator)
  VALUES (v_snapshot_type, HEXTORAW(v_target_id), v_target_name,
      v_target_type, v_display_target_name, v_display_target_type,
      v_description, v_start_timestamp, v_elapsed_time, v_current,
      v_status, v_message, v_creator)
  RETURNING RAWTOHEX(snapshot_guid) INTO v_guid;

END INSERT_GEN_SNAPSHOT_INFO;

-- *********************************************************************

-- use current EM user as snapshot creator
PROCEDURE INSERT_GEN_SNAPSHOT_INFO (v_snapshot_type VARCHAR2,
    v_target_id VARCHAR2, v_target_name VARCHAR2, v_target_type VARCHAR2,
    v_display_target_name VARCHAR2, v_display_target_type VARCHAR2,
    v_description VARCHAR2, v_start_timestamp DATE,
    v_elapsed_time NUMBER, v_current VARCHAR2,
    v_status VARCHAR2, v_message VARCHAR2, v_guid OUT VARCHAR2) IS
BEGIN

  INSERT INTO MGMT_ECM_GEN_SNAPSHOT (snapshot_type, target_guid,
      target_name, target_type, display_target_name, display_target_type,
      description, start_timestamp, elapsed_time, is_current,
      status, message, creator)
  VALUES (v_snapshot_type, HEXTORAW(v_target_id), v_target_name,
      v_target_type, v_display_target_name, v_display_target_type,
      v_description, v_start_timestamp, v_elapsed_time, v_current,
      v_status, v_message, mgmt_user.get_current_em_user())
  RETURNING RAWTOHEX(snapshot_guid) INTO v_guid;

END INSERT_GEN_SNAPSHOT_INFO;

-- *********************************************************************

-- Private
PROCEDURE CT_LOG (v_message IN VARCHAR2) IS

l_index  NUMBER := 1;
l_length NUMBER := 0;

BEGIN

  IF (ECM_CT.G_IS_DEBUG = 1) THEN
    BEGIN
      l_length := LENGTH(v_message);
      WHILE (l_length > 0)
      LOOP
        DBMS_OUTPUT.PUT_LINE (SUBSTR(v_message,l_index,255));
        l_length := l_length - 255;
        l_index := l_index + 255;
      END LOOP;

    -- ignore any errors
    EXCEPTION WHEN OTHERS THEN NULL;
    END;
  END IF;

END CT_LOG;

-- *********************************************************************
/* VK: This method is not used any longer.. for now just commenting it out in 
case we want to revert back to using (a derivative of) this code

-- Private
PROCEDURE FAIL_WITH_STATUS (
    v_code IN NUMBER,
    v_message IN VARCHAR2) IS

l_error_message VARCHAR2(32767) := NULL;

BEGIN

  BEGIN
    IF (v_message IS NOT NULL) THEN
      l_error_message := v_message;
    ELSE
      l_error_message := SQLERRM;
    END IF;

    -- truncate message if >= 4000
    IF (LENGTH(l_error_message) >= ECM_CT.G_MAX_MESSAGE_LEN)
    THEN
      l_error_message := SUBSTR(l_error_message,1,(ECM_CT.G_MAX_MESSAGE_LEN-3)) || '...';
    END IF;

    -- VK: "SNAPSHOT" would have to be registered with 
    -- mgmt_log.register_logging_module before it can be used. Also, 
    -- this "Error in snapshot: Error:" message seems a bit strange.. And 
    -- should the truncation above account for this prefix?
    -- After-load procedure should just throw the exception.. 
    -- I believe the generic loader will log it
    -- MGMT_LOG.LOG_ERROR ('SNAPSHOT', null,
    --                   'Error in snapshot: ' || ' Error: ' || l_error_message);

  -- ignore any errors
  EXCEPTION WHEN OTHERS THEN NULL;
  END;

  raise_application_error (v_code, l_error_message);

END FAIL_WITH_STATUS;
*/

-- *********************************************************************

-- Called by Generic Loader before entering metric collection data
-- to create a snapshot in MGMT_ECM_GEN_SNAPSHOT
--
-- Inputs: target_guid             - guid of collection target
--         collection_name         - snapshot type
--         collection_timestamp    - collection time at target
--         emd_url                 - Agent's URL
--         version                 - snapshot version
--         config_metadata_version - configuration metadata version
--
-- Output: ecm_snapshot_guid       - guid of the new snapshot

PROCEDURE PRELOAD_CALLBACK (
    v_input IN SMP_EMD_NVPAIR_ARRAY,
    v_preLoadOutput OUT SMP_EMD_NVPAIR_ARRAY) IS

-- obtained from v_input
l_target_guid MGMT_TARGETS.TARGET_GUID%TYPE;
l_snapshot_type MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_TYPE%TYPE := NULL;
l_start_timestamp MGMT_ECM_GEN_SNAPSHOT.START_TIMESTAMP%TYPE := SYSDATE;

-- returned in v_preLoadOutput
l_snapshot_guid VARCHAR2(64) := NULL;

-- obtained from mgmt_targets
l_target_name MGMT_TARGETS.TARGET_NAME%TYPE := NULL;
l_target_type MGMT_TARGETS.TARGET_TYPE%TYPE := NULL;
l_display_target_name MGMT_TARGETS.DISPLAY_NAME%TYPE := NULL;
l_display_target_type MGMT_TARGETS.TYPE_DISPLAY_NAME%TYPE := NULL;

-- local variables
l_input_name VARCHAR(256) := NULL;
l_null_description CONSTANT MGMT_ECM_GEN_SNAPSHOT.DESCRIPTION%TYPE := NULL;
l_null_message CONSTANT MGMT_ECM_GEN_SNAPSHOT.MESSAGE%TYPE := NULL;
l_null_elapsed_time CONSTANT MGMT_ECM_GEN_SNAPSHOT.ELAPSED_TIME%TYPE := -1;
l_null_creator CONSTANT MGMT_ECM_GEN_SNAPSHOT.CREATOR%TYPE := NULL;

BEGIN

  FOR i IN 1..v_input.COUNT
  LOOP
    l_input_name := UPPER(v_input(i).name);
    IF (l_input_name = ECM_CT.G_TARGET_GUID) THEN
      l_target_guid := HEXTORAW(v_input(i).value);

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-->          ' || v_input(i).name || ' = ''' || l_target_guid || '''');
      END IF;

    ELSIF (l_input_name = ECM_CT.G_COLLECTION_NAME) THEN
      l_snapshot_type := v_input(i).value;

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-->      ' || v_input(i).name || ' = ''' || l_snapshot_type || '''');
      END IF;

    ELSIF (l_input_name = ECM_CT.G_COLLECTION_TIMESTAMP) THEN
      l_start_timestamp := TO_DATE(v_input(i).value,MGMT_GLOBAL.G_AGENT_DATE_FORMAT);

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('--> ' || v_input(i).name || ' = ''' || l_start_timestamp || '''');
      END IF;

    END IF;
  END LOOP;

  IF (ECM_CT.G_IS_DEBUG = 1) THEN
    CT_LOG('-- Retrieving target data with ''' || l_target_guid || '''');
  END IF;

  BEGIN
    SELECT target_name, target_type, display_name, type_display_name
      INTO l_target_name, l_target_type, l_display_target_name, l_display_target_type
      FROM mgmt_targets
      WHERE (target_guid = l_target_guid);

  -- bail out when target doesn't exist
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      RAISE MGMT_GLOBAL.target_does_not_exist;
  END;

  IF (ECM_CT.G_IS_DEBUG = 1) THEN
    CT_LOG('-- Creating new ''' || l_snapshot_type || ''' snapshot for target ''' || l_target_name || '''');
  END IF;

  INSERT_GEN_SNAPSHOT_INFO (
      l_snapshot_type, RAWTOHEX(l_target_guid),
      l_target_name, l_target_type,
      l_display_target_name, l_display_target_type,
      l_null_description, l_start_timestamp, l_null_elapsed_time,
      ECM_CT.G_IS_TRANSIENT_TYPE, ECM_CT.G_NOT_FOUND_STATUS,
      l_null_message, l_null_creator, l_snapshot_guid);
  COMMIT;

  -- Store the snapshot guid for later use
  v_preLoadOutput := SMP_EMD_NVPAIR_ARRAY();
  v_preLoadOutput.extend(1);
  v_preLoadOutput(1) := SMP_EMD_NVPAIR(ECM_CT.G_ECM_SNAPSHOT_ID,
                                       l_snapshot_guid);

END PRELOAD_CALLBACK;

-- *********************************************************************

-- Called by Generic Loader after entering metric collection data
-- to calculate change history for the snapshot in MGMT_ECM_GEN_SNAPSHOT
--
-- Inputs: target_guid             - guid of collection target
--         collection_name         - snapshot type
--         collection_timestamp    - collection time at target
--         emd_url                 - Agent's URL
--         version                 - snapshot version
--         config_metadata_version - configuration metadata version
--
-- Inputs: ecm_snapshot_guid       - guid of the new snapshot

PROCEDURE POSTLOAD_CALLBACK (
    v_input IN SMP_EMD_NVPAIR_ARRAY,
    v_preLoadOutput IN SMP_EMD_NVPAIR_ARRAY) IS

-- obtained from v_input
l_target_guid MGMT_TARGETS.TARGET_GUID%TYPE;
l_snapshot_type MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_TYPE%TYPE := NULL;
l_start_timestamp MGMT_ECM_GEN_SNAPSHOT.START_TIMESTAMP%TYPE := SYSDATE;

-- obtained from v_preLoadOutput
l_snapshot_guid MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_GUID%TYPE;

-- obtained from mgmt_targets
l_target_name MGMT_TARGETS.TARGET_NAME%TYPE := NULL;
l_target_type MGMT_TARGETS.TARGET_TYPE%TYPE := NULL;
l_display_target_name MGMT_TARGETS.DISPLAY_NAME%TYPE := NULL;
l_display_target_type MGMT_TARGETS.TYPE_DISPLAY_NAME%TYPE := NULL;

-- obtained from mgmt_ecm_snapshot_metadata
l_after_load_proc_name MGMT_ECM_SNAPSHOT_METADATA.AFTER_LOAD_PROC_NAME%TYPE := NULL;
l_history_on MGMT_ECM_SNAPSHOT_METADATA.HISTORY_ON%TYPE := 'N';

-- useful types
TYPE T_NUMBER_TABLE IS TABLE OF NUMBER;
TYPE T_VARCHAR2_TABLE IS TABLE OF VARCHAR2(4000);
TYPE T_GUID_TABLE IS TABLE OF MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_GUID%TYPE;

-- used for collecting metric warnings
l_error_message VARCHAR2(32767) := NULL;
l_metric_error_type T_NUMBER_TABLE;
l_metric_error_message T_VARCHAR2_TABLE;
l_lock_snapshot_list T_GUID_TABLE;

-- used to determine lock conditions
l_lock_guid VARCHAR2(64) := NULL;
l_current_snapshot_guid MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_GUID%TYPE;

-- used to form call to after_load_proc
l_sql VARCHAR2(200) := NULL;
l_metadata_version VARCHAR2(64) := NULL;
l_afterLoadInput SMP_EMD_NVPAIR_ARRAY := NULL;

-- used to detect old-style AFTER_LOAD interface
wrong_number_of_arguments EXCEPTION;
PRAGMA EXCEPTION_INIT(wrong_number_of_arguments, -6553);

-- local variables
l_input_name VARCHAR(256) := NULL;
l_preLoadOutput_name VARCHAR(256) := NULL;
l_null_description CONSTANT MGMT_ECM_GEN_SNAPSHOT.DESCRIPTION%TYPE := NULL;
l_null_message CONSTANT MGMT_ECM_GEN_SNAPSHOT.MESSAGE%TYPE := NULL;
l_null_elapsed_time CONSTANT MGMT_ECM_GEN_SNAPSHOT.ELAPSED_TIME%TYPE := -1;
l_null_creator CONSTANT MGMT_ECM_GEN_SNAPSHOT.CREATOR%TYPE := NULL;

BEGIN
  BEGIN
    FOR i IN 1..v_input.COUNT
    LOOP
      l_input_name := UPPER(v_input(i).name);
      IF (l_input_name = ECM_CT.G_TARGET_GUID) THEN
        l_target_guid := HEXTORAW(v_input(i).value);

        IF (ECM_CT.G_IS_DEBUG = 1) THEN
          CT_LOG('-->          ' || v_input(i).name || ' = ''' || l_target_guid || '''');
        END IF;

      ELSIF (l_input_name = ECM_CT.G_COLLECTION_NAME) THEN
        l_snapshot_type := v_input(i).value;

        IF (ECM_CT.G_IS_DEBUG = 1) THEN
          CT_LOG('-->      ' || v_input(i).name || ' = ''' || l_snapshot_type || '''');
        END IF;

      ELSIF (l_input_name = ECM_CT.G_COLLECTION_TIMESTAMP) THEN
        l_start_timestamp := TO_DATE(v_input(i).value,MGMT_GLOBAL.G_AGENT_DATE_FORMAT);

        IF (ECM_CT.G_IS_DEBUG = 1) THEN
          CT_LOG('--> ' || v_input(i).name || ' = ''' || l_start_timestamp || '''');
        END IF;

      ELSIF (l_input_name = ECM_CT.G_CONFIG_METADATA_VERSION) THEN
        l_metadata_version := v_input(i).value;

        IF (ECM_CT.G_IS_DEBUG = 1) THEN
          CT_LOG('-->      ' || v_input(i).name || ' = ''' || l_metadata_version || '''');
        END IF;

      END IF;
    END LOOP;

    FOR i IN 1..v_preLoadOutput.COUNT
    LOOP
      l_preLoadOutput_name := UPPER(v_preLoadOutput(i).name);
      IF (l_preLoadOutput_name = ECM_CT.G_ECM_SNAPSHOT_ID) THEN
        l_snapshot_guid := HEXTORAW(v_preLoadOutput(i).value);

        IF (ECM_CT.G_IS_DEBUG = 1) THEN
          CT_LOG('-->      ' || v_preLoadOutput(i).name || ' = ''' || l_snapshot_guid || '''');
        END IF;

      END IF;
    END LOOP;

    -- In addition to retrieving target data, the following makes sure
    -- target has not been deleted yet (by checking for l_target_guid).
    -- If ecm handle_target_deletion callback is started before this check, the
    -- following check will see that the target is no longer in mgmt_targets table.
    -- If this check succeeds and target deletion as well as the callback start
    -- after this check, the target guid will be removed from the snapshot row
    -- committed in the preload_callback procedure with the ecm handle_target_deletion
    -- callback procedure (which will see the row due to the prior commit.)
    -- The snapshot itself will be removed as part of delete_orphan_snapshots procedure.

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Retrieving target data with ''' || l_target_guid || '''');
    END IF;

    BEGIN
        SELECT target_name, target_type, display_name, type_display_name
        INTO l_target_name, l_target_type, l_display_target_name, l_display_target_type
            FROM mgmt_targets
            WHERE (target_guid = l_target_guid);

    -- bail out if target doesn't exist
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            RAISE MGMT_GLOBAL.target_does_not_exist;
    END;

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Collecting metric warnings for target_guid ''' || l_target_guid || '''');
    END IF;

    BEGIN
      SELECT metric_error_type, metric_error_message BULK COLLECT
        INTO l_metric_error_type, l_metric_error_message
        FROM mgmt_metric_errors
        WHERE target_guid = l_target_guid
          AND coll_name = l_snapshot_type
          AND collection_timestamp = l_start_timestamp
          AND metric_error_message IS NOT NULL;

      FOR i IN 1..l_metric_error_message.COUNT
      LOOP
        IF (l_metric_error_type(i) = ECM_CT.G_IS_METRIC_ERROR)
        THEN

          IF (ECM_CT.G_IS_DEBUG = 1) THEN
            CT_LOG('-- Found metric errors for target_guid ''' || l_target_guid || '''');
          END IF;

          -- delete snapshot
          UPDATE mgmt_ecm_gen_snapshot
             SET is_current = ECM_CT.G_IS_DELETED_TYPE
           WHERE snapshot_guid = l_snapshot_guid;

          RETURN;
        END IF;

        IF (l_metric_error_message(i) IS NOT NULL)
        THEN
          IF (l_error_message IS NULL) THEN
            l_error_message := l_metric_error_message(i);
          ELSE
            l_error_message := l_error_message || ' ' || CHR(10) || l_metric_error_message(i);
          END IF;
        END IF;
      END LOOP;

      -- truncate message if >= 4000
      IF (LENGTH(l_error_message) >= ECM_CT.G_MAX_MESSAGE_LEN)
      THEN
        l_error_message := SUBSTR(l_error_message,1,(ECM_CT.G_MAX_MESSAGE_LEN-3)) || '...';
      END IF;

      UPDATE mgmt_ecm_gen_snapshot
        SET  message = l_error_message
        WHERE (snapshot_guid = l_snapshot_guid);

    -- ignore no metric errors
    EXCEPTION
      WHEN NO_DATA_FOUND THEN NULL;
    END;

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Finding after_load_proc_name procedure and executing it');
    END IF;

    BEGIN
      SELECT after_load_proc_name
        INTO l_after_load_proc_name
        FROM mgmt_ecm_snapshot_metadata
        WHERE (snapshot_type = l_snapshot_type)
          AND (target_type = l_target_type)
          AND (kind = ECM_CT.G_IS_LOADER);

    -- ignore when no after_load_proc
    EXCEPTION
      -- should we raise exception if there is no metadata? For now, proceed.
      WHEN NO_DATA_FOUND THEN NULL; 
    END;

    IF (l_after_load_proc_name IS NOT NULL) THEN
    
      -- Store the metadata version for integrators to use
      l_afterLoadInput := SMP_EMD_NVPAIR_ARRAY();
      l_afterLoadInput.extend(1);
      l_afterLoadInput(1) := SMP_EMD_NVPAIR(ECM_CT.G_CONFIG_METADATA_VERSION,
                                       l_metadata_version);

      l_sql := 'CALL ' || EM_CHECK.QUALIFIED_SQL_NAME(l_after_load_proc_name) || '(:snapshot_guid,:afterLoadInput)';

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-- Executing ' || l_sql || ' with ''' || l_snapshot_guid || '''');
      END IF;

      BEGIN
          EXECUTE IMMEDIATE l_sql USING l_snapshot_guid, l_afterLoadInput;
      EXCEPTION
        -- Retry callback if new parameter is not currently supported by callback
        WHEN wrong_number_of_arguments THEN
          -- ORA-06553: PLS-306: wrong number or types of arguments in call to 'AFTER_LOAD'
          l_sql := 'CALL ' || EM_CHECK.QUALIFIED_SQL_NAME(l_after_load_proc_name) || '(:snapshot_guid)';
          EXECUTE IMMEDIATE l_sql USING l_snapshot_guid;
      END;

      -- Re-obtaining various target/snapshot information in case it was
      -- changed by the after-load procedure. CSA performs such changes.
      -- Generally, integrators should NOT perform such changes.
      SELECT target_guid, target_name, target_type, 
             display_target_name, display_target_type, snapshot_type
        INTO l_target_guid, l_target_name, l_target_type, 
             l_display_target_name, l_display_target_type, l_snapshot_type 
        FROM mgmt_ecm_gen_snapshot
       WHERE snapshot_guid = l_snapshot_guid;
            
      -- Note: start_timestamp and elapsed_time fields could also be changed by
      -- after-load procedure but we do not reacquire them above as they are
      -- not used below.

    END IF;

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Trying to lock current snapshot');
    END IF;

    BEGIN
      SELECT snapshot_guid INTO l_current_snapshot_guid
        FROM mgmt_ecm_gen_snapshot
        WHERE (snapshot_type = l_snapshot_type)
          AND (target_name = l_target_name)
          AND (target_type = l_target_type)
          AND (is_current = ECM_CT.G_IS_CURRENT_TYPE)
        FOR UPDATE OF is_current;

    -- ignore when no current snapshot
    EXCEPTION
      WHEN NO_DATA_FOUND THEN NULL;
    END;

    -- Lock on new row if no lock obtained on current snapshot
    IF (l_current_snapshot_guid IS NULL) THEN

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-- Inserting locking snapshot with new guid');
      END IF;

      INSERT_GEN_SNAPSHOT_INFO (
          l_snapshot_type, NULL,
          l_target_name, l_target_type,
          l_display_target_name, l_display_target_type,
          l_null_description, SYSDATE, l_null_elapsed_time,
          ECM_CT.G_IS_LOCKED_TYPE, ECM_CT.G_NOT_FOUND_STATUS,
          l_null_message, l_null_creator, l_lock_guid);
      COMMIT;

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-- Locking snapshot with ''' || l_lock_guid || '''');
      END IF;

      BEGIN
        SELECT snapshot_guid BULK COLLECT INTO l_lock_snapshot_list
          FROM mgmt_ecm_gen_snapshot
          WHERE (snapshot_type = l_snapshot_type)
            AND (target_name = l_target_name)
            AND (target_type = l_target_type)
            AND (is_current = ECM_CT.G_IS_LOCKED_TYPE)
          FOR UPDATE OF is_current;

      -- bail out with error when lock is missing
      EXCEPTION
        WHEN NO_DATA_FOUND THEN
          raise_application_error (MGMT_GLOBAL.SNAPSHOT_LOCK_ERR, 
                                   'Could not find just created lock row');
      END;

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-- Retrying lock of current snapshot');
      END IF;

      -- We are now locked on L snapshot. If a new current 
      -- snapshot appears meanwhile, let's try to lock on
      -- it as well and get its guid.
      BEGIN
        SELECT snapshot_guid INTO l_current_snapshot_guid
          FROM mgmt_ecm_gen_snapshot
          WHERE (snapshot_type = l_snapshot_type)
            AND (target_name = l_target_name)
            AND (target_type = l_target_type)
            AND (is_current = ECM_CT.G_IS_CURRENT_TYPE)
          FOR UPDATE OF is_current;

      -- ignore if we still can't get the lock
      EXCEPTION
        WHEN NO_DATA_FOUND THEN NULL;
      END;

    END IF;

    BEGIN
      SELECT history_on
        INTO l_history_on
        FROM mgmt_ecm_snapshot_metadata
       WHERE snapshot_type = l_snapshot_type
         AND target_type = l_target_type
         AND kind = ECM_CT.G_IS_PRESENTATION;
    EXCEPTION
      -- for now don't worry if metadata is missing
      WHEN NO_DATA_FOUND THEN NULL; 
    END;
    
    IF (l_history_on = 'Y') THEN

      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-- Calculating history for snapshot_guid ''' || l_snapshot_guid || '''');
      END IF;

      MGMT_ECM_SNAPSHOT_PKG.DO_AND_SAVE_HISTORY (
                                  l_snapshot_type,
                                  l_target_type,
                                  l_snapshot_guid,
                                  l_current_snapshot_guid);
    END IF;

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Making snapshot current for snapshot_guid ''' || l_snapshot_guid || '''');
    END IF;

    -- delete old current snapshot
    IF (l_current_snapshot_guid IS NOT NULL) THEN
      UPDATE mgmt_ecm_gen_snapshot
         SET is_current = ECM_CT.G_IS_DELETED_TYPE
       WHERE snapshot_guid = l_current_snapshot_guid;
    END IF;

    -- make new snapshot into current snapshot
    UPDATE mgmt_ecm_gen_snapshot
      SET is_current = ECM_CT.G_IS_CURRENT_TYPE,
          status = ECM_CT.G_COLLECTED_STATUS
      WHERE snapshot_guid = l_snapshot_guid;

    COMMIT;

  -- bail out after deleting temporary snapshot
  EXCEPTION
    WHEN OTHERS THEN
        UPDATE mgmt_ecm_gen_snapshot
           SET is_current = ECM_CT.G_IS_DELETED_TYPE
         WHERE snapshot_guid = l_snapshot_guid;

        RAISE;
  END;

  IF (l_lock_guid IS NOT NULL) THEN

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Deleting snapshot with ''' || l_lock_guid || '''');
    END IF;

    BEGIN
-- Due to bug 4254731, this delete is changed to an update.  The update will
-- ensure that the entry gets cleaned up when other deleted snapshots are cleaned
-- up
--      DELETE FROM mgmt_ecm_gen_snapshot
      UPDATE mgmt_ecm_gen_snapshot
        SET is_current = ECM_CT.G_IS_DELETED_TYPE
        WHERE snapshot_guid = HEXTORAW(l_lock_guid);
      COMMIT;

    -- bail out when nothing to do
    EXCEPTION
      WHEN NO_DATA_FOUND THEN NULL;
    END;

  END IF;

  -- Run policy checks for this snapshot.  The snapshot type, target type and
  -- target name are used to determine if there are any policies that need
  -- to be run because they specified ON_REFRESH.
  -- This is here because the is_current flag is expected to be 'Y' for the
  -- snapshot being tested.
  -- Also, it is done in the same transaction as the appearance of the new
  -- snapshot so the policy violations reported are always consistent with
  -- the current snapshot displayed.
  BEGIN
    -- New way is to collection dependent metrics which will evaluate policies.
    EM_COLL_UTIL.run_snapshot_dependent_colls( p_snapshotType=>l_snapshot_type,
                                               p_targetType=>l_target_type,
                                               p_targetName=>l_target_name );                                            
  -- ignore all errors
  EXCEPTION WHEN OTHERS THEN NULL;
  END;

END POSTLOAD_CALLBACK;

-- *********************************************************************

-- Called to delete a generic snapshot
--
-- Inputs: snapshot_guid           - guid of the snapshot to be deleted

PROCEDURE DELETE_SNAPSHOT (v_snapshot_guid IN RAW) IS


BEGIN

      UPDATE mgmt_ecm_gen_snapshot
         SET is_current = ECM_CT.G_IS_DELETED_TYPE
       WHERE snapshot_guid = v_snapshot_guid;

      COMMIT;

END DELETE_SNAPSHOT;

/************************ old code for DELETE_SNAPSHOT *******************

-- obtained from mgmt_ecm_gen_snapshot
l_snapshot_type MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_TYPE%TYPE;
l_target_type MGMT_ECM_GEN_SNAPSHOT.TARGET_TYPE%TYPE;

-- obtained from mgmt_ecm_snapshot_md_tables
l_table_names VARCHAR2_TABLE;

-- sql to be executed
l_sql VARCHAR2(200) := NULL;

BEGIN

  IF ECM_CT.G_IS_DEBUG = 1 THEN
    CT_LOG('-- Deleting snapshot_guid ''' || v_snapshot_guid || '''');
  END IF;

  BEGIN
    SELECT snapshot_type, target_type INTO l_snapshot_type, l_target_type
      FROM mgmt_ecm_gen_snapshot
      WHERE snapshot_guid = v_snapshot_guid
      FOR UPDATE OF is_current;

  -- bail out when nothing to do
  EXCEPTION
    WHEN NO_DATA_FOUND THEN RETURN;
  END;

  UPDATE mgmt_ecm_gen_snapshot
    SET is_current = ECM_CT.G_IS_DELETED_TYPE
    WHERE snapshot_guid = v_snapshot_guid;
  COMMIT;

  BEGIN
    SELECT t.name BULK COLLECT INTO l_table_names
      FROM mgmt_ecm_snapshot_metadata m, mgmt_ecm_snapshot_md_tables t
      WHERE m.snapshot_type = l_snapshot_type
        AND m.target_type = l_target_type
        AND m.kind = ECM_CT.G_IS_LOADER
        AND m.metadata_id = t.metadata_id
      ORDER BY tbl_order DESC;

    IF l_table_names IS NOT NULL AND l_table_names.COUNT > 0 THEN
      FOR i IN l_table_names.FIRST..l_table_names.LAST
      LOOP
          IF ECM_CT.G_IS_DEBUG = 1 THEN
            CT_LOG('-- Deleting data in table "' || l_table_names(i) || '"');
          END IF;

          BEGIN
            l_sql := 'DECLARE guid RAW(16); ' ||
                     'BEGIN guid := :1;' ||
                     ' DELETE FROM ' || em_check.sql_object_name(
                         em_check.enquote_name(l_table_names(i), false)) ||
                     ' WHERE ' || ECM_CT.G_ECM_SNAPSHOT_ID || ' = guid; ' ||
                     'END;';

            IF ECM_CT.G_IS_DEBUG = 1 THEN
              CT_LOG('-- Executing ' || l_sql || ' with ''' || v_snapshot_guid || '''');
            END IF;

            EXECUTE IMMEDIATE l_sql USING v_snapshot_guid;
            COMMIT;

          -- ignore when table is empty
          EXCEPTION
            WHEN NO_DATA_FOUND THEN NULL;
          END;
      END LOOP;
    END IF;

  -- don't worry when no loader tables / views are found
  EXCEPTION
    WHEN NO_DATA_FOUND THEN NULL;
  END;

  IF ECM_CT.G_IS_DEBUG = 1 THEN
    CT_LOG('-- Destroying snapshot_guid ''' || v_snapshot_guid || '''');
  END IF;

  DELETE FROM mgmt_ecm_gen_snapshot
    WHERE snapshot_guid = v_snapshot_guid;
  COMMIT;

END DELETE_SNAPSHOT;
************************ old code for DELETE_SNAPSHOT *******************/


-- *********************************************************************

PROCEDURE HANDLE_ORPHAN_SNAPSHOTS (v_number_of_days IN NUMBER DEFAULT 3) IS

-- number of processed snapshots
l_num_snapshots NUMBER := 0;

BEGIN

  LOOP
    BEGIN 
      UPDATE mgmt_ecm_gen_snapshot
         SET is_current = ECM_CT.G_IS_DELETED_TYPE
       WHERE is_current <> ECM_CT.G_IS_CURRENT_TYPE
         AND is_current <> ECM_CT.G_IS_NOTCURRENT_TYPE
         AND is_current <> ECM_CT.G_IS_DELETED_TYPE
         AND (SYSDATE - v_number_of_days) > saved_timestamp
         AND ROWNUM <= MGMT_GLOBAL.MAX_COMMIT;

      l_num_snapshots := SQL%ROWCOUNT;
   
      IF l_num_snapshots > 0 THEN
        COMMIT;
      END IF;

    EXCEPTION
      -- NO_DATA_FOUND cannot be thrown for UPDATE of 0 rows.
      WHEN OTHERS THEN

        MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME, 
          ORPHAN_SNAP_CREATION_ERR_1, 
          'Encountered a problem while setting up orphan ECM snapshots for ' ||
          'deletion. Will try to process orphan snapshots one by one. ' || 
          'Problem: ' || SQLCODE || ': ' || SQLERRM, 
          v_log_level_in =>  MGMT_GLOBAL.G_WARN); 

             
        -- If there are no errors during one-by-one processing of orphans, 
        -- try to continue with the outer loop
        l_num_snapshots := MGMT_GLOBAL.MAX_COMMIT + 1;

        -- Update snapshots one-by-one
        FOR snap IN 
               (SELECT snapshot_guid FROM mgmt_ecm_gen_snapshot
                 WHERE is_current <> ECM_CT.G_IS_CURRENT_TYPE
                   AND is_current <> ECM_CT.G_IS_NOTCURRENT_TYPE
                   AND (SYSDATE - v_number_of_days) > saved_timestamp
                   AND ROWNUM <= MGMT_GLOBAL.MAX_COMMIT)
        LOOP
          BEGIN
            UPDATE mgmt_ecm_gen_snapshot
               SET is_current = ECM_CT.G_IS_DELETED_TYPE
             WHERE snapshot_guid = snap.snapshot_guid; 
          EXCEPTION
            WHEN OTHERS THEN 

              MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,    
                ORPHAN_SNAP_CREATION_ERR_2, 
                'Failed to set up orphan snapshot for deletion. ' || 
                'Snapshot ID: ' || snap.snapshot_guid || 
                ' Problem: ' || SQLCODE || ': ' || SQLERRM,
                v_log_level_in =>  MGMT_GLOBAL.G_ERROR); 

              -- Make sure that after we go through the rest of 
              -- orphan snapshots in this chunk, we exit the 
              -- outer loop since there is
              -- at least one snapshot that we cannot update,
              -- and so we do not want to end up in infinite loop
              -- which keeps trying to update it.
              l_num_snapshots := 0;
          END;
        END LOOP;

        -- commit all successful updates
        COMMIT;

    END;

    EXIT WHEN l_num_snapshots < MGMT_GLOBAL.MAX_COMMIT;
  END LOOP;

  RETURN;

END HANDLE_ORPHAN_SNAPSHOTS;

-- *********************************************************************

-- Called to delete all generic snapshots marked for deletion
-- Parameters:
-- p_target_types - target types array. Must match in length to snapshot 
--   types array.
-- p_snapshot_types - snapshot types array. Must match in length to 
--   target types array. Snapshot type at index i is for target type
--   at index i in the target types array.
-- p_is_primary - indicates if this is the primary thread or one of the 
--   worker threads. In the former case, snapshots are removed from
--   mgmt_ecm_gen_snapshot table. In the latter case, they are just
--   marked for later removal by primary thread. There should be only one
--   primary thread.
-- p_result_out - returns number of snapshots that we attempt to delete by 
--   converting them to G_IS_BEING_DELETED_TYPE state
PROCEDURE DELETE_SNAPSHOTS(p_target_types VARCHAR2_TABLE,
                           p_snapshot_types VARCHAR2_TABLE,
                           p_is_primary BOOLEAN := FALSE,
                           p_result_out OUT NUMBER)
IS

-- obtained from mgmt_ecm_snapshot_md_tables
l_table_names VARCHAR2_TABLE;

-- sql to be executed
l_sql VARCHAR2(512) := NULL;

-- current date
l_cur_date MGMT_ECM_GEN_SNAPSHOT.SAVED_TIMESTAMP%TYPE;

-- number of processed snapshots
l_num_snapshots NUMBER := 0;

-- number of processed rows (current and total)
l_num_rows NUMBER := 0;
l_total_num_rows NUMBER := 0;

-- temp variable for error case
l_any_work_left NUMBER := 0;

BEGIN
  p_result_out := 0;

  -- Delete each snapshot type
  FOR snap_type_idx IN p_snapshot_types.FIRST..p_snapshot_types.LAST
  LOOP
    BEGIN 

      l_table_names := NULL;
      BEGIN
        SELECT t.name BULK COLLECT INTO l_table_names
          FROM mgmt_ecm_snapshot_metadata m, mgmt_ecm_snapshot_md_tables t
         WHERE m.target_type = p_target_types(snap_type_idx)
           AND m.snapshot_type = p_snapshot_types(snap_type_idx)
           AND m.kind = ECM_CT.G_IS_LOADER
           AND m.metadata_id = t.metadata_id
         ORDER BY tbl_order DESC;
      EXCEPTION -- ignore if no tables in snapshot
        WHEN NO_DATA_FOUND THEN NULL;
      END;

      -- Convert all snapshots of this type with is_current = 
      -- ECM_CT.G_IS_BEING_DELETED_TYPE into ECM_CT.G_IS_DELETED_TYPE. This is 
      -- assuming that the only reason there would be snapshots of 
      -- ECM_CT.G_IS_BEING_DELETED_TYPE type is if DB crashed and/or the 
      -- previous deletion process got interrupted. Noone but this thread 
      -- should be deleting snapshots of this type during the execution of 
      -- this method.
      UPDATE mgmt_ecm_gen_snapshot 
         SET is_current = ECM_CT.G_IS_DELETED_TYPE
       WHERE target_type = p_target_types(snap_type_idx)
         AND snapshot_type = p_snapshot_types(snap_type_idx)
         AND is_current = ECM_CT.G_IS_BEING_DELETED_TYPE;

      IF SQL%ROWCOUNT > 0 THEN
        COMMIT;
      END IF;

      -- loop over all snapshots; MAX_COMMIT snapshots at a time 
      LOOP 

        -- Mark snapshots to be deleted
        UPDATE mgmt_ecm_gen_snapshot
           SET is_current = ECM_CT.G_IS_BEING_DELETED_TYPE
        WHERE target_type = p_target_types(snap_type_idx)
          AND snapshot_type = p_snapshot_types(snap_type_idx)
          AND is_current = ECM_CT.G_IS_DELETED_TYPE
          AND rownum <= MGMT_GLOBAL.MAX_COMMIT;
 
        l_num_snapshots := SQL%ROWCOUNT;
        p_result_out := p_result_out + l_num_snapshots;
        IF l_num_snapshots > 0 THEN
          COMMIT;
        ELSE 
          EXIT;
        END IF;

        -- if there are tables in the current snapshot type
        IF l_table_names IS NOT NULL AND l_table_names.COUNT > 0 THEN

          -- delete data from each table
          FOR tbl_idx IN l_table_names.FIRST..l_table_names.LAST
          LOOP
             
            -- Delete rows from the table tbl_idx in chunks of size MAX_COMMIT
            LOOP   
              BEGIN
                l_sql := 
                  ' DELETE FROM ' || em_check.sql_object_name(
                      em_check.enquote_name(l_table_names(tbl_idx), false)) ||
                  ' WHERE ' || ECM_CT.G_ECM_SNAPSHOT_ID || 
                  ' IN (SELECT snapshot_guid FROM mgmt_ecm_gen_snapshot ' ||
                  '      WHERE target_type = ' || 
                                 em_check.enquote_literal(
                                   replace(p_target_types(snap_type_idx), '''', '''''')) ||
                       ' AND snapshot_type = ' || 
                               em_check.enquote_literal(
                                   replace(p_snapshot_types(snap_type_idx), '''', '''''')) ||
                       ' AND is_current = ''' || 
                                 ECM_CT.G_IS_BEING_DELETED_TYPE || ''') ' ||
                  ' AND ROWNUM <= ' || MGMT_GLOBAL.MAX_COMMIT;

                EXECUTE IMMEDIATE l_sql; 
                l_num_rows := SQL%ROWCOUNT;
                l_total_num_rows := l_total_num_rows + l_num_rows;

                -- Total number of rows could be less than MAX_COMMIT
                -- if some tables have almost nothing to delete
                IF l_total_num_rows >= MGMT_GLOBAL.MAX_COMMIT THEN
                  COMMIT;
                  l_total_num_rows := 0;
                END IF;

              EXCEPTION
                -- NO_DATA_FOUND cannot be thrown for DELETE of 0 rows. 

                -- includes ref. constraints violations, deadlocks, etc.
                WHEN OTHERS THEN 

                  MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
                    DELETE_SNAP_ERR_3, 
                    'Encountered a problem while purging data from table ' ||
                    l_table_names(tbl_idx) || ' for target type ' ||
                    p_target_types(snap_type_idx) || ' / snapshot type ' ||
                    p_snapshot_types(snap_type_idx) || 
                    '. Will try to remove data one snapshot at a time. ' ||
                    'Problem: ' || SQLCODE || ': ' || SQLERRM,
                    v_log_level_in =>  MGMT_GLOBAL.G_WARN);


                  -- Retry slowly and find out where the exception happened

                  COMMIT; -- commit anything that DID succeed since last commit
                  l_total_num_rows := 0;
                  l_cur_date := SYSDATE;

                  -- Delete rows for snapshots one-by-one and handle the errors
                  FOR snap IN 
                         (SELECT snapshot_guid FROM mgmt_ecm_gen_snapshot
                           WHERE target_type = p_target_types(snap_type_idx)
                             AND snapshot_type = p_snapshot_types(snap_type_idx)
                             AND is_current = ECM_CT.G_IS_BEING_DELETED_TYPE) 
                  LOOP
                    BEGIN
                      -- Delete data from the table l_table_names(tbl_idx) 
                      -- for the snapshot
                      LOOP
                        l_sql := 
                          ' DELETE FROM ' || em_check.sql_object_name(
                              em_check.enquote_name(l_table_names(tbl_idx), false)) ||
                          ' WHERE ' || ECM_CT.G_ECM_SNAPSHOT_ID || ' = :1 ' ||
                          '   AND ROWNUM <= ' || MGMT_GLOBAL.MAX_COMMIT;

                        EXECUTE IMMEDIATE l_sql USING snap.snapshot_guid;
                        l_num_rows := SQL%ROWCOUNT;
                        l_total_num_rows := l_total_num_rows + l_num_rows;
                        
                        -- commit when enough rows are deleted
                        IF l_total_num_rows >= MGMT_GLOBAL.MAX_COMMIT THEN
                          COMMIT;
                          l_total_num_rows := 0;
                        END IF;

                        EXIT WHEN l_num_rows <  MGMT_GLOBAL.MAX_COMMIT;
                      END LOOP;
                     
                      -- If code ends up here for the last snapshot, 
                      -- then all snapshots got 
                      -- successfully removed without errors when
                      -- we tried one-by-one removal. 
                      -- Next, since l_num_rows < MGMT_GLOBAL.MAX_COMMIT
                      -- at this point, the outter loop for the given table
                      -- will correctly exit: we are done with this table.

                    EXCEPTION
                      -- NO_DATA_FOUND cannot be thrown for DELETE of 0 rows. 
                      WHEN OTHERS THEN

                        MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
                          DELETE_SNAP_ERR_4,
                          'Failed to purge data from table ' ||
                          l_table_names(tbl_idx) || ' for target type ' ||
                          p_target_types(snap_type_idx) || 
                          ' / snapshot type ' ||
                          p_snapshot_types(snap_type_idx) ||
                          '. Snapshot ID with the problem: ' || 
                          snap.snapshot_guid ||
                          '. Problem: ' || SQLCODE || ': ' || SQLERRM,
                          v_log_level_in =>  MGMT_GLOBAL.G_ERROR);

                        -- commit successful deletions 
                        IF l_total_num_rows > 0 THEN
                          COMMIT;
                          l_total_num_rows := 0;
                        END IF;
                        -- l_total_num_rows is 0 in this case no matter what.

                        UPDATE mgmt_ecm_gen_snapshot
                           SET is_current = ECM_CT.G_IS_ERROR_TYPE,
                               saved_timestamp = l_cur_date
                         WHERE snapshot_guid = snap.snapshot_guid;

                        COMMIT;

                        -- see if any work is left from this current chunk
                        -- of snapshots 
                        l_any_work_left := NULL;
                        BEGIN 
                          SELECT 1 INTO l_any_work_left
                            FROM DUAL 
                           WHERE EXISTS (SELECT * 
                             FROM mgmt_ecm_gen_snapshot
                            WHERE target_type = p_target_types(snap_type_idx) 
                              AND snapshot_type = p_snapshot_types(snap_type_idx) 
                              AND is_current = ECM_CT.G_IS_BEING_DELETED_TYPE); 
                        EXCEPTION
                          WHEN NO_DATA_FOUND THEN NULL;
                        END;

                        IF l_any_work_left IS NOT NULL THEN
                          -- Now that we found error and changed the 
                          -- snapshot's is_current, we'll try to proceed with 
                          -- fast removal approach in the hope that this was 
                          -- the only problematic snapshot.
                          -- Exit inner loop (for one-by-one processing of
                          -- snapshots), but not the outer loop for 
                          -- table tbl_idx:
                          l_num_rows := MGMT_GLOBAL.MAX_COMMIT + 1;
                          EXIT; 
                        ELSE
                          -- If no work is left, go on to the next set of
                          -- snapshots. Do not go back to the outer loop,
                          -- since there is a chance of getting exception
                          -- again, even with 0 snapshots to delete, 
                          -- and then we'd end up here again with infinite 
                          -- loop. 
                          -- So, exit both inner and outer loops. If any 
                          -- tables are left, they we'll go quickly through
                          -- them - through this or other paths.
                          l_num_rows := 0;
                          EXIT;
                        END IF;
                    END;
                  END LOOP; -- deleting rows for snapshots one-by-one 
              END; -- all code and exception handling for table tbl_idx

              -- Exit the loop when the number of removed rows does not
              -- reach the maximum (as specified by rownum constraint)
              EXIT WHEN l_num_rows < MGMT_GLOBAL.MAX_COMMIT;
            END LOOP; -- delete rows from table tbl_idx in chunks

          END LOOP; -- delete data from each table

          -- commit deletion of any remaining rows
          IF l_total_num_rows > 0 THEN COMMIT; END IF;

        END IF; -- if there are tables in the metadata for the snapshot type

        -- Delete / update rows in mgmt_ecm_gen_snapshot table
        BEGIN
          IF p_is_primary THEN
            -- delete rows from the mgmt_ecm_gen_snapshot table
            DELETE FROM mgmt_ecm_gen_snapshot
             WHERE target_type = p_target_types(snap_type_idx)
               AND snapshot_type = p_snapshot_types(snap_type_idx)
               AND is_current = ECM_CT.G_IS_BEING_DELETED_TYPE;
          ELSE
            -- update rows in the mgmt_ecm_gen_snapshot table to 
            -- be removed later by primary thread / job
            UPDATE mgmt_ecm_gen_snapshot
               SET is_current = ECM_CT.G_IS_ALMOST_DELETED_TYPE
             WHERE target_type = p_target_types(snap_type_idx)
               AND snapshot_type = p_snapshot_types(snap_type_idx)
               AND is_current = ECM_CT.G_IS_BEING_DELETED_TYPE;
          END IF;

          COMMIT;

        EXCEPTION
          -- NO_DATA_FOUND cannot be thrown for delete / update 
          WHEN OTHERS THEN

            MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
              DELETE_SNAP_ERR_5, 
              'Encountered a problem while removing last snapshot rows' ||
              ' for target type ' || p_target_types(snap_type_idx) || 
              ' / snapshot type ' || p_snapshot_types(snap_type_idx) ||
              '. Will try to remove the rows one snapshot at a time. ' ||
              'Problem: ' || SQLCODE || ': ' || SQLERRM,
              v_log_level_in =>  MGMT_GLOBAL.G_WARN);

            -- find problematic snapshots, and remove / update others
            l_cur_date := SYSDATE;
            FOR snap IN 
                      (SELECT snapshot_guid FROM mgmt_ecm_gen_snapshot
                        WHERE target_type = p_target_types(snap_type_idx)
                          AND snapshot_type = p_snapshot_types(snap_type_idx)
                          AND is_current = ECM_CT.G_IS_BEING_DELETED_TYPE)
            LOOP
              BEGIN

                IF p_is_primary THEN
                  DELETE FROM mgmt_ecm_gen_snapshot 
                   WHERE snapshot_guid = snap.snapshot_guid; 
                ELSE
                  UPDATE mgmt_ecm_gen_snapshot
                     SET is_current = ECM_CT.G_IS_ALMOST_DELETED_TYPE
                   WHERE snapshot_guid = snap.snapshot_guid;
                END IF;

              EXCEPTION
                 -- NO_DATA_FOUND cannot be thrown for delete / update
                WHEN OTHERS THEN

                  MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
                    DELETE_SNAP_ERR_6,
                    'Failed to remove last snapshot row' ||
                    ' for target type ' || p_target_types(snap_type_idx) || 
                    ' / snapshot type ' || p_snapshot_types(snap_type_idx) ||
                    '. Snapshot ID with the problem: ' ||
                    snap.snapshot_guid ||
                    '. Problem: ' || SQLCODE || ': ' || SQLERRM,
                    v_log_level_in =>  MGMT_GLOBAL.G_ERROR);
 
                  -- Last try: make the snapshot an error snapshot and go on
                  UPDATE mgmt_ecm_gen_snapshot
                     SET is_current = ECM_CT.G_IS_ERROR_TYPE,
                         saved_timestamp = l_cur_date
                   WHERE snapshot_guid = snap.snapshot_guid;

              END;
            END LOOP; -- finding problematic snapshots

            COMMIT;

        END; -- delete / update rows in mgmt_ecm_gen_snapshot table

        -- Exit loop when number of snapshots selected for removal
        -- does not reach the maximum as determined by rownum constraint
        -- in the query at the beginning of the loop
        EXIT WHEN l_num_snapshots < MGMT_GLOBAL.MAX_COMMIT;

      END LOOP; -- loop over all snapshots to delete, a chunk at a time

    -- catch any exceptions for the snapshot type deletion
    -- log error, and try to delete other snapshot types
    EXCEPTION
      WHEN OTHERS THEN

        MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
          DELETE_SNAP_ERR_7,
          'Failed to purge deleted snapshots' ||
          ' for target type ' || p_target_types(snap_type_idx) ||
          ' / snapshot type ' || p_snapshot_types(snap_type_idx) ||
          '. Problem: ' || SQLCODE || ': ' || SQLERRM,
          v_log_level_in =>  MGMT_GLOBAL.G_ERROR);

    END;

  END LOOP; -- delete each snapshot type

END DELETE_SNAPSHOTS;


-- *********************************************************************

-- Called to delete all generic snapshots marked for deletion
-- This is the primary deletion thread if deletions are split into threads
-- To split out CSA snapshot deletion into separate thread:
-- (1) Uncomment 2 sections below that talk about CSA: one for
--     not including CSA in primary threads, and the other
--     that handles "almost deleted" snapshots created by other threads.
-- (2) Uncomment DELETE_CSA_SNAPSHOTS procedure in both this and pkgdef files. 
PROCEDURE DELETE_SNAPSHOTS(pcb_params IN OUT MGMT_PURGE_CALLBACK_PARAMS) IS

l_target_types VARCHAR2_TABLE;
l_snapshot_types VARCHAR2_TABLE;

-- number of processed snapshots
l_num_snapshots NUMBER := 0;
l_result NUMBER := 0;

-- current date
l_cur_date MGMT_ECM_GEN_SNAPSHOT.SAVED_TIMESTAMP%TYPE;

-- continue deletion of target types?
l_continue_deletion BOOLEAN := TRUE;

BEGIN

  -- select snapshot types to delete: all non-host-config, non CSA types.
  BEGIN
    SELECT target_type, snapshot_type
           BULK COLLECT INTO l_target_types, l_snapshot_types
      FROM mgmt_ecm_snapshot_metadata
     WHERE kind = ECM_CT.G_IS_LOADER;
       /*** Exclude CSA snapshot deletion from this procedure
       AND NOT ((target_type = 'oracle_csa_collector' -- all but CSA
                 AND snapshot_type = 'oracle_csa')
             OR (target_type = 'oracle_csa_client'
                 AND snapshot_type = 'oracle_csa_host')); 
       ***/

  -- handle the case when there are no types to work on
  EXCEPTION
    WHEN NO_DATA_FOUND THEN l_continue_deletion := FALSE;
  END;

  IF l_continue_deletion THEN
    DELETE_SNAPSHOTS(l_target_types, l_snapshot_types, TRUE, l_result);
  END IF;

  pcb_params.rows_processed  := l_result;
  pcb_params.callback_result := 0;
  pcb_params.error_code      := 0;
  pcb_params.error_msg       := NULL;

  /*** Following includes CSA and snapshots deleted in other threads
  -- Delete "almost deleted" snapshots created by other threads.
  LOOP
    BEGIN
      DELETE FROM mgmt_ecm_gen_snapshot
       WHERE is_current = ECM_CT.G_IS_ALMOST_DELETED_TYPE 
         AND rownum <= MGMT_GLOBAL.MAX_COMMIT;

      l_num_snapshots := SQL%ROWCOUNT;

      COMMIT;
    EXCEPTION
      -- NO_DATA_FOUND cannot be thrown for delete
      WHEN OTHERS THEN

        MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
          DELETE_SNAP_ERR_8,
          'Encountered a problem while removing last snapshot rows. ' ||
          'Will try to remove the rows one snapshot at a time. ' ||
          'Problem: ' || SQLCODE || ': ' || SQLERRM,
          v_log_level_in =>  MGMT_GLOBAL.G_WARN);

        -- find problematic snapshots, and remove others
        l_cur_date := SYSDATE;
        FOR snap IN
                  (SELECT snapshot_guid FROM mgmt_ecm_gen_snapshot
                    WHERE is_current = ECM_CT.G_IS_ALMOST_DELETED_TYPE
                      AND rownum <= MGMT_GLOBAL.MAX_COMMIT)
        LOOP
          BEGIN

            DELETE FROM mgmt_ecm_gen_snapshot
             WHERE snapshot_guid = snap.snapshot_guid;

          EXCEPTION
             -- NO_DATA_FOUND cannot be thrown for delete 
            WHEN OTHERS THEN

              MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
                DELETE_SNAP_ERR_9, 
                'Failed to remove last snapshot row. ' ||
                ' Snapshot ID with the problem: ' ||
                snap.snapshot_guid ||
                '. Problem: ' || SQLCODE || ': ' || SQLERRM,
                v_log_level_in =>  MGMT_GLOBAL.G_ERROR);

              -- Last try: make the snapshot an error snapshot and go on
              UPDATE mgmt_ecm_gen_snapshot
                 SET is_current = ECM_CT.G_IS_ERROR_TYPE,
                     saved_timestamp = l_cur_date
               WHERE snapshot_guid = snap.snapshot_guid;

          END;
        END LOOP; -- finding problematic snapshots, and removing others

        -- do not exit the loop in case of exception
        l_num_snapshots := MGMT_GLOBAL.MAX_COMMIT + 1;

        COMMIT;

    END;

    EXIT WHEN l_num_snapshots <  MGMT_GLOBAL.MAX_COMMIT;
  END LOOP; -- end of deleting "almost deleted" snapshots
  ***/

  -- This has to follow deletion of "almost deleted" snapshots,
  -- since otherwise this procedure would orphan old "almost 
  -- deleted" snapshots by changing them back to "deleted" 
  -- snapshots before having a chance to actually delete them 
  -- first.
  HANDLE_ORPHAN_SNAPSHOTS;

END DELETE_SNAPSHOTS;

-- *********************************************************************

/*** Uncomment this procedure if CSA is separated into a separate "thread".
**** See comments above in this file for what else needs to be done.
-- Called to delete all CSA snapshots marked for deletion
PROCEDURE DELETE_CSA_SNAPSHOTS IS

l_target_types VARCHAR2_TABLE;
l_snapshot_types VARCHAR2_TABLE;

BEGIN

  l_target_types := VARCHAR2_TABLE();
  l_snapshot_types := VARCHAR2_TABLE();

  l_target_types.EXTEND;
  l_snapshot_types.EXTEND;

  l_target_types(l_target_types.LAST) := 'oracle_csa_collector';
  l_snapshot_types(l_snapshot_types.LAST) := 'oracle_csa';

  l_target_types.EXTEND;
  l_snapshot_types.EXTEND;

  l_target_types(l_target_types.LAST) := 'oracle_csa_client';
  l_snapshot_types(l_snapshot_types.LAST) := 'oracle_csa_host';

  DELETE_SNAPSHOTS(l_target_types, l_snapshot_types, FALSE);

END DELETE_CSA_SNAPSHOTS;
***/

-- *********************************************************************

PROCEDURE HANDLE_ORPHAN_HOST_CONFIGS (v_number_of_days IN NUMBER DEFAULT 3) IS

-- number of purged snapshots
l_num_snapshots NUMBER := 0;

BEGIN

  -- delete rows from the "suspect orphans" table that did not turn out to
  -- be orphans or that were removed on a previous run of purge; i.e. delete 
  -- any suspect snapshot ids for which there is no longer any snapshots 
  -- in the snapshot table with temporary status (e.g. T, D, L)
  BEGIN 
    DELETE FROM mgmt_ecm_host_configs_to_del suspect
     WHERE NOT EXISTS 
           (SELECT * FROM mgmt_ecm_snapshot s
             WHERE suspect.snapshot_guid = s.snapshot_guid
               AND s.is_current <> ECM_CT.G_IS_CURRENT_TYPE
               AND s.is_current <> ECM_CT.G_IS_NOTCURRENT_TYPE); 
    COMMIT;
  EXCEPTION
    WHEN OTHERS THEN NULL;
  END;
 
  -- Setup orphan snapshots for removal: old suspects and 
  -- current snapshots without corresponding targets. 
  LOOP
    BEGIN

      UPDATE mgmt_ecm_snapshot
         SET is_current = ECM_CT.G_IS_DELETED_TYPE
       WHERE is_current <> ECM_CT.G_IS_DELETED_TYPE AND
             snapshot_guid in 
             (SELECT snapshot_guid 
                FROM 
                ((SELECT snapshot_guid 
                    FROM mgmt_ecm_host_configs_to_del suspect
                   WHERE (SYSDATE - suspect.ts) > v_number_of_days)
                 UNION ALL
                  (SELECT s.snapshot_guid 
                     FROM mgmt_ecm_snapshot s
                    WHERE s.is_current = ECM_CT.G_IS_CURRENT_TYPE 
                      AND NOT EXISTS (SELECT * FROM mgmt_targets t
                                       WHERE t.target_name = s.target_name
                                         AND t.target_type = s.target_type))
                )
                WHERE ROWNUM <= MGMT_GLOBAL.MAX_COMMIT
             );

      l_num_snapshots := SQL%ROWCOUNT;
      IF l_num_snapshots > 0 THEN
        COMMIT;
      END IF;
 
    EXCEPTION
      -- NO_DATA_FOUND cannot be thrown for UPDATE of 0 rows
      WHEN OTHERS THEN 

        MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME, 
          ORPHAN_HOST_CONFIG_ERR_10,
          'Encountered a problem while setting up orphan host ' || 
          'config snapshots for deletion. Will try to process ' ||
          'orphan snapshots one by one. ' || 
          'Problem: ' || SQLCODE || ': ' || SQLERRM, 
          v_log_level_in =>  MGMT_GLOBAL.G_WARN); 

             
        -- If there are no errors during one-by-one processing of orphans, 
        -- try to continue with the outer loop
        l_num_snapshots := MGMT_GLOBAL.MAX_COMMIT + 1;
 
        FOR snap IN
               (SELECT snapshot_guid
                  FROM
                  ((SELECT snapshot_guid 
                      FROM mgmt_ecm_host_configs_to_del suspect
                     WHERE (SYSDATE - suspect.ts) > v_number_of_days)
                   UNION ALL
                    (SELECT s.snapshot_guid 
                       FROM mgmt_ecm_snapshot s 
                      WHERE s.is_current = ECM_CT.G_IS_CURRENT_TYPE 
                        AND NOT EXISTS (SELECT * FROM mgmt_targets t 
                                         WHERE t.target_name = s.target_name
                                           AND t.target_type = s.target_type))
                  ) 
                  WHERE ROWNUM <= MGMT_GLOBAL.MAX_COMMIT
               ) 
        LOOP
          BEGIN
            UPDATE mgmt_ecm_snapshot 
               SET is_current = ECM_CT.G_IS_DELETED_TYPE 
             WHERE snapshot_guid = snap.snapshot_guid;

          EXCEPTION
            WHEN OTHERS THEN 

              MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,    
                ORPHAN_HOST_CONFIG_ERR_11, 
                'Failed to set up orphan host config snapshot for ' || 
                'deletion. Snapshot ID: ' || snap.snapshot_guid || 
                ' Problem: ' || SQLCODE || ': ' || SQLERRM,
                v_log_level_in =>  MGMT_GLOBAL.G_ERROR); 

              -- Make sure that after we go through the rest of 
              -- orphan snapshots in this chunk, we exit the 
              -- outer loop since there is
              -- at least one snapshot that we cannot update,
              -- and so we do not want to end up in infinite loop
              -- which keeps trying to update it.
              l_num_snapshots := 0;
          END;
        END LOOP;

        -- commit all successful updates
        COMMIT;

    END; -- removal of a chunk of orphans
  
    EXIT WHEN l_num_snapshots < MGMT_GLOBAL.MAX_COMMIT;
  END LOOP;

 
  -- insert new potential suspects: e.g. lock snapshots for which there 
  -- already exists a current snapshot, and other snapshot that are neither
  -- current nor saved.
  BEGIN
 
     INSERT INTO mgmt_ecm_host_configs_to_del 
        (SELECT s.SNAPSHOT_GUID, SYSDATE FROM mgmt_ecm_snapshot s
         WHERE (s.is_current <> ECM_CT.G_IS_CURRENT_TYPE
                AND s.is_current <> ECM_CT.G_IS_NOTCURRENT_TYPE
                AND s.is_current <> ECM_CT.G_IS_LOCKED_TYPE)
            OR
               (s.is_current = ECM_CT.G_IS_LOCKED_TYPE AND 
                   EXISTS (SELECT * FROM mgmt_ecm_snapshot s2
                   WHERE s.target_name = s2.target_name AND
                         s.target_type = s2.target_type AND
                         s.snapshot_type = s2.snapshot_type AND
                         s.snapshot_type_version = s2.snapshot_type_version AND
                         s2.is_current = ECM_CT.G_IS_CURRENT_TYPE) 
               )
        );
     COMMIT;
  EXCEPTION 
     WHEN OTHERS THEN NULL; -- ignore exceptions
  END;
                     
END HANDLE_ORPHAN_HOST_CONFIGS;

-- *********************************************************************

-- Called to delete all host configuration snapshots marked for deletion
PROCEDURE DELETE_HOST_CONFIG_SNAPSHOTS (pcb_params IN OUT 
                                               MGMT_PURGE_CALLBACK_PARAMS) IS

-- sql to be executed
l_sql VARCHAR2(512) := NULL;

-- current date
l_cur_date MGMT_ECM_HOST_CONFIGS_TO_DEL.TS%TYPE;

-- number of processed snapshots
l_num_snapshots NUMBER := 0;

-- number of processed rows (current and total)
l_num_rows NUMBER := 0;
l_total_num_rows NUMBER := 0;

-- number of snapshots that we will attempt to delete
-- by converting them to G_IS_BEING_DELETED_TYPE state
l_prev_result NUMBER := 0;
l_result NUMBER := 0;
l_snapshot_guids T_SNAP_GUID_TABLE;

BEGIN

  -- loop over all snapshots; MAX_COMMIT snapshots at a time 
  LOOP 

    l_prev_result := l_result;

    SELECT snapshot_guid BULK COLLECT INTO l_snapshot_guids
    FROM mgmt_ecm_snapshot WHERE is_current = ECM_CT.G_IS_DELETED_TYPE
    AND ROWNUM <= MGMT_GLOBAL.MAX_COMMIT;

    IF (l_snapshot_guids IS NOT NULL AND l_snapshot_guids.COUNT > 0) THEN

      FOR i IN l_snapshot_guids.FIRST..l_snapshot_guids.LAST LOOP
        BEGIN
           DELETE FROM mgmt_ecm_snapshot
           WHERE snapshot_guid = l_snapshot_guids(i);

        EXCEPTION
          WHEN OTHERS THEN
             MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
             DELETE_HOST_CONFIG_ERR_12,  
               'Failed to purge host config snapshot with ID: ' ||
               l_snapshot_guids(i) || 
               '. Problem: ' || SQLCODE || ': ' || SQLERRM,
               v_log_level_in =>  MGMT_GLOBAL.G_ERROR);

              -- This snapshot will get moved to orphan suspect list
              UPDATE mgmt_ecm_snapshot
                 SET is_current = ECM_CT.G_IS_ERROR_TYPE
               WHERE snapshot_guid = l_snapshot_guids(i);
        END;

        COMMIT;

        l_result := l_result + 1;

      END LOOP; -- delete snapshots in a chunk
    END IF;

    EXIT WHEN l_prev_result = l_result;
  END LOOP; -- loop over all snapshots, a chunk at a time

  -- compute orphan snapshots and mark them for deletion
  HANDLE_ORPHAN_HOST_CONFIGS;

  pcb_params.callback_result := 0;
  pcb_params.error_code      := 0;
  pcb_params.error_msg       := NULL;
  pcb_params.rows_processed  := l_result;

-- catch any exceptions for the snapshot type deletion
-- log error, and try to delete other snapshot types
EXCEPTION
  WHEN OTHERS THEN

      MGMT_LOG.LOG_ERROR(ECM_PURGE_LOG_MODULE_NAME,
        DELETE_HOST_CONFIG_ERR_13, 
        'Failed to delete host config snapshots' ||
        '. Problem: ' || SQLCODE || ': ' || SQLERRM,
        v_log_level_in =>  MGMT_GLOBAL.G_ERROR);

      pcb_params.callback_result := 0;
      pcb_params.error_code      := 0;
      pcb_params.error_msg       := 'Failed to delete host config snapshots. See logged errors.';
      pcb_params.rows_processed  := l_result;

     -- Should we re-RAISE the exception here? 
     -- For now we do not, because we do not do this for
     -- generic framework, where every exception is only
     -- logged so we can proceed with other snapshot type
     -- deletions.
     -- RAISE;

END DELETE_HOST_CONFIG_SNAPSHOTS;

-- Dummy for 10.1.0.3.0 -> 10.2.0.1.0 upgrade
PROCEDURE PURGE_HOST_CONFIGS (pcb_params IN OUT
                                               MGMT_PURGE_CALLBACK_PARAMS) IS
BEGIN
    NULL;
END PURGE_HOST_CONFIGS;

-- *********************************************************************

-- Get the key list.  Need to look at the metadata of this table and the
-- parent tables, even though the actual table has all the columns in it.

-- Private
FUNCTION GET_KEY_COLUMN_LIST (
    v_table_name IN VARCHAR2,
    v_metadata_id IN RAW)
 RETURN VARCHAR2_TABLE IS

l_key_list VARCHAR2_TABLE := NULL;

BEGIN

  BEGIN
    SELECT c.name BULK COLLECT INTO l_key_list
      FROM
        (
        SELECT name
        FROM
          mgmt_ecm_snapshot_md_tables t
        START WITH name = v_table_name
               AND metadata_id = v_metadata_id
        CONNECT BY name = PRIOR t.parent_table_name
                            AND metadata_id = v_metadata_id
        ) t,
      mgmt_ecm_snapshot_md_columns c
      WHERE (c.table_name = t.name)
        AND (c.metadata_id = v_metadata_id)
        AND (c.IS_KEY = 'Y');

  -- ignore if no key columns found
  EXCEPTION
    WHEN NO_DATA_FOUND THEN NULL;
  END;

  RETURN l_key_list;

END;

-- *********************************************************************

-- Get a list of all non-key columns in a table

-- Private
FUNCTION GET_NON_KEY_COLUMN_LIST (
    v_table_name IN VARCHAR2,
    v_metadata_id IN RAW)
 RETURN VARCHAR2_TABLE IS

l_nonkey_list VARCHAR2_TABLE := NULL;

BEGIN

  BEGIN
    SELECT name BULK COLLECT INTO l_nonkey_list
      FROM mgmt_ecm_snapshot_md_columns
      WHERE (table_name = v_table_name)
        AND (metadata_id = v_metadata_id)
        AND (is_key = 'N');

  -- ignore when no non-key columns found
  EXCEPTION
    WHEN NO_DATA_FOUND THEN NULL;
  END;

  RETURN l_nonkey_list;

END;

-- *********************************************************************

-- Private
FUNCTION TO_STRING (v_list IN VARCHAR2_TABLE)
 RETURN VARCHAR2 IS

l_string VARCHAR2(32767) := '';
l_comma VARCHAR2(1) := '';

BEGIN

  IF (v_list IS NOT NULL AND v_list.COUNT > 0) THEN
    FOR i IN v_list.FIRST..v_list.LAST
    LOOP
      IF (LENGTH(v_list(i)) > 0) THEN
        l_string := l_string || l_comma || 
          em_check.simple_sql_name(em_check.enquote_name(v_list(i), false));
        l_comma := ',';
      END IF;
    END LOOP;
  END IF;

  RETURN l_string;

END;

-- *********************************************************************

-- Private
PROCEDURE DUPLICATE_TABLE_DATA (
    v_snapshot_guid IN RAW,
    v_new_snapshot_guid IN RAW,
    v_table_names IN VARCHAR2_TABLE,
    v_metadata_id RAW) IS

l_sql VARCHAR2(4000) := NULL;
l_key_columns VARCHAR2(4000) := NULL;
l_columns VARCHAR2(4000) := NULL;

l_column_names VARCHAR2_TABLE;

BEGIN

  IF (v_table_names IS NOT NULL AND v_table_names.COUNT > 0) THEN
    FOR i IN v_table_names.FIRST..v_table_names.LAST
    LOOP
      IF (ECM_CT.G_IS_DEBUG = 1) THEN
        CT_LOG('-- Duplicating data in table "' || v_table_names(i) || '"');
      END IF;

      BEGIN
        -- Compute the table's column list, l_columns, from the metadata
        
        -- find key column names for v_table_names(i) including those inherited
        -- from ancestor tables
        l_column_names := GET_KEY_COLUMN_LIST (v_table_names(i), v_metadata_id);
        l_key_columns := TO_STRING(l_column_names);

        -- CT_LOG('-- Key Columns: ' || l_key_columns);

        -- find non-key column names for v_table_names(i)
        l_column_names := GET_NON_KEY_COLUMN_LIST 
                                              (v_table_names(i), v_metadata_id);
        l_columns := TO_STRING(l_column_names);

        -- CT_LOG('-- Non-key Columns: ' || l_columns);

        IF (LENGTH(l_key_columns) > 0 AND LENGTH(l_columns) > 0) THEN
          l_columns := l_key_columns || ' , ' || l_columns;
        ELSIF (LENGTH(l_key_columns) > 0) THEN
          l_columns := l_key_columns;
        END IF;

        -- l_columns is not wrapped in em_check (or dbms_assert) since per Ravi P. it's
        -- sufficient to create l_columns with wrapped columns there. Thus,
        -- we do the wrapping of each column in TO_STRING function.
        l_sql := 'DECLARE guid RAW(16); new_guid VARCHAR2(64); ' ||
                 'BEGIN guid := :1; new_guid := RAWTOHEX(:2);' ||
                 ' INSERT INTO ' || em_check.sql_object_name(
                     em_check.enquote_name(v_table_names(i), false)) || 
                 ' (' || ECM_CT.G_ECM_SNAPSHOT_ID || ', ' || l_columns || ')' ||
                 ' (SELECT HEXTORAW(new_guid), ' || l_columns ||
                 '  FROM ' || em_check.sql_object_name(
                     em_check.enquote_name(v_table_names(i), false)) || 
                 '  WHERE (' || ECM_CT.G_ECM_SNAPSHOT_ID || ' = guid)); ' ||
                 'END;';

        IF (ECM_CT.G_IS_DEBUG = 1) THEN
          CT_LOG('-- Executing ' || l_sql || ' with ''' || v_snapshot_guid || '''');
        END IF;

        EXECUTE IMMEDIATE l_sql USING v_snapshot_guid, v_new_snapshot_guid;
        COMMIT;

      -- ignore if table data is empty
      EXCEPTION
        WHEN NO_DATA_FOUND THEN NULL;
      END;

    END LOOP;
  END IF;

END DUPLICATE_TABLE_DATA;

-- *********************************************************************

-- Called to copy a generic snapshot
--
-- Inputs: snapshot_guid           - guid of the existing snapshot
--         description             - description of saved snapshot
-- Output: savedConfigGuid         - guid of the newly saved configuration

-- Private
PROCEDURE COPY_SNAPSHOT (
    v_snapshot_guid IN RAW,
    v_description IN VARCHAR2,
    v_savedConfigGuid OUT VARCHAR2) IS

-- obtained from mgmt_ecm_gen_snapshot
l_snapshot_type MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_TYPE%TYPE := NULL;
l_start_timestamp MGMT_ECM_GEN_SNAPSHOT.START_TIMESTAMP%TYPE := SYSDATE;
l_elapsed_time MGMT_ECM_GEN_SNAPSHOT.ELAPSED_TIME%TYPE := -1;
l_target_guid MGMT_ECM_GEN_SNAPSHOT.TARGET_GUID%TYPE;
l_target_name MGMT_ECM_GEN_SNAPSHOT.TARGET_NAME%TYPE := NULL;
l_target_type MGMT_ECM_GEN_SNAPSHOT.TARGET_TYPE%TYPE := NULL;
l_display_target_name MGMT_ECM_GEN_SNAPSHOT.DISPLAY_TARGET_NAME%TYPE := NULL;
l_display_target_type MGMT_ECM_GEN_SNAPSHOT.DISPLAY_TARGET_TYPE%TYPE := NULL;
l_status MGMT_ECM_GEN_SNAPSHOT.STATUS%TYPE := NULL;
l_message MGMT_ECM_GEN_SNAPSHOT.MESSAGE%TYPE := NULL;
l_creator MGMT_ECM_GEN_SNAPSHOT.CREATOR%TYPE := MGMT_USER.GET_CURRENT_EM_USER();
l_is_current MGMT_ECM_GEN_SNAPSHOT.IS_CURRENT%TYPE := NULL;

-- obtained from mgmt_ecm_snapshot_metadata
l_metadata_id_l MGMT_ECM_SNAPSHOT_METADATA.METADATA_ID%TYPE;

-- obtained from mgmt_ecm_snapshot_md_tables
l_table_names VARCHAR2_TABLE;

-- obtained from INSERT_GEN_SNAPSHOT_INFO
l_snapshot_guid VARCHAR2(64) := NULL;
l_new_snapshot_guid MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_GUID%TYPE;

-- local variables
l_is_current_new MGMT_ECM_GEN_SNAPSHOT.IS_CURRENT%TYPE := NULL;

BEGIN

  BEGIN
    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Copying snapshot_guid ''' || v_snapshot_guid || '''');
    END IF;

    BEGIN
      SELECT snapshot_type, start_timestamp, elapsed_time,
             target_guid, target_name, target_type,
             display_target_name, display_target_type,
             is_current, message, status
        INTO l_snapshot_type, l_start_timestamp, l_elapsed_time,
             l_target_guid, l_target_name, l_target_type,
             l_display_target_name, l_display_target_type,
             l_is_current, l_message, l_status
        FROM mgmt_ecm_gen_snapshot
        WHERE (snapshot_guid = v_snapshot_guid);

    -- bail out if no snapshot found
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        raise_application_error (MGMT_GLOBAL.SNAPSHOT_MISSING_ERR, 
                                 'Could not find snapshot to copy');
    END;

    IF (ECM_CT.G_IS_DEBUG = 1) THEN
      CT_LOG('-- Creating new ''' || l_snapshot_type || ''' snapshot for target ''' || l_target_name || '''');
    END IF;

    INSERT_GEN_SNAPSHOT_INFO (
        l_snapshot_type, RAWTOHEX(l_target_guid),
        l_target_name, l_target_type,
        l_display_target_name, l_display_target_type,
        v_description, l_start_timestamp, l_elapsed_time,
        ECM_CT.G_IS_SAVING_TYPE, l_status,
        l_message, l_creator, l_snapshot_guid);
    COMMIT;

    l_new_snapshot_guid := HEXTORAW(l_snapshot_guid);

    -- check if target still exists and null out target_guid if not
    BEGIN
      SELECT target_name INTO l_target_name
        FROM mgmt_targets
        WHERE (target_guid = l_target_guid);

    -- null out target_guid if target no longer exists
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        UPDATE mgmt_ecm_gen_snapshot
          SET target_guid = NULL
          WHERE (snapshot_guid = l_new_snapshot_guid);
    END;

    -- Duplicate loader tables
    BEGIN
      SELECT metadata_id INTO l_metadata_id_l
        FROM mgmt_ecm_snapshot_metadata
        WHERE snapshot_type = l_snapshot_type
          AND target_type = l_target_type
          AND kind = ECM_CT.G_IS_LOADER;
          
      SELECT name BULK COLLECT INTO l_table_names
        FROM mgmt_ecm_snapshot_md_tables
        WHERE metadata_id = l_metadata_id_l
        ORDER BY tbl_order DESC;

      DUPLICATE_TABLE_DATA (v_snapshot_guid,
                            l_new_snapshot_guid,
                            l_table_names,
                            l_metadata_id_l);

    -- don't worry if no loader tables / views are found
    EXCEPTION
      WHEN NO_DATA_FOUND THEN NULL;
    END;

    -- check if the old snapshot is still valid
    BEGIN
      SELECT is_current INTO l_is_current_new
        FROM mgmt_ecm_gen_snapshot
        WHERE (snapshot_guid = v_snapshot_guid)
          AND (is_current = l_is_current);
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        raise_application_error (MGMT_GLOBAL.SNAPSHOT_MISSING_ERR, 
                  'Snapshot dissapeared during the copy operation');
    END;

    UPDATE mgmt_ecm_gen_snapshot
      SET is_current = ECM_CT.G_IS_NOTCURRENT_TYPE
      WHERE (snapshot_guid = l_new_snapshot_guid);
     COMMIT;

     v_savedConfigGuid := RAWTOHEX(l_new_snapshot_guid);
  EXCEPTION
    WHEN OTHERS THEN
      -- delete snapshot
      UPDATE mgmt_ecm_gen_snapshot
         SET is_current = ECM_CT.G_IS_DELETED_TYPE
       WHERE snapshot_guid = l_new_snapshot_guid;

      COMMIT;

      RAISE;
  END;

END COPY_SNAPSHOT;

-- *********************************************************************

-- Called to copy a generic snapshot
--
-- Inputs: target name, target type, and snapshot type of the current snapshot
-- to be coppied. v_description is the description of the new saved snapshot.
-- v_snapGuidIn is the snapshot GUID of the configuration to be saved, 
-- not-null in case of saving of saved configurations.
-- returns guid of the newly saved configuration

FUNCTION COPY_SNAPSHOT_GEN (
    v_target_name IN VARCHAR2,
    v_target_type IN VARCHAR2,
    v_snapshot_type IN VARCHAR2,
    v_description IN VARCHAR2,
    v_snapGuidIn IN VARCHAR2 default null)
RETURN VARCHAR2
IS
l_current_snapshot_guid MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_GUID%TYPE;
l_success BOOLEAN := FALSE;
l_saved_snapshot_guid VARCHAR2(32) :=null;

BEGIN

  FOR i IN 1..NUM_OF_SNAPSHOT_COPY_RETRIES
  LOOP
    BEGIN
        IF (v_snapGuidIn IS  NULL) THEN
          SELECT snapshot_guid INTO l_current_snapshot_guid
            FROM mgmt_ecm_gen_snapshot
            WHERE (snapshot_type = v_snapshot_type)
              AND (target_name = v_target_name)
              AND (target_type = v_target_type)
              AND (is_current = ECM_CT.G_IS_CURRENT_TYPE);
        END IF;                             
    
        -- bail out when snapshot doesn't exist
        EXCEPTION
          WHEN NO_DATA_FOUND THEN
            raise_application_error (MGMT_GLOBAL.SNAPSHOT_MISSING_ERR, 
                                     'Could not find snapshot to copy');
    END;

    BEGIN
          IF(v_snapGuidIn IS NOT NULL)
          THEN
          l_current_snapshot_guid := HEXTORAW(v_snapGuidIn);
          END IF;
          COPY_SNAPSHOT (l_current_snapshot_guid, v_description,l_saved_snapshot_guid);
          l_success := TRUE;
      EXIT;
    
        -- try again if snapshot changed during the copy
        EXCEPTION
          WHEN MGMT_GLOBAL.SNAPSHOT_MISSING THEN NULL;
    END;
  END LOOP;

  IF (NOT l_success) THEN
    -- snapshot keeps changing for all retries.. 
    raise_application_error (MGMT_GLOBAL.SNAPSHOT_MISSING_ERR, 
        'Snapshot keeps getting updated during the snapshot save operation.');
  END IF;
--  l_saved_snapshot_guid := 'F4276509CA25E495E030578C9203448E';
  return l_saved_snapshot_guid;
END;

-------------------------------------------------------------------------------
  -- SAVE_COMPOSITE_CONFIG
  -- This is to save all the member snapshots for a given composite target type
  -- Inputs: target name, target type of the current target, whose configs are to
  -- be copied recursively for all members. v_description is the description of the new saved snapshots,
  -- which will be used same for all the members
  
   PROCEDURE SAVE_COMPOSITE_CONFIG(v_target_name IN VARCHAR2,
                                   v_target_type IN VARCHAR2,
                                   v_description IN VARCHAR2,
                                   p_save_details out cursorType)
   IS
   l_save_result save_composite_tab:=save_composite_tab();
   l_temp_rec save_composite_rec;
   l_collection_time DATE;
   l_comp_target_guid RAW(16) := mgmt_target.get_target_guid(v_target_name, v_target_type);
--    saveRec l_member_details%ROWTYPE;
    l_is_composite NUMBER:=0;
   l_value MGMT_TYPE_PROPERTIES.property_value%TYPE;
   l_saved_config_guid varchar2(32);
   BEGIN

  FOR saveRec in (
            SELECT  t.target_name target_name,t.target_type target_type,
            t.display_name display_target_name,
            t.type_display_name display_target_type,m.snapshot_type
            FROM mgmt_flat_target_assoc members, mgmt_targets t,
            mgmt_ecm_snapshot_metadata m
            WHERE members.is_membership = 1
            AND members.source_target_guid = l_comp_target_guid
            AND members.assoc_target_guid = t.target_guid
            AND m.target_type = t.target_type
            AND m.kind='P' AND m.compare_on='Y' and m.compare_ui_on='Y'
            UNION
            SELECT t.target_name target_name,t.target_type target_type,
            t.display_name display_target_name,
            t.type_display_name display_target_type,m.snapshot_type
            FROM mgmt_targets t, mgmt_ecm_snapshot_metadata m
            where t.target_name=v_target_name 
            AND   t.target_type=v_target_type 
            AND   m.target_type=v_target_type 
            AND   m.kind='P' AND m.compare_on='Y' AND m.compare_ui_on='Y'
            )
   LOOP
         l_save_result.extend;
            IF (saveRec.target_type='host') 
            THEN
             l_save_result(l_save_result.last) := save_composite_rec(
             saveRec.target_name,
             saveRec.target_type,
             saveRec.display_target_name,
             saveRec.display_target_type,
             saveRec.snapshot_type,
                l_collection_time,NULL,SAVE_COMPOSITE_HOST     
             );
            
            ELSE
                 BEGIN
                     -- currently this is going to work only for generic configs
                     -- not going to work for host configurations
                        l_saved_config_guid:=COPY_SNAPSHOT_GEN(saveRec.target_name,saveRec.target_type,
                        saveRec.snapshot_type,v_description);
                     --get the collection time to display in the save multiple config confirmation page.
                     SELECT START_TIMESTAMP INTO l_collection_time
                     FROM MGMT_ECM_GEN_SNAPSHOT
                     WHERE target_name=saveRec.target_name AND
                     target_type=saveRec.target_type AND
                     snapshot_type=saveRec.snapshot_type AND
                     is_current='Y';
                     
                     l_save_result(l_save_result.last) := save_composite_rec(
                     saveRec.target_name,
                     saveRec.target_type,
                     saveRec.display_target_name,
                     saveRec.display_target_type,
                     saveRec.snapshot_type,
                        l_collection_time,NULL,SAVE_COMPOSITE_SUC    
                     );
                 EXCEPTION
                    when others then
                     l_save_result(l_save_result.last) := save_composite_rec(
                     saveRec.target_name,
                     saveRec.target_type,
                     saveRec.display_target_name,
                     saveRec.display_target_type,
                     saveRec.snapshot_type,
                        NULL,SQLERRM,SAVE_COMPOSITE_FAIL     
                     );
                END;
           END IF;

    END LOOP;

  OPEN p_save_details FOR
   SELECT target_name,
          target_type,
          display_target_name,
          display_target_type,
          snapshot_type,
          collection_time,error_message,save_success
           FROM   TABLE(CAST(l_save_result AS save_composite_tab))
   ORDER BY target_name;

   END SAVE_COMPOSITE_CONFIG;

-- *********************************************************************

-- The ecm target deletion callback
PROCEDURE HANDLE_TARGET_DELETION (
    p_target_name VARCHAR2,
    p_target_type VARCHAR2,
    p_target_guid RAW) IS

BEGIN

  -- mark the current snapshots for deletion 
  UPDATE mgmt_ecm_gen_snapshot
     SET is_current = ECM_CT.G_IS_DELETED_TYPE
   WHERE target_guid = p_target_guid 
     AND is_current = ECM_CT.G_IS_CURRENT_TYPE;

  -- set target_guid to null in all snapshots for the deleted target
  UPDATE mgmt_ecm_gen_snapshot
     SET target_guid = NULL
   WHERE target_guid = p_target_guid;

  COMMIT;

END HANDLE_TARGET_DELETION;

-- *********************************************************************

-- Called by host config loading code to convert temporary snapshot to
-- current one and compute history
--
-- Inputs: v_snapshot_guid - guid of the temporary snapshot to be converted 

PROCEDURE HOST_CONFIG_POSTLOAD (v_snapshot_guid RAW) IS

-- information about the input snapshot, which is about to become current
l_target_name MGMT_ECM_SNAPSHOT.TARGET_NAME%TYPE;
l_target_type MGMT_ECM_SNAPSHOT.TARGET_TYPE%TYPE;
l_snapshot_type MGMT_ECM_SNAPSHOT.SNAPSHOT_TYPE%TYPE;
l_snapshot_type_version MGMT_ECM_SNAPSHOT.SNAPSHOT_TYPE_VERSION%TYPE;

-- used to determine lock conditions
l_lock_snapshot_list T_SNAP_GUID_TABLE;
l_lock_guid MGMT_ECM_SNAPSHOT.SNAPSHOT_GUID%TYPE := NULL;
l_current_snapshot_guid MGMT_ECM_SNAPSHOT.SNAPSHOT_GUID%TYPE := NULL;


BEGIN

    BEGIN
        SELECT target_name, target_type, snapshot_type, snapshot_type_version
          INTO l_target_name, l_target_type, l_snapshot_type,
               l_snapshot_type_version
          FROM mgmt_ecm_snapshot
         WHERE snapshot_guid = v_snapshot_guid;
    EXCEPTION
        -- snapshot no longer exists for some reason, so nothing to do 
        WHEN NO_DATA_FOUND THEN
           RETURN; 
    END;

    --insert an entry for this snapshot's components and containers into
    --mgmt_inv_summary
    INSERT INTO 
      mgmt_inv_summary 
        (
          snapshot_guid,
          comp_external_name, 
          comp_version, 
          container_location, 
          container_guid,
          is_patched,
          map_target_type,
          map_property_name,
          map_property_value)
      SELECT
        v_snapshot_guid,
        comp.external_name,
        case when ps_patch.version is null then comp.version else ps_patch.version end as version,
        cont.container_location,
        cont.container_guid,
        case  
          when 
            exists 
            ( 
              SELECT * 
              FROM mgmt_inv_patch p 
              WHERE p.container_guid = cont.container_guid 
            )
          then 1 else 0 
        end as is_patched,
        map.target_type,
        map.property_name,
        map.property_value
      FROM
        mgmt_inv_container cont,
        mgmt_inv_component comp,
        mgmt_inv_versioned_patch ps_patch,
        mgmt_target_type_component_map map
      WHERE
        cont.snapshot_guid = v_snapshot_guid
        and comp.container_guid = cont.container_guid
        and comp.component_guid = ps_patch.component_guid(+)
        and map.component_name = comp.name;
    
    COMMIT;    

--    update the patches column in mgmt_hc_os_summary based on whether or not the os is patched
    UPDATE mgmt_hc_os_summary
    SET patches = 
      (SELECT count(*)
       FROM mgmt_hc_os_components c
       WHERE c.snapshot_guid = v_snapshot_guid
       and c.type='Patch')
    WHERE
      snapshot_guid = v_snapshot_guid;

   COMMIT;

    BEGIN
        SELECT snapshot_guid INTO l_current_snapshot_guid
          FROM mgmt_ecm_snapshot
         WHERE snapshot_type = l_snapshot_type
           AND target_name = l_target_name
           AND target_type = l_target_type
           AND snapshot_type_version = l_snapshot_type_version
           AND is_current = ECM_CT.G_IS_CURRENT_TYPE
           FOR UPDATE OF is_current;
    EXCEPTION
        -- ignore when no current snapshot
        WHEN NO_DATA_FOUND THEN NULL;
    END;

    -- Lock on new row if no lock obtained on current snapshot
    IF (l_current_snapshot_guid IS NULL) THEN

        INSERT_SNAPSHOT_INFO (
            l_snapshot_type, l_snapshot_type_version,
            l_target_name, l_target_type,
            l_target_name, l_target_type,
            NULL, SYSDATE, -1,
            ECM_CT.G_IS_LOCKED_TYPE, NULL,
            l_lock_guid);
        COMMIT;
  
        BEGIN
            SELECT snapshot_guid BULK COLLECT INTO l_lock_snapshot_list
              FROM mgmt_ecm_snapshot
             WHERE snapshot_type = l_snapshot_type
               AND target_name = l_target_name
               AND target_type = l_target_type
               AND snapshot_type_version = l_snapshot_type_version
               AND is_current = ECM_CT.G_IS_LOCKED_TYPE
               FOR UPDATE OF is_current;
        -- bail out with error when lock is missing
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
              raise_application_error (MGMT_GLOBAL.SNAPSHOT_LOCK_ERR, 
                'Could not find just created host configuration lock row');
        END;

    END IF;

    -- make current snapshot, if any, into DELTA snapshot type
    -- If we had to create L snapshot above and meanwhile a
    -- current snapshot appeared, we would get the lock here.
    UPDATE mgmt_ecm_snapshot SET snapshot_type = 'DELTA' 
     WHERE snapshot_type = l_snapshot_type
       AND target_name = l_target_name
       AND target_type = l_target_type
       AND snapshot_type_version = l_snapshot_type_version
       AND is_current = ECM_CT.G_IS_CURRENT_TYPE;

    -- make input (temporary) snapshot into current
    UPDATE mgmt_ecm_snapshot SET is_current = ECM_CT.G_IS_CURRENT_TYPE
     WHERE snapshot_guid = v_snapshot_guid;

    -- compute history 
    MGMT_HC_DELTA.RECORD_HC_DIFFS(
        l_target_name, l_target_type, l_snapshot_type,
        l_target_name, l_target_type, 'DELTA', 'DELTA');

    -- prepare DELTA snapshot (i.e. old current snapshot) for deletion
    UPDATE mgmt_ecm_snapshot
       SET snapshot_type = l_snapshot_type,
           is_current = ECM_CT.G_IS_DELETED_TYPE
     WHERE snapshot_type = 'DELTA'
       AND target_name = l_target_name
       AND target_type = l_target_type
       AND snapshot_type_version = l_snapshot_type_version
       AND is_current = ECM_CT.G_IS_CURRENT_TYPE;

    COMMIT;

-- Dues to bug 4254731, this delete is removed.  It was identified as deadlocking
-- probably due to BLOCK locks.  Removing the code is ok becuase the entry
-- we be garbage collected when the HOST CONFIG ORPHANS are cleaned up.
-- It is a single entry with no children, so it should not be a space usage
-- problem.
--    -- If we created a lock snapshot, remove it now
--    IF (l_lock_guid IS NOT NULL) THEN
--
--        BEGIN
--            DELETE FROM mgmt_ecm_snapshot
--                WHERE snapshot_guid = l_lock_guid;
--            COMMIT;
--
--        -- bail out when nothing to do
--        EXCEPTION
--            WHEN NO_DATA_FOUND THEN NULL;
--        END;
--
--    END IF;

END HOST_CONFIG_POSTLOAD;

-- *********************************************************************

PROCEDURE HOST_CONFIG_TARGET_DELETION (
    p_target_name VARCHAR2,
    p_target_type VARCHAR2,
    p_target_guid RAW) IS

BEGIN

  -- delete host config snapshot
  UPDATE mgmt_ecm_snapshot
     SET is_current = ECM_CT.G_IS_DELETED_TYPE
   WHERE target_name = p_target_name
     AND target_type = p_target_type
     AND is_current = 'Y';
  COMMIT;

  UPDATE mgmt_ecm_savedhostconfig
   SET target_guid = NULL
   WHERE target_guid = p_target_guid;
  COMMIT;

END HOST_CONFIG_TARGET_DELETION;

-- *********************************************************************

-- Test post load procedure. Call the procedure assuming there is a (single)
-- temporary snapshot for a given target name / type and snapshot type

PROCEDURE TEST_POST_LOAD (
    p_target_name VARCHAR2,
    p_target_type VARCHAR2,
    p_snapshot_type VARCHAR2) IS

v_in1 SMP_EMD_NVPAIR_ARRAY;
v_in2 SMP_EMD_NVPAIR_ARRAY;

v_target_guid MGMT_ECM_GEN_SNAPSHOT.TARGET_GUID%TYPE;
v_snapshot_guid MGMT_ECM_GEN_SNAPSHOT.SNAPSHOT_GUID%TYPE;

BEGIN

SELECT snapshot_guid, target_guid INTO v_snapshot_guid, v_target_guid
  FROM MGMT_ECM_GEN_SNAPSHOT
  WHERE target_name = p_target_name
    AND target_type = p_target_type
    AND snapshot_type = p_snapshot_type
    AND is_current = ECM_CT.G_IS_TRANSIENT_TYPE;

v_in1 := SMP_EMD_NVPAIR_ARRAY();
v_in1.extend(4);
v_in1(1) := SMP_EMD_NVPAIR(ECM_CT.G_COLLECTION_NAME, p_snapshot_type);
v_in1(2) := SMP_EMD_NVPAIR(ECM_CT.G_TARGET_GUID, v_target_guid);
v_in1(3) := SMP_EMD_NVPAIR(ECM_CT.G_COLLECTION_TIMESTAMP, SYSDATE());
v_in1(4) := SMP_EMD_NVPAIR(ECM_CT.G_CONFIG_METADATA_VERSION, '1.0');

v_in2 := SMP_EMD_NVPAIR_ARRAY();
v_in2.extend(1);
v_in2(1) := SMP_EMD_NVPAIR(ECM_CT.G_ECM_SNAPSHOT_ID, v_snapshot_guid);

POSTLOAD_CALLBACK(v_in1, v_in2);

END TEST_POST_LOAD;

-- *********************************************************************

-- Test
PROCEDURE AFTER_LOAD_PROC (
    v_snapshot_guid IN RAW,
    v_afterLoadInput IN SMP_EMD_NVPAIR_ARRAY) IS
BEGIN

  IF (ECM_CT.G_IS_DEBUG = 1) THEN
    CT_LOG('-- Begin AFTER_LOAD_PROC');
    CT_LOG('-->   snapshot_guid = ''' || v_snapshot_guid || '''');
    CT_LOG('-- End AFTER_LOAD_PROC');
  END IF;

END AFTER_LOAD_PROC;

-- *********************************************************************

-- Test
PROCEDURE AFTER_IMPORT_PROC (
    v_snapshot_guid IN RAW,
    v_afterImportInput IN SMP_EMD_NVPAIR_ARRAY) IS
BEGIN

  IF (ECM_CT.G_IS_DEBUG = 1) THEN
    CT_LOG('-- Begin AFTER_IMPORT_PROC');
    CT_LOG('-->   snapshot_guid = ''' || v_snapshot_guid || '''');
    CT_LOG('-- End AFTER_IMPORT_PROC');
  END IF;

END AFTER_IMPORT_PROC;

-- *********************************************************************

PROCEDURE GET_HOST_CONFIG_INFO (
    v_sguid IN VARCHAR2,
    v_target_name IN VARCHAR2,
    v_target_type IN VARCHAR2,
    p_isStale OUT VARCHAR2,
    p_systemConfig OUT VARCHAR2,
    p_hardwareProvider OUT VARCHAR2,
    p_numberOfCpus OUT INTEGER,
    p_memory OUT INTEGER,
    osName OUT VARCHAR2,
    l_home_list OUT MGMT_ORACLESW_LIST,
    l_vendor_list OUT MGMT_VENDORSW_LIST,
    p_comps OUT sys_refcursor)
IS
p_snaprow NUMBER := 0;

BEGIN
  p_isStale := 'FALSE';

  BEGIN
    SELECT 1 INTO p_snaprow
       FROM MGMT_ECM_SNAPSHOT
    WHERE snapshot_guid = HEXTORAW(v_sguid);
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      p_isStale := 'TRUE';
  END;
  
   open p_comps for 
    SELECT component_name, collection_status, collection_message FROM MGMT_ECM_SNAP_COMPONENT_INFO WHERE snapshot_guid = HEXTORAW(v_sguid) ;

    BEGIN
        SELECT s.system_config, s.vendor_name, s.cpu_count, s.memory_size_in_mb
          into p_systemConfig, p_hardwareProvider,p_numberOfCpus,p_memory
        FROM MGMT_HC_HARDWARE_MASTER s
          WHERE s.snapshot_guid = HEXTORAW(v_sguid) ;
    EXCEPTION 
      WHEN NO_DATA_FOUND THEN NULL;
    END;

    BEGIN
        SELECT
         os.name || ' '|| os.base_version ||
         decode(os.update_level, null,'',' '|| os.update_level) ||
         decode ( os.address_length_in_bits, null, '', ' (' ||
         os.address_length_in_bits || ')')
           into osName
        FROM MGMT_HC_OS_SUMMARY os
      WHERE snapshot_guid = HEXTORAW(v_sguid);
    EXCEPTION
       WHEN NO_DATA_FOUND THEN NULL;
    END;

    BEGIN
      SELECT MGMT_ORACLESW_RECORD(comp.name,
           comp.external_name,
           decode(patch.version, NULL, comp.version, patch.version),
           comp.version,
           cont.container_location,
           cont.container_name,
           comp.TIMESTAMP,
           decode(advisory.home_location, NULL, 0, 1))
        BULK COLLECT INTO l_home_list
           FROM
               MGMT_INV_COMPONENT  comp,
               mgmt_inv_container  cont,
               MGMT_INV_VERSIONED_PATCH patch,
               (SELECT /*+ index(hrv) */ unique hrv.column_str_value as home_location
                   FROM
                     mgmt_policies p,
                     mgmt_current_violation v,                     
                     mgmt_violation_context hrv,
                     mgmt_targets t
                   WHERE  t.target_name = v_target_name
                     AND t.target_type = v_target_type
                     AND p.author = 'ORACLE'
                     AND p.target_type = 'host'
                     AND p.policy_name = 'Critical Patch Advisories for Oracle Homes'
                     AND p.policy_guid = v.policy_guid
					 AND (v.exempt_code = 0 or
                       (v.exempt_code = 2 and v.exempt_until <= SYSDATE))     
                     AND v.target_guid = hrv.target_guid 
           	         AND v.policy_guid = hrv.policy_guid  
                     AND v.key_value = hrv.key_value 
                     AND hrv.column_name = 'HOME_LOCATION' ) advisory
           WHERE comp.is_top_level = 'Y'
              and comp.component_guid = patch.component_guid(+)
              and comp.container_guid = cont.container_guid
              and cont.snapshot_guid = HEXTORAW(v_sguid)
              and cont.container_location = advisory.home_location(+)
            GROUP BY comp.name, comp.external_name,
                     decode(patch.version, NULL, comp.version, patch.version),
                     comp.version, cont.container_location, cont.container_name,
                     comp.TIMESTAMP, advisory.home_location;
      EXCEPTION 
        WHEN NO_DATA_FOUND THEN NULL;
      END;                          

    BEGIN
      SELECT MGMT_VENDORSW_RECORD(s.name, s.vendor_name, s.version,
                                s.installed_location, count(*), s.installation_date)
     BULK COLLECT INTO l_vendor_list
       FROM MGMT_HC_VENDOR_SW_SUMMARY s
       WHERE s.snapshot_guid = HEXTORAW(v_sguid)
       GROUP BY s.name, s.vendor_name, s.version, s.installed_location,s.installation_date;
     EXCEPTION 
      WHEN NO_DATA_FOUND THEN NULL;
    END;      

END GET_HOST_CONFIG_INFO;

PROCEDURE GET_HW_INFO (
    v_sguid IN VARCHAR2,
    p_isStale OUT VARCHAR2,
    l_hwgenlist OUT MGMT_HWGEN_LIST,
    l_cpulist OUT MGMT_CPUDETAILS_LIST,
    l_iocardlist OUT MGMT_IOCARDDETAILS_LIST,
    l_niclist OUT MGMT_NICDETAILS_LIST
    )
IS
p_snaprow NUMBER := 0;
BEGIN
  p_isStale := 'FALSE';

  BEGIN
    SELECT 1 INTO p_snaprow
       FROM MGMT_ECM_SNAPSHOT
    WHERE snapshot_guid = HEXTORAW(v_sguid);
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      p_isStale := 'TRUE';
  END;

  BEGIN
    select MGMT_HWGEN_RECORD
       (system_config, machine_architecture, vendor_name, memory_size_in_mb,
        local_disk_space_in_gb, clock_freq_in_mhz, cpu_count, cpu_board_count,
       iocard_count, fan_count, power_supply_count)
      BULK COLLECT INTO l_hwgenlist
        from MGMT_HC_HARDWARE_MASTER where snapshot_guid = HEXTORAW(v_sguid);
    EXCEPTION 
      WHEN NO_DATA_FOUND THEN NULL;
    END;

  BEGIN    
  select MGMT_CPUDETAILS_RECORD
     (freq_in_mhz, vendor_name, revision, ecache_in_mb, impl, mask )
    BULK COLLECT INTO l_cpulist
    from MGMT_HC_CPU_DETAILS where snapshot_guid = HEXTORAW(v_sguid);
    EXCEPTION 
      WHEN NO_DATA_FOUND THEN NULL;
    END;

  BEGIN
  select MGMT_IOCARDDETAILS_RECORD
       (name, vendor_name, bus, freq_in_mhz, revision )
    BULK COLLECT INTO l_iocardlist
     from MGMT_HC_IOCARD_DETAILS where snapshot_guid = HEXTORAW(v_sguid);
    EXCEPTION 
      WHEN NO_DATA_FOUND THEN NULL;
    END;

  BEGIN
    select MGMT_NICDETAILS_RECORD
   ( name, inet_address, max_transfer_unit, broadcast_address, mask,
       flags, mac_address, hostname_aliases )
           BULK COLLECT INTO l_niclist
     from MGMT_HC_NIC_DETAILS where snapshot_guid = HEXTORAW(v_sguid);
    EXCEPTION 
      WHEN NO_DATA_FOUND THEN NULL;
    END;

END GET_HW_INFO;


END ECM_CT;
/
show errors;