diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/.gitignore b/MARS_Packages/REL04/MARS-1443-PREHOOK/.gitignore new file mode 100644 index 0000000..da39485 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/.gitignore @@ -0,0 +1,6 @@ +# Exclude temporary folders from version control +confluence/ +log/ +test/ +mock_data/ +*.log diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/01_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_SPEC.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/01_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_SPEC.sql new file mode 100644 index 0000000..8fbc381 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/01_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_SPEC.sql @@ -0,0 +1,29 @@ +--============================================================================================================================= +-- MARS-1443-PREHOOK: Install FILE_MANAGER Package Specification +--============================================================================================================================= +-- Purpose: Deploy FILE_MANAGER package specification +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Related: MARS-1443-PREHOOK +--============================================================================================================================= + +SET SERVEROUTPUT ON + +PROMPT ======================================================================== +PROMPT Installing FILE_MANAGER Package Specification +PROMPT ======================================================================== + +@@new_version/FILE_MANAGER.pkg + +-- Verify compilation status (check specific schema when installing as ADMIN) +SELECT object_name, object_type, status +FROM ALL_OBJECTS +WHERE OWNER = 'CT_MRDS' + AND object_name = 'FILE_MANAGER' + AND object_type = 'PACKAGE'; + +PROMPT SUCCESS: FILE_MANAGER package specification installed + +--============================================================================================================================= +-- End of Script +--============================================================================================================================= diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/02_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_BODY.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/02_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_BODY.sql new file mode 100644 index 0000000..e2ab008 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/02_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_BODY.sql @@ -0,0 +1,36 @@ +--============================================================================================================================= +-- MARS-1443-PREHOOK: Install FILE_MANAGER Package Body +--============================================================================================================================= +-- Purpose: Deploy FILE_MANAGER package body +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Related: MARS-1443-PREHOOK +--============================================================================================================================= + +SET SERVEROUTPUT ON + +PROMPT ======================================================================== +PROMPT Installing FILE_MANAGER Package Body +PROMPT ======================================================================== + +@@new_version/FILE_MANAGER.pkb + +-- Verify compilation status (check specific schema when installing as ADMIN) +SELECT object_name, object_type, status +FROM ALL_OBJECTS +WHERE OWNER = 'CT_MRDS' + AND object_name = 'FILE_MANAGER' + AND object_type = 'PACKAGE BODY'; + +-- Check for compilation errors +SELECT * +FROM ALL_ERRORS +WHERE OWNER = 'CT_MRDS' + AND NAME = 'FILE_MANAGER' + AND TYPE = 'PACKAGE BODY'; + +PROMPT SUCCESS: FILE_MANAGER package body installed + +--============================================================================================================================= +-- End of Script +--============================================================================================================================= diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/03_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_SPEC.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/03_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_SPEC.sql new file mode 100644 index 0000000..77507e9 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/03_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_SPEC.sql @@ -0,0 +1,44 @@ +-- =================================================================== +-- MARS-1443-PREHOOK: Install ODS.FILE_MANAGER_ODS Package Specification +-- =================================================================== +-- Purpose: Deploy FILE_MANAGER_ODS package specification +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Package: ODS.FILE_MANAGER_ODS + +SET ECHO ON +SET DEFINE OFF + +PROMPT Installing ODS.FILE_MANAGER_ODS Package Specification... + +@@new_version/FILE_MANAGER_ODS.pkg + +PROMPT Checking for compilation errors... +SELECT + LINE, + POSITION, + TEXT AS ERROR_MESSAGE +FROM ALL_ERRORS +WHERE OWNER = 'ODS' + AND NAME = 'FILE_MANAGER_ODS' + AND TYPE = 'PACKAGE' +ORDER BY SEQUENCE; + +PROMPT Verifying package status... +SELECT + OBJECT_NAME, + OBJECT_TYPE, + STATUS, + TO_CHAR(LAST_DDL_TIME, 'YYYY-MM-DD HH24:MI:SS') AS LAST_MODIFIED +FROM ALL_OBJECTS +WHERE OWNER = 'ODS' + AND OBJECT_NAME = 'FILE_MANAGER_ODS' + AND OBJECT_TYPE = 'PACKAGE'; + +PROMPT +PROMPT FILE_MANAGER_ODS Package Specification installation completed. +PROMPT + +SET DEFINE ON + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/04_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_BODY.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/04_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_BODY.sql new file mode 100644 index 0000000..6790062 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/04_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_BODY.sql @@ -0,0 +1,45 @@ +-- =================================================================== +-- MARS-1443-PREHOOK: Install ODS.FILE_MANAGER_ODS Package Body +-- =================================================================== +-- Purpose: Deploy FILE_MANAGER_ODS package body +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Package: ODS.FILE_MANAGER_ODS + +SET ECHO ON +SET DEFINE OFF + +PROMPT Installing ODS.FILE_MANAGER_ODS Package Body... + +@@new_version/FILE_MANAGER_ODS.pkb + +PROMPT Checking for compilation errors... +SELECT + LINE, + POSITION, + TEXT AS ERROR_MESSAGE +FROM ALL_ERRORS +WHERE OWNER = 'ODS' + AND NAME = 'FILE_MANAGER_ODS' + AND TYPE = 'PACKAGE BODY' +ORDER BY SEQUENCE; + +PROMPT Verifying package status... +SELECT + OBJECT_NAME, + OBJECT_TYPE, + STATUS, + TO_CHAR(LAST_DDL_TIME, 'YYYY-MM-DD HH24:MI:SS') AS LAST_MODIFIED +FROM ALL_OBJECTS +WHERE OWNER = 'ODS' + AND OBJECT_NAME = 'FILE_MANAGER_ODS' + AND OBJECT_TYPE IN ('PACKAGE', 'PACKAGE BODY') +ORDER BY OBJECT_TYPE; + +PROMPT +PROMPT FILE_MANAGER_ODS Package Body installation completed. +PROMPT + +SET DEFINE ON + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/91_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_SPEC.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/91_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_SPEC.sql new file mode 100644 index 0000000..fc75bbe --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/91_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_SPEC.sql @@ -0,0 +1,29 @@ +--============================================================================================================================= +-- MARS-1443-PREHOOK: Rollback FILE_MANAGER Package Specification +--============================================================================================================================= +-- Purpose: Restore FILE_MANAGER package specification to version before MARS-1443-PREHOOK changes +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Related: MARS-1443-PREHOOK (ROLLBACK) +--============================================================================================================================= + +SET SERVEROUTPUT ON + +PROMPT ======================================================================== +PROMPT Rolling back FILE_MANAGER Package Specification +PROMPT ======================================================================== + +@@rollback_version/FILE_MANAGER.pkg + +-- Verify compilation status (check specific schema when installing as ADMIN) +SELECT object_name, object_type, status +FROM ALL_OBJECTS +WHERE OWNER = 'CT_MRDS' + AND object_name = 'FILE_MANAGER' + AND object_type = 'PACKAGE'; + +PROMPT SUCCESS: FILE_MANAGER package specification rolled back to v3.3.0 + +--============================================================================================================================= +-- End of Script +--============================================================================================================================= diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/92_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_BODY.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/92_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_BODY.sql new file mode 100644 index 0000000..5b6b627 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/92_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_BODY.sql @@ -0,0 +1,36 @@ +--============================================================================================================================= +-- MARS-1443-PREHOOK: Rollback FILE_MANAGER Package Body +--============================================================================================================================= +-- Purpose: Restore FILE_MANAGER package body to version before MARS-1443-PREHOOK changes +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Related: MARS-1443-PREHOOK (ROLLBACK) +--============================================================================================================================= + +SET SERVEROUTPUT ON + +PROMPT ======================================================================== +PROMPT Rolling back FILE_MANAGER Package Body +PROMPT ======================================================================== + +@@rollback_version/FILE_MANAGER.pkb + +-- Verify compilation status (check specific schema when installing as ADMIN) +SELECT object_name, object_type, status +FROM ALL_OBJECTS +WHERE OWNER = 'CT_MRDS' + AND object_name = 'FILE_MANAGER' + AND object_type = 'PACKAGE BODY'; + +-- Check for compilation errors +SELECT * +FROM ALL_ERRORS +WHERE OWNER = 'CT_MRDS' + AND NAME = 'FILE_MANAGER' + AND TYPE = 'PACKAGE BODY'; + +PROMPT SUCCESS: FILE_MANAGER package body rolled back to v3.3.0 + +--============================================================================================================================= +-- End of Script +--============================================================================================================================= diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/93_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_BODY.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/93_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_BODY.sql new file mode 100644 index 0000000..0ebcd31 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/93_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_BODY.sql @@ -0,0 +1,45 @@ +-- =================================================================== +-- MARS-1443-PREHOOK: Rollback ODS.FILE_MANAGER_ODS Package Body +-- =================================================================== +-- Purpose: Restore FILE_MANAGER_ODS package body to version before MARS-1443-PREHOOK changes +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Package: ODS.FILE_MANAGER_ODS + +SET ECHO ON +SET DEFINE OFF + +PROMPT Rolling back ODS.FILE_MANAGER_ODS Package Body... + +@@rollback_version/FILE_MANAGER_ODS.pkb + +PROMPT Checking for compilation errors... +SELECT + LINE, + POSITION, + TEXT AS ERROR_MESSAGE +FROM ALL_ERRORS +WHERE OWNER = 'ODS' + AND NAME = 'FILE_MANAGER_ODS' + AND TYPE = 'PACKAGE BODY' +ORDER BY SEQUENCE; + +PROMPT Verifying package status... +SELECT + OBJECT_NAME, + OBJECT_TYPE, + STATUS, + TO_CHAR(LAST_DDL_TIME, 'YYYY-MM-DD HH24:MI:SS') AS LAST_MODIFIED +FROM ALL_OBJECTS +WHERE OWNER = 'ODS' + AND OBJECT_NAME = 'FILE_MANAGER_ODS' + AND OBJECT_TYPE IN ('PACKAGE', 'PACKAGE BODY') +ORDER BY OBJECT_TYPE; + +PROMPT +PROMPT FILE_MANAGER_ODS Package Body rollback completed. +PROMPT + +SET DEFINE ON + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/94_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_SPEC.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/94_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_SPEC.sql new file mode 100644 index 0000000..35740df --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/94_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_SPEC.sql @@ -0,0 +1,44 @@ +-- =================================================================== +-- MARS-1443-PREHOOK: Rollback ODS.FILE_MANAGER_ODS Package Specification +-- =================================================================== +-- Purpose: Restore FILE_MANAGER_ODS package specification to version before MARS-1443-PREHOOK changes +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 +-- Package: ODS.FILE_MANAGER_ODS + +SET ECHO ON +SET DEFINE OFF + +PROMPT Rolling back ODS.FILE_MANAGER_ODS Package Specification... + +@@rollback_version/FILE_MANAGER_ODS.pkg + +PROMPT Checking for compilation errors... +SELECT + LINE, + POSITION, + TEXT AS ERROR_MESSAGE +FROM ALL_ERRORS +WHERE OWNER = 'ODS' + AND NAME = 'FILE_MANAGER_ODS' + AND TYPE = 'PACKAGE' +ORDER BY SEQUENCE; + +PROMPT Verifying package status... +SELECT + OBJECT_NAME, + OBJECT_TYPE, + STATUS, + TO_CHAR(LAST_DDL_TIME, 'YYYY-MM-DD HH24:MI:SS') AS LAST_MODIFIED +FROM ALL_OBJECTS +WHERE OWNER = 'ODS' + AND OBJECT_NAME = 'FILE_MANAGER_ODS' + AND OBJECT_TYPE = 'PACKAGE'; + +PROMPT +PROMPT FILE_MANAGER_ODS Package Specification rollback completed. +PROMPT + +SET DEFINE ON + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/install_mars1443_prehook.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/install_mars1443_prehook.sql new file mode 100644 index 0000000..ddf4e2f --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/install_mars1443_prehook.sql @@ -0,0 +1,94 @@ +-- =================================================================== +-- MARS-1443-PREHOOK INSTALL SCRIPT +-- =================================================================== +-- Purpose: Pre-hook installation script for MARS-1443 +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 + +-- Dynamic spool file generation (using SYS_CONTEXT - no DBA privileges required) +-- Log files are automatically created in log/ subdirectory +-- IMPORTANT: Ensure log/ directory exists before SPOOL (use host mkdir) +host mkdir log 2>nul + +var filename VARCHAR2(100) +BEGIN + :filename := 'log/INSTALL_MARS_1443_PREHOOK_' || SYS_CONTEXT('USERENV', 'CON_NAME') || '_' || TO_CHAR(SYSDATE,'YYYYMMDD_HH24MISS') || '.log'; +END; +/ +column filename new_value _filename +select :filename filename from dual; +spool &_filename + +SET ECHO OFF +SET TIMING ON +SET SERVEROUTPUT ON SIZE UNLIMITED +SET PAUSE OFF + +-- Set current schema context (optional - use when modifying packages in specific schema) +-- ALTER SESSION SET CURRENT_SCHEMA = CT_MRDS; + +PROMPT ========================================================================= +PROMPT MARS-1443-PREHOOK: Pre-hook Installation +PROMPT ========================================================================= +PROMPT +PROMPT Expected Duration: 1-2 minutes +PROMPT ========================================================================= + +-- Confirm installation with user +ACCEPT continue CHAR PROMPT 'Type YES to continue with installation, or Ctrl+C to abort: ' +WHENEVER SQLERROR EXIT SQL.SQLCODE +BEGIN + IF '&continue' IS NULL OR TRIM('&continue') IS NULL OR UPPER(TRIM('&continue')) != 'YES' THEN + RAISE_APPLICATION_ERROR(-20001, 'Installation aborted by user'); + END IF; +END; +/ +WHENEVER SQLERROR CONTINUE + +PROMPT +PROMPT ========================================================================= +PROMPT Step 1: Install FILE_MANAGER Package Specification v3.5.0 +PROMPT ========================================================================= +@@01_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_SPEC.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 2: Install FILE_MANAGER Package Body v3.5.0 +PROMPT ========================================================================= +@@02_MARS_1443_PREHOOK_install_CT_MRDS_FILE_MANAGER_BODY.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 3: Install FILE_MANAGER_ODS Package Specification v2.4.0 +PROMPT ========================================================================= +@@03_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_SPEC.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 4: Install FILE_MANAGER_ODS Package Body v2.4.0 +PROMPT ========================================================================= +@@04_MARS_1443_PREHOOK_install_ODS_FILE_MANAGER_ODS_BODY.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 5: Track Package Versions +PROMPT ========================================================================= +@@track_package_versions.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 6: Verify All Tracked Packages +PROMPT ========================================================================= +@@verify_packages_version.sql + +PROMPT +PROMPT ========================================================================= +PROMPT MARS-1443-PREHOOK Installation - COMPLETED +PROMPT ========================================================================= +PROMPT Check the log file for complete installation details. +PROMPT Log file: log/INSTALL_MARS_1443_PREHOOK__.log +PROMPT ========================================================================= + +spool off + +quit; diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER.pkb b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER.pkb new file mode 100644 index 0000000..74b686e --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER.pkb @@ -0,0 +1,2097 @@ +create or replace PACKAGE BODY CT_MRDS.FILE_MANAGER +AS + + ---------------------------------------------------------------------------------------------------- + -- PRIVATE FUNCTION: NORMALIZE_DATE_FORMAT + ---------------------------------------------------------------------------------------------------- + /** + * Purpose: Normalize Oracle date format strings for use in external tables + * + * Problem: ISO 8601 formats like 'YYYY-MM-DDTHH24:MI:SS.FF3TZH:TZM' fail because + * literal character 'T' must be enclosed in double quotes for Oracle + * external table DATE column definitions. + * + * Solution: Detect unquoted 'T' separator and wrap it in double quotes + * + * Parameters: + * pDateFormat - Original date format from A_COLUMN_DATE_FORMAT table + * + * Returns: Normalized format with quoted 'T' if applicable + * + * Examples: + * Input: 'YYYY-MM-DDTHH24:MI:SS.FF3TZH:TZM' + * Output: 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM' + * + * Input: 'DD/MM/YYYY HH24:MI:SS' (no T) + * Output: 'DD/MM/YYYY HH24:MI:SS' (unchanged) + * + * Input: 'YYYY-MM-DD"T"HH24:MI:SS' (already quoted) + * Output: 'YYYY-MM-DD"T"HH24:MI:SS' (unchanged) + * + * Author: Grzegorz Michalski + * Date: 2025-11-27 + * Version: 1.0.0 (MARS-1046) + */ + FUNCTION NORMALIZE_DATE_FORMAT(pDateFormat VARCHAR2) RETURN VARCHAR2 IS + vNormalizedFormat VARCHAR2(500); + BEGIN + -- Return NULL if input is NULL + IF pDateFormat IS NULL THEN + RETURN NULL; + END IF; + + vNormalizedFormat := pDateFormat; + + -- Check if 'T' separator exists and is NOT already quoted + -- Pattern: [YMD]T[HM] (date component + T + time component) + IF INSTR(vNormalizedFormat, '"T"') = 0 AND + REGEXP_LIKE(vNormalizedFormat, '[YMD]T[HM]') THEN + + -- Wrap 'T' in double quotes using regex replace + -- Pattern matches: (date format char) + T + (time format char) + -- Replacement: \1 + "T" + \2 + vNormalizedFormat := REGEXP_REPLACE(vNormalizedFormat, '([YMD])T([HM])', '\1"T"\2'); + END IF; + + RETURN vNormalizedFormat; + + EXCEPTION + WHEN OTHERS THEN + -- If normalization fails, return original format (safety fallback) + RETURN pDateFormat; + END NORMALIZE_DATE_FORMAT; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_SOURCE_FILE_CONFIG(pFileUri IN VARCHAR2 DEFAULT NULL + , pSourceFileReceivedKey IN NUMBER DEFAULT NULL + , pSourceFileConfigKey IN NUMBER DEFAULT NULL) + RETURN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pFileUri => '''||nvl(pFileUri,'NULL')||'''' + ,'pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey),'NULL') + ,'pSourceFileConfigKey => '||nvl(to_char(pSourceFileConfigKey),'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + + BEGIN + IF pFileUri IS NOT NULL THEN + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE REGEXP_LIKE(pFileUri, A_SOURCE_KEY||'/'||SOURCE_FILE_ID||'/'||TABLE_ID||'/'||SOURCE_FILE_NAME_PATTERN); + ELSIF pSourceFileReceivedKey IS NOT NULL THEN + SELECT T.* + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG T, CT_MRDS.A_SOURCE_FILE_RECEIVED R + WHERE T.A_SOURCE_FILE_CONFIG_KEY = R.A_SOURCE_FILE_CONFIG_KEY + AND R.A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + ELSIF pSourceFileConfigKey IS NOT NULL THEN + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE A_SOURCE_FILE_CONFIG_KEY = pSourceFileConfigKey; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + END IF; + -- Set global package variable vgSourceFileConfigKey - used in error messages + vgSourceFileConfigKey := vSourceFileConfig.A_SOURCE_FILE_CONFIG_KEY; + EXCEPTION + WHEN ENV_MANAGER.ERR_EMPTY_FILEURI_AND_RECKEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH_FOR_FILEURI, ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI); + + WHEN TOO_MANY_ROWS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE, ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE); + + WHEN OTHERS THEN + -- Log complete error details including full stack trace and backtrace + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_UNKNOWN, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END; + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vSourceFileConfig; + + END GET_SOURCE_FILE_CONFIG; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey IN NUMBER DEFAULT NULL) + -- + -- Get source file received info + -- + RETURN tSourceFileReceived + IS + vSourceFileReceived tSourceFileReceived; + vBucket VARCHAR2(400); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey),'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + BEGIN + SELECT R.A_SOURCE_FILE_RECEIVED_KEY, R.A_SOURCE_FILE_CONFIG_KEY, + 'INBOX'||'/'||T.A_SOURCE_KEY||'/'||T.SOURCE_FILE_ID||'/'||T.TABLE_ID||'/' as SOURCE_FILE_PREFIX_INBOX, + 'ODS'||'/'||T.A_SOURCE_KEY||'/'||T.TABLE_ID||'/' as SOURCE_FILE_PREFIX_ODS, + 'QUARANTINE'||'/'||T.A_SOURCE_KEY||'/'||T.TABLE_ID||'/' as SOURCE_FILE_PREFIX_QUARANTINE, + 'ARCHIVE'||'/'||T.A_SOURCE_KEY||'/'||T.SOURCE_FILE_ID||'/' as SOURCE_FILE_PREFIX_ARCHIVE, + R.SOURCE_FILE_NAME, + R.RECEPTION_DATE, R.PROCESSING_STATUS, R.EXTERNAL_TABLE_NAME + INTO vSourceFileReceived + FROM CT_MRDS.A_SOURCE_FILE_RECEIVED R, CT_MRDS.A_SOURCE_FILE_CONFIG T + WHERE R.A_SOURCE_FILE_CONFIG_KEY = T.A_SOURCE_FILE_CONFIG_KEY + AND A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE); + WHEN TOO_MANY_ROWS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MULTI_CONFIG_FOR_RECEIVED_FILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTI_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_MULTI_CONFIG_FOR_RECEIVED_FILE); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END; + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + RETURN vSourceFileReceived; + + END GET_SOURCE_FILE_RECEIVED_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName IN VARCHAR2) + RETURN PLS_INTEGER + -- + -- Register a newly received source file A_SOURCE_FILE_RECEIVED + -- This overload automatically determines source file type from the file name + -- + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vSourceFileReceivedKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '''||nvl(pSourceFileReceivedName, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO',vParameters); + + vSourceFileConfig := GET_SOURCE_FILE_CONFIG(pSourceFileReceivedName); + vSourceFileReceivedKey := REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName, vSourceFileConfig); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + RETURN vSourceFileReceivedKey; + EXCEPTION + + WHEN ENV_MANAGER.ERR_EMPTY_FILEURI_AND_RECKEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + + WHEN ENV_MANAGER.ERR_NO_CONFIG_MATCH_FOR_FILEURI THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH_FOR_FILEURI, ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI); + + WHEN ENV_MANAGER.ERR_FILE_NOT_EXISTS_ON_CLOUD THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_EXISTS_ON_CLOUD, ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD); + + WHEN ENV_MANAGER.ERR_FILE_ALREADY_REGISTERED THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED, ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED); + + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_UNKNOWN, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END REGISTER_SOURCE_FILE_RECEIVED; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION REGISTER_SOURCE_FILE_RECEIVED( + pSourceFileReceivedName IN VARCHAR2 + ,pSourceFileConfig IN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE) + RETURN PLS_INTEGER + -- + -- Register a newly received source file A_SOURCE_FILE_RECEIVED + -- + IS + vExternalTableName VARCHAR2(200); + vDirName VARCHAR2(1000); + vFileName VARCHAR2(1000); + vChecksum A_SOURCE_FILE_RECEIVED.CHECKSUM%TYPE; + vCreated A_SOURCE_FILE_RECEIVED.CREATED%TYPE; + vBytes A_SOURCE_FILE_RECEIVED.BYTES%TYPE; + vSourceFileReceivedKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vRow CT_MRDS.A_SOURCE_FILE_RECEIVED%ROWTYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '''||nvl(pSourceFileReceivedName, 'NULL')||'''' + ,'pSourceFileConfig => '||'tSourceFileConfig record type')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + vDirName := REGEXP_SUBSTR(pSourceFileReceivedName, '(.*/)(.*)', 1, 1, NULL, 1); + -- Remove prefix from file name + vFileName := REGEXP_SUBSTR(pSourceFileReceivedName,'[^/]*$'); + + ENV_MANAGER.LOG_PROCESS_EVENT('gvCredentialName','DEBUG',ENV_MANAGER.gvCredentialName); + ENV_MANAGER.LOG_PROCESS_EVENT('gvInboxBucketUri','DEBUG',ENV_MANAGER.gvInboxBucketUri); + ENV_MANAGER.LOG_PROCESS_EVENT('vDirName','DEBUG',vDirName); + + SELECT + checksum, created, bytes + INTO + vChecksum, vCreated, vBytes + FROM DBMS_CLOUD.LIST_OBJECTS(ENV_MANAGER.gvCredentialName, + ENV_MANAGER.gvInboxBucketUri || vDirName + ) + WHERE object_name = vFileName + ; + vSourceFileReceivedKey := CT_MRDS.A_SOURCE_FILE_RECEIVED_KEY_SEQ.NEXTVAL; + vExternalTableName := REPLACE( + REGEXP_SUBSTR(pSourceFileConfig.TEMPLATE_TABLE_NAME||'_'||vSourceFileReceivedKey, + '\..*'), + '.',''); + + INSERT INTO CT_MRDS.A_SOURCE_FILE_RECEIVED + (A_SOURCE_FILE_RECEIVED_KEY, A_SOURCE_FILE_CONFIG_KEY, + SOURCE_FILE_NAME, RECEPTION_DATE, + PROCESSING_STATUS, EXTERNAL_TABLE_NAME, + CHECKSUM, CREATED, BYTES) + VALUES (vSourceFileReceivedKey, pSourceFileConfig.A_SOURCE_FILE_CONFIG_KEY, + vFileName, SYSDATE, + 'RECEIVED', vExternalTableName, + vChecksum, vCreated, vBytes); + COMMIT; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vSourceFileReceivedKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + vgMsgTmp := ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD + ||cgBL||' '||'File: '||ENV_MANAGER.gvInboxBucketUri || vDirName || vFileName; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_EXISTS_ON_CLOUD, vgMsgTmp); + + WHEN DUP_VAL_ON_INDEX THEN + select * into vRow + from CT_MRDS.A_SOURCE_FILE_RECEIVED + where CHECKSUM = vChecksum + and CREATED = vCreated + and BYTES = vBytes + ; + vgMsgTmp := ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED + ||cgBL||' '||'Details about existing File: ' + ||cgBL||' '||'-------------------------' + ||cgBL||' '||'A_SOURCE_FILE_RECEIVED_KEY = '||vRow.A_SOURCE_FILE_RECEIVED_KEY + ||cgBL||' '||'A_SOURCE_FILE_CONFIG_KEY = '||vRow.A_SOURCE_FILE_CONFIG_KEY + ||cgBL||' '||'SOURCE_FILE_NAME = '||vRow.SOURCE_FILE_NAME + ||cgBL||' '||'CHECKSUM = '||vRow.CHECKSUM + ||cgBL||' '||'CREATED = '||vRow.CREATED + ||cgBL||' '||'BYTES = '||vRow.BYTES + ||cgBL||' '||'RECEPTION_DATE = '||vRow.RECEPTION_DATE + ||cgBL||' '||'PROCESSING_STATUS = '||vRow.PROCESSING_STATUS + ||cgBL||' '||'EXTERNAL_TABLE_NAME = '||vRow.EXTERNAL_TABLE_NAME + ||cgBL||' '||'-------------------------' + ||cgBL||' '||'There cannot be two files with the same values for (CHECKSUM, CREATED, BYTES)' + ; + + +-- vChecksum, vCreated, vBytes + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED, vgMsgTmp); + + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END REGISTER_SOURCE_FILE_RECEIVED; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey IN PLS_INTEGER, pStatus IN VARCHAR2) + -- + -- Change status of file in the A_SOURCE_FILE_RECEIVED table + -- + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey),'NULL') + ,'pStatus => '''||nvl(pStatus, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + UPDATE CT_MRDS.A_SOURCE_FILE_RECEIVED + SET PROCESSING_STATUS=pStatus + WHERE A_SOURCE_FILE_RECEIVED_KEY=pSourceFileReceivedKey; + COMMIT; + ENV_MANAGER.LOG_PROCESS_EVENT('File status changed to '||pStatus,'INFO', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + END SET_SOURCE_FILE_RECEIVED_STATUS; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_EXTERNAL_TABLE_COLUMNS(pTargetTableTemplate IN VARCHAR2) + RETURN CLOB + -- + -- Create list of columns for DBMS_CLOUD.CREATE_EXTERNAL_TABLE from existing template table + -- + IS + vColumnList CLOB; + vTableName VARCHAR2(200); + vSchemaName VARCHAR2(200); + BEGIN + vSchemaName := REPLACE(REGEXP_SUBSTR(pTargetTableTemplate,'.*\.'),'.',''); + vTableName := REPLACE(REGEXP_SUBSTR(pTargetTableTemplate,'\..*'),'.',''); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'SQLTERMINATOR', True); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'COLLATION_CLAUSE', 'NEVER'); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'REF_CONSTRAINTS', False); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'STORAGE', False); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'TABLESPACE', False); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'SEGMENT_ATTRIBUTES', False); + vColumnList := RTRIM( + LTRIM( + REGEXP_SUBSTR(DBMS_METADATA.GET_DDL('TABLE', vTableName, vSchemaName),'\(.*\)',1,1,'mn'), + '('), + ')'); + RETURN vColumnList; + END GET_EXTERNAL_TABLE_COLUMNS; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL -- MARS-1049: NEW PARAMETER FOR FILE ENCODING + ) + -- + -- Create external table for a single source file to validate the file structure + -- + IS + vTableName VARCHAR2(200); + vColumnList CLOB; + vFieldList CLOB; + vFormat VARCHAR2(200); + + vPrefix VARCHAR2(200); + vFileName VARCHAR2(1000); + vFileExtension VARCHAR2(200); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pTableName => '''||nvl(pTableName, 'NULL')||'''' + ,'pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pPrefix => '''||nvl(pPrefix, 'NULL')||'''' + ,'pBucketUri => '''||nvl(pBucketUri, 'NULL')||'''' + ,'pFileName => '''||nvl(pFileName, 'NULL')||'''' + ,'pDelimiter => '''||nvl(pDelimiter, 'NULL')||'''' + ,'pEncoding => '''||nvl(pEncoding, 'NULL')||'''' -- MARS-1049: NOWY + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + -- Strip off leading and trailing slashes from prefix + vPrefix := TRIM(BOTH '/' FROM pPrefix); + + -- Generate column and field list from template table + GENERATE_EXTERNAL_TABLE_PARAMS (pTemplateTableName, vColumnList, vFieldList); + + --vFormat evaluation based on pBucketUri first, then pPrefix + -- Archive bucket should use parquet + implicit partitioning regardless of prefix + IF INSTR(pBucketUri, ENV_MANAGER.gvArchiveBucketName)>0 THEN + vFormat := '{"type": "parquet" + ,"implicit_partition_type": "hive" + ,"implicit_partition_columns":["PARTITION_YEAR","PARTITION_MONTH"]}'; + vColumnList := vColumnList||cgBL||' , "PARTITION_YEAR" varchar2(4)'||cgBL||', "PARTITION_MONTH" varchar2(2)'; + vFieldList := NULL; + vFileExtension := '.parquet'; + + -- For INBOX, ODS, and other ARCHIVE prefixes (not in archive bucket) use CSV + ELSIF SUBSTR(pPrefix,1,5) = 'INBOX' OR SUBSTR(pPrefix,1,3) = 'ODS' + OR SUBSTR(pPrefix,1,7) = 'ARCHIVE' + THEN + -- MARS-1049: Create format with encoding if specified + IF pDelimiter = '|' THEN + IF pEncoding IS NOT NULL AND LENGTH(TRIM(pEncoding)) > 0 THEN + vFormat := json_object( + 'delimiter' VALUE '|', + 'skipheaders' VALUE '1', + 'characterset' VALUE pEncoding + ); + ELSE + vFormat := json_object('delimiter' VALUE '|', 'skipheaders' VALUE '1'); + END IF; + ELSE + IF pEncoding IS NOT NULL AND LENGTH(TRIM(pEncoding)) > 0 THEN + vFormat := json_object( + 'type' VALUE 'CSV', + 'skipheaders' VALUE '1', + 'ignoremissingcolumns' VALUE 'true', + 'characterset' VALUE pEncoding + ); + ELSE + vFormat := json_object('type' VALUE 'CSV', 'skipheaders' VALUE '1', 'ignoremissingcolumns' value 'true'); + END IF; + END IF; + + vFileExtension := '.csv'; + + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN_PREFIX, ENV_MANAGER.MSG_UNKNOWN_PREFIX); + END IF; + + -- No filename give: Match all csv files + IF pFileName IS NOT NULL THEN + vFileName := pFileName; + ELSE + vFileName := pBucketUri||vPrefix||'/*'||vFileExtension; + END IF; + + ENV_MANAGER.LOG_PROCESS_EVENT('pTableName', 'DEBUG', pTableName); + ENV_MANAGER.LOG_PROCESS_EVENT('ENV_MANAGER.vpCredentialName', 'DEBUG', ENV_MANAGER.gvCredentialName); + ENV_MANAGER.LOG_PROCESS_EVENT('vFileName', 'DEBUG', vFileName); + ENV_MANAGER.LOG_PROCESS_EVENT('vColumnList', 'DEBUG', vColumnList); + ENV_MANAGER.LOG_PROCESS_EVENT('vFieldList', 'DEBUG', vFieldList); + ENV_MANAGER.LOG_PROCESS_EVENT('vFormat', 'DEBUG', vFormat); + + -- Pre-validation: Check CSV column count for CSV files only + IF SUBSTR(pPrefix,1,5) = 'INBOX' AND pFileName IS NOT NULL THEN + DECLARE + vCsvFirstLine VARCHAR2(4000); + vCsvColCount NUMBER := 0; + vTemplateColCount NUMBER := 0; + vExcessColumns VARCHAR2(2000); + + -- Get template column count + CURSOR c_template_count IS + SELECT COUNT(*) as col_count + FROM ALL_TAB_COLUMNS + WHERE OWNER = UPPER(REPLACE(REGEXP_SUBSTR(pTemplateTableName,'.*\.'),'.','')) + AND TABLE_NAME = UPPER(REGEXP_REPLACE(pTemplateTableName,'^.*\.','')); + + BEGIN + -- Get template column count + FOR rec IN c_template_count LOOP + vTemplateColCount := rec.col_count; + END LOOP; + + -- Read first line of CSV to count columns + BEGIN + SELECT UTL_RAW.CAST_TO_VARCHAR2( + DBMS_LOB.SUBSTR( + DBMS_CLOUD.GET_OBJECT( + credential_name => ENV_MANAGER.gvCredentialName, + object_uri => pFileName + ), + 4000, 1 + ) + ) INTO vCsvFirstLine FROM DUAL; + + -- Count commas in header line + 1 for total columns + vCsvColCount := REGEXP_COUNT(REGEXP_SUBSTR(vCsvFirstLine, '[^'||chr(10)||']*'), ',') + 1; + + ENV_MANAGER.LOG_PROCESS_EVENT('CSV Column Count: ' || vCsvColCount || ', Template Column Count: ' || vTemplateColCount, 'INFO', vParameters); + + -- Check for excess columns + IF vCsvColCount > vTemplateColCount THEN + vgMsgTmp := ENV_MANAGER.MSG_EXCESS_COLUMNS_DETECTED + ||cgBL||'EXCESS COLUMNS DETECTED!' + ||cgBL||'CSV file has ' || vCsvColCount || ' columns but template expects only ' || vTemplateColCount + ||cgBL||'Excess columns: ' || (vCsvColCount - vTemplateColCount) + ||cgBL||'CSV header: ' || SUBSTR(REGEXP_SUBSTR(vCsvFirstLine, '[^'||chr(10)||']*'), 1, 200) + ||cgBL||'POSSIBLE SOLUTIONS:' + ||cgBL||' 1. Remove excess columns from CSV file before processing' + ||cgBL||' 2. Add excess columns to template table: ' || pTemplateTableName; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EXCESS_COLUMNS_DETECTED, vgMsgTmp); + END IF; + + EXCEPTION + WHEN ENV_MANAGER.ERR_EXCESS_COLUMNS_DETECTED THEN + RAISE; -- Re-raise the excess columns error + WHEN ENV_MANAGER.ERR_FILE_VALIDATION_FAILED THEN + RAISE; -- Re-raise the validation error + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Warning: Could not perform pre-validation column count check: ' || SQLERRM, 'WARN', vParameters); + -- Continue with normal processing if pre-validation fails + END; + END; + END IF; + + DBMS_CLOUD.CREATE_EXTERNAL_TABLE( + TABLE_NAME => pTableName, + CREDENTIAL_NAME => ENV_MANAGER.gvCredentialName, + FILE_URI_LIST => vFileName, + COLUMN_LIST => vColumnList, + FIELD_LIST => vFieldList, + FORMAT => vFormat + ); + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + EXCEPTION + WHEN ENV_MANAGER.ERR_EXCESS_COLUMNS_DETECTED THEN + RAISE; -- Re-raise the excess columns error with specific code -20011 + WHEN ENV_MANAGER.ERR_UNKNOWN_PREFIX THEN + vgMsgTmp := ENV_MANAGER.MSG_UNKNOWN_PREFIX || ': ' || pPrefix; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN_PREFIX, vgMsgTmp); + WHEN ENV_MANAGER.ERR_MISSING_COLUMN_DATE_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END CREATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE CREATE_EXTERNAL_TABLE(pSourceFileReceivedKey IN NUMBER) + -- + -- Create external table for a single source file to validate the file structure + -- + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vSourceFileReceived tSourceFileReceived; + vTableName VARCHAR2(200); + vFileName VARCHAR2(1000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + vSourceFileConfig := GET_SOURCE_FILE_CONFIG(pSourceFileReceivedKey => pSourceFileReceivedKey); + vSourceFileReceived := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + vTableName := vSourceFileConfig.TEMPLATE_TABLE_NAME; + + vFileName := ENV_MANAGER.gvInboxBucketUri ||vSourceFileReceived.SOURCE_FILE_PREFIX_INBOX||vSourceFileReceived.SOURCE_FILE_NAME; + + CREATE_EXTERNAL_TABLE( + pTableName => vSourceFileReceived.EXTERNAL_TABLE_NAME, + pTemplateTableName => vSourceFileConfig.TEMPLATE_TABLE_NAME, + pPrefix => vSourceFileReceived.SOURCE_FILE_PREFIX_INBOX, + pBucketUri => ENV_MANAGER.gvInboxBucketUri, + pFileName => vFileName, + pDelimiter => ',', + pEncoding => vSourceFileConfig.ENCODING -- MARS-1049: NOWY PARAMETR + ); + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + END CREATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE VALIDATE_SOURCE_FILE_RECEIVED(pSourceFileReceivedKey IN NUMBER) + -- + -- Check the structure of the received file using DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE + -- + IS + vSourceFileReceived tSourceFileReceived; + vOperationId NUMBER := -1; + vBadfileTable USER_LOAD_OPERATIONS.BADFILE_TABLE%TYPE; + vStatus USER_LOAD_OPERATIONS.STATUS%TYPE; + vErrors NUMBER := 0; + vNumRows NUMBER := 0; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + vSourceFileReceived := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + ENV_MANAGER.LOG_PROCESS_EVENT('vSourceFileReceived.EXTERNAL_TABLE_NAME: '||vSourceFileReceived.EXTERNAL_TABLE_NAME,'DEBUG', vParameters); + BEGIN + DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE(vSourceFileReceived.EXTERNAL_TABLE_NAME, vOperationId); + EXCEPTION + WHEN OTHERS THEN + -- Log complete error details including full stack trace and backtrace + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_FILE_VALIDATION_FAILED, vParameters, 'FILE_MANAGER'); + + -- Call detailed validation error analysis and log the results + DECLARE + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vValidationLogTable VARCHAR2(200); + vTemplateSchema VARCHAR2(200); + vTemplateTable VARCHAR2(200); + vCsvFileUri VARCHAR2(2000); + vAnalysisResult VARCHAR2(32000); + vFailedOperationId NUMBER; + BEGIN + -- Get source file configuration + vSourceFileConfig := GET_SOURCE_FILE_CONFIG(pSourceFileReceivedKey => pSourceFileReceivedKey); + + -- Extract template schema and table from template table name + vTemplateSchema := REPLACE(REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME,'.*\.'),'.',''); + vTemplateTable := REPLACE(REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME,'\..*'),'.',''); + + -- Construct CSV file URI + vCsvFileUri := ENV_MANAGER.gvInboxBucketUri || vSourceFileReceived.SOURCE_FILE_PREFIX_INBOX || vSourceFileReceived.SOURCE_FILE_NAME; + + -- Find the failed validation operation ID + SELECT MAX(ID) INTO vFailedOperationId + FROM USER_LOAD_OPERATIONS + WHERE TABLE_NAME = vSourceFileReceived.EXTERNAL_TABLE_NAME + AND TYPE = 'VALIDATE' + AND STATUS != 'COMPLETED'; + + -- Get validation log table name + IF vFailedOperationId IS NOT NULL THEN + SELECT LOGFILE_TABLE INTO vValidationLogTable + FROM USER_LOAD_OPERATIONS + WHERE ID = vFailedOperationId; + + -- Call detailed error analysis + vAnalysisResult := ENV_MANAGER.ANALYZE_VALIDATION_ERRORS( + pValidationLogTable => vValidationLogTable, + pTemplateSchema => vTemplateSchema, + pTemplateTable => vTemplateTable, + pCsvFileUri => vCsvFileUri + ); + + -- Log detailed analysis results + ENV_MANAGER.LOG_PROCESS_EVENT('DETAILED VALIDATION ERROR ANALYSIS:', 'ERROR', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT(vAnalysisResult, 'ERROR', vParameters); + END IF; + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Error during validation analysis: ' || SQLERRM, 'ERROR', vParameters); + END; + + MOVE_FILE(pSourceFileReceivedKey => pSourceFileReceivedKey, pDestination => 'QUARANTINE'); + -- Ensure the status change is committed before raising exception + COMMIT; + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, ENV_MANAGER.MSG_FILE_VALIDATION_FAILED); + END; + ENV_MANAGER.LOG_PROCESS_EVENT('vOperationId of validation: '||vOperationId,'DEBUG', vParameters); + IF vOperationId = -1 + THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + END IF; + + SELECT BADFILE_TABLE, ROWS_LOADED, STATUS + INTO vBadfileTable, vNumRows, vStatus + FROM USER_LOAD_OPERATIONS + WHERE ID = vOperationId; + + -- MARS-1409: Extract and validate A_WORKFLOW_HISTORY_KEY from external table + DECLARE + vWorkflowHistoryKey NUMBER; + vWorkflowKeyCount NUMBER; + vWorkflowKeyDistinct NUMBER; + vDynamicSQL VARCHAR2(1000); + BEGIN + -- Build dynamic SQL to count distinct A_WORKFLOW_HISTORY_KEY values + vDynamicSQL := 'SELECT COUNT(*), COUNT(DISTINCT A_WORKFLOW_HISTORY_KEY) FROM ' || vSourceFileReceived.EXTERNAL_TABLE_NAME; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Extracting A_WORKFLOW_HISTORY_KEY from external table', 'DEBUG', vParameters); + + -- Count total rows and distinct workflow keys + EXECUTE IMMEDIATE vDynamicSQL INTO vWorkflowKeyCount, vWorkflowKeyDistinct; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Total rows: ' || vWorkflowKeyCount || ', Distinct A_WORKFLOW_HISTORY_KEY values: ' || vWorkflowKeyDistinct, 'DEBUG', vParameters); + + -- Validate workflow key presence and uniqueness + IF vWorkflowKeyDistinct = 0 OR vWorkflowKeyDistinct IS NULL THEN + -- No A_WORKFLOW_HISTORY_KEY found or all values are NULL + vgMsgTmp := ENV_MANAGER.MSG_WORKFLOW_KEY_NULL || ' [File: ' || vSourceFileReceived.SOURCE_FILE_NAME || ']'; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WORKFLOW_KEY_NULL, vgMsgTmp); + + ELSIF vWorkflowKeyDistinct > 1 THEN + -- Multiple different A_WORKFLOW_HISTORY_KEY values found + vgMsgTmp := ENV_MANAGER.MSG_MULTIPLE_WORKFLOW_KEYS || ' [Found: ' || vWorkflowKeyDistinct || ' distinct values in file: ' || vSourceFileReceived.SOURCE_FILE_NAME || ']'; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_WORKFLOW_KEYS, vgMsgTmp); + + ELSE + -- Exactly one A_WORKFLOW_HISTORY_KEY value - fetch and save it + vDynamicSQL := 'SELECT DISTINCT A_WORKFLOW_HISTORY_KEY FROM ' || vSourceFileReceived.EXTERNAL_TABLE_NAME; + EXECUTE IMMEDIATE vDynamicSQL INTO vWorkflowHistoryKey; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Extracted A_WORKFLOW_HISTORY_KEY: ' || vWorkflowHistoryKey, 'DEBUG', vParameters); + + -- Update A_SOURCE_FILE_RECEIVED with workflow history key + UPDATE CT_MRDS.A_SOURCE_FILE_RECEIVED + SET A_WORKFLOW_HISTORY_KEY = vWorkflowHistoryKey + WHERE A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Updated A_SOURCE_FILE_RECEIVED with A_WORKFLOW_HISTORY_KEY: ' || vWorkflowHistoryKey, 'INFO', vParameters); + END IF; + EXCEPTION + WHEN ENV_MANAGER.ERR_WORKFLOW_KEY_NULL THEN + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE; + WHEN ENV_MANAGER.ERR_MULTIPLE_WORKFLOW_KEYS THEN + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE; + WHEN OTHERS THEN + vgMsgTmp := 'MARS-1409: Error extracting A_WORKFLOW_HISTORY_KEY: ' || SQLERRM; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, vgMsgTmp); + END; + +-- DBMS_OUTPUT.PUT_LINE(vStatus); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATED'); + ENV_MANAGER.LOG_PROCESS_EVENT('File status changed to VALIDATED','DEBUG', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + WHEN ENV_MANAGER.ERR_DIDNT_GET_LOAD_OPERATION_ID THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + + WHEN ENV_MANAGER.ERR_FILE_VALIDATION_FAILED THEN + vgMsgTmp := ENV_MANAGER.MSG_FILE_VALIDATION_FAILED; + ENV_MANAGER.LOG_PROCESS_ERROR(vgMsgTmp, vParameters, 'FILE_MANAGER'); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, vgMsgTmp); + + WHEN ENV_MANAGER.ERR_WORKFLOW_KEY_NULL THEN + vgMsgTmp := ENV_MANAGER.MSG_WORKFLOW_KEY_NULL; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WORKFLOW_KEY_NULL, vgMsgTmp); + + WHEN ENV_MANAGER.ERR_MULTIPLE_WORKFLOW_KEYS THEN + vgMsgTmp := ENV_MANAGER.MSG_MULTIPLE_WORKFLOW_KEYS; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_WORKFLOW_KEYS, vgMsgTmp); + + WHEN OTHERS THEN + IF SQLCODE = -20404 THEN + vgMsgTmp := ENV_MANAGER.MSG_FILE_NOT_FOUND_ON_CLOUD||cgBL||SQLERRM; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_FOUND_ON_CLOUD, vgMsgTmp); + + ELSIF SQLCODE = -20003 THEN + execute immediate 'select LISTAGG(record, '''||cgBL||''') from (select * from '||REGEXP_SUBSTR(SQLERRM, '"([^"]+)"."([^"]+)"')||' order by rownum desc) where rownum <=2' + into vgMsgTmp; + vgMsgTmp := ENV_MANAGER.MSG_FILE_VALIDATION_FAILED||cgBL||SQLERRM||cgBL||vgMsgTmp; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, vgMsgTmp); +-- ELSIF SQLCODE = -20000 THEN + -- TO_DO Add additional info about current config + -- ENV_MANAGER.MSG_FILE_VALIDATION_FAILED := ENV_MANAGER.MSG_FILE_VALIDATION_FAILED||cgBL||SQLERRM||cgBL||FILE_MANAGER.OUTPUT_SOURCE_FILE_CONFIG_INFO( ..config key value.. ); +-- dbms_output.put_line(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); +-- ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_WRONG_DATE_TIMESTAMP_FORMAT, 'ERROR', vParameters); +-- ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); +-- RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DATE_TIMESTAMP_FORMAT, ENV_MANAGER.MSG_WRONG_DATE_TIMESTAMP_FORMAT); + + ELSE + -- Log complete error details including full stack trace and backtrace + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_UNKNOWN, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END IF; + END VALIDATE_SOURCE_FILE_RECEIVED; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2 + -- + -- wrapper for DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE + -- + IS + vOperationId NUMBER := -1; + vBadfileTable USER_LOAD_OPERATIONS.BADFILE_TABLE%TYPE; + vLogfileTable USER_LOAD_OPERATIONS.LOGFILE_TABLE%TYPE; + vStatus USER_LOAD_OPERATIONS.STATUS%TYPE; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vDetails clob; + TYPE TCURSOR is REF CURSOR; + vCursor TCURSOR; + vQuery VARCHAR2(1000); + vRecord VARCHAR2(10000); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pTableName => '''||nvl(pTableName, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE(pTableName, vOperationId); + ENV_MANAGER.LOG_PROCESS_EVENT('vOperationId of validation: '||vOperationId,'DEBUG', vParameters); + IF vOperationId = -1 + THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + END IF; + + SELECT decode(STATUS, 'COMPLETED', 'PASSED', STATUS), LOGFILE_TABLE, BADFILE_TABLE + INTO vStatus, vLogfileTable, vBadfileTable + FROM USER_LOAD_OPERATIONS + WHERE ID = vOperationId; + + RETURN vStatus; + EXCEPTION + WHEN ENV_MANAGER.ERR_DIDNT_GET_LOAD_OPERATION_ID THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + + WHEN OTHERS THEN + SELECT decode(STATUS, 'COMPLETED', 'PASSED', STATUS), LOGFILE_TABLE, BADFILE_TABLE + INTO vStatus, vLogfileTable, vBadfileTable + FROM USER_LOAD_OPERATIONS + WHERE ID = vOperationId; + vQuery := 'select record from ( + select + nvl(l.record,''----------------------------------------------------'') as record + ,rownum as lp + ,max(case when nvl(instr(l.record, ''error'' ),0) > 0 then rownum else 0 end) over (partition by 1) as ENV_MANAGER.ERR_row + from '||vLogfileTable||' l + ) + where lp >= ENV_MANAGER.ERR_row + order by rownum'; + + vDetails := vStatus||cgBL||'----------------------------------------------------'||cgBL; + + OPEN vCursor for vQuery; + loop + fetch vCursor into vRecord; + EXIT WHEN vCursor%NOTFOUND; + vDetails := vDetails ||vRecord ||cgBL; +-- for i in loop +-- vDetails := vDetails ||i.record ||cgBL; + end loop; + CLOSE vCursor; + vDetails := vDetails||'More details can be found in below tables:'||cgBL|| + ' SELECT * FROM USER_LOAD_OPERATIONS WHERE ID = '||vOperationId||';'||cgBL|| + ' SELECT * FROM '||vLogfileTable||';'||cgBL|| + ' SELECT * FROM '||vBadfileTable||';' + ; + + RETURN vDetails; + + END VALIDATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION S_VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2 + -- + -- Simple + -- + IS + vCount PLS_INTEGER; + BEGIN + execute immediate 'select count(1) from '||pTableName into vCount; + IF vCount >= 0 + THEN + RETURN 'PASSED'; + END IF; + + RETURN 'FAILED'; + EXCEPTION + WHEN OTHERS THEN + RETURN 'FAILED'; + END S_VALIDATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE DROP_EXTERNAL_TABLE(pSourceFileReceivedKey IN NUMBER) + -- + -- Drop external table created to validate the file structure + -- + IS + vSourceFileReceived tSourceFileReceived; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + vSourceFileReceived := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + EXECUTE IMMEDIATE 'DROP TABLE '||vSourceFileReceived.EXTERNAL_TABLE_NAME; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END DROP_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE COPY_FILE(pSourceFileReceivedKey IN NUMBER, pDestination IN VARCHAR2) + -- + -- Possible pDestination values are: 'ODS' or 'ARCHIVE' + -- + IS + vSourceFileReceivedInfo tSourceFileReceived; + vSourceObject VARCHAR2(2000); + vTargetObject VARCHAR2(2000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; +-- vStatus VARCHAR2(20); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => ' ||nvl(to_char(pSourceFileReceivedKey), 'NULL'), + 'pDestination => '''||nvl(pDestination, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + vSourceFileReceivedInfo := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + + IF pDestination = 'ODS' THEN + vSourceObject := ENV_MANAGER.gvInboxBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_INBOX ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vTargetObject := ENV_MANAGER.gvDataBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_ODS ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + END IF; + + DBMS_CLOUD.COPY_OBJECT(source_credential_name => ENV_MANAGER.gvCredentialName, + source_object_uri => vSourceObject, + target_object_uri => vTargetObject, + target_credential_name => ENV_MANAGER.gvCredentialName + ); + ENV_MANAGER.LOG_PROCESS_EVENT('File copied to '||pDestination||' target location','DEBUG', vTargetObject); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END COPY_FILE; + + ---------------------------------------------------------------------------------------------------- + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE MOVE_FILE(pSourceFileReceivedKey IN NUMBER, pDestination IN VARCHAR2) + -- + -- Possible pDestination values are: 'ODS' or 'ARCHIVE' + -- + IS + vSourceFileReceivedInfo tSourceFileReceived; + vSourceObject VARCHAR2(2000); + vTargetObject VARCHAR2(2000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vStatus VARCHAR2(20); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => ' ||nvl(to_char(pSourceFileReceivedKey), 'NULL'), + 'pDestination => '''||nvl(pDestination, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + + vSourceFileReceivedInfo := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + + IF pDestination = 'ODS' THEN + vSourceObject := ENV_MANAGER.gvInboxBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_INBOX ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vTargetObject := ENV_MANAGER.gvDataBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_ODS ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vStatus := 'READY_FOR_INGESTION'; + ELSIF pDestination = 'QUARANTINE' THEN + vSourceObject := ENV_MANAGER.gvInboxBucketUri ||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_INBOX ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vTargetObject := ENV_MANAGER.gvInboxBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_QUARANTINE||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vStatus := 'VALIDATION_FAILED'; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + END IF; + + DBMS_CLOUD.MOVE_OBJECT(source_credential_name => ENV_MANAGER.gvCredentialName, + source_object_uri => vSourceObject, + target_object_uri => vTargetObject, + target_credential_name => ENV_MANAGER.gvCredentialName + ); + ENV_MANAGER.LOG_PROCESS_EVENT('File moved to '||pDestination||' target location','DEBUG', vTargetObject); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => vStatus); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + WHEN ENV_MANAGER.ERR_WRONG_DESTINATION_PARAM THEN + ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM := ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM + ||cgBL||' '||'Possible parameters are: ''ODS'' or ''ARCHIVE''' + ||cgBL||' '||'Provided destination parameter: '''||pDestination||''''; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + WHEN ENV_MANAGER.ERR_NO_CONFIG_FOR_RECEIVED_FILE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END MOVE_FILE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE DELETE_FOLDER_CONTENTS(pBucketArea IN VARCHAR2, pFolderPrefix IN VARCHAR2) + -- + -- Delete all files from specified folder in cloud storage + -- pBucketArea: 'INBOX', 'DATA', 'ARCHIVE' + -- pFolderPrefix: folder path within bucket (e.g., 'C2D/UC_DISSEM/UC_NMA_DISSEM/') + -- + IS + vBucketUri VARCHAR2(2000); + vFolderUri VARCHAR2(2000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vFilesDeleted PLS_INTEGER := 0; + vObjectName VARCHAR2(4000); + vFullObjectUri VARCHAR2(4000); + + -- Cursor to list all objects in the folder + CURSOR c_objects IS + SELECT object_name + FROM TABLE(DBMS_CLOUD.LIST_OBJECTS( + credential_name => ENV_MANAGER.gvCredentialName, + location_uri => vBucketUri + )) + WHERE object_name IS NOT NULL + AND object_name LIKE pFolderPrefix || '%'; + + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( + 'pBucketArea => '''||nvl(pBucketArea, 'NULL')||'''', + 'pFolderPrefix => '''||nvl(pFolderPrefix, 'NULL')||'''' + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + -- Get bucket URI based on bucket area + vBucketUri := GET_BUCKET_URI(pBucketArea); + + ENV_MANAGER.LOG_PROCESS_EVENT('Listing objects in bucket with prefix: ' || pFolderPrefix, 'DEBUG', vBucketUri); + + -- List and delete all objects in the folder + FOR obj_rec IN c_objects LOOP + vObjectName := obj_rec.object_name; + vFullObjectUri := vBucketUri || vObjectName; + + BEGIN + ENV_MANAGER.LOG_PROCESS_EVENT('Deleting object', 'DEBUG', vFullObjectUri); + + DBMS_CLOUD.DELETE_OBJECT( + credential_name => ENV_MANAGER.gvCredentialName, + object_uri => vFullObjectUri + ); + + vFilesDeleted := vFilesDeleted + 1; + ENV_MANAGER.LOG_PROCESS_EVENT('Object deleted successfully', 'DEBUG', vObjectName); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Error deleting object: ' || vObjectName || ' - ' || SQLERRM, 'ERROR', vParameters); + -- Continue with next file instead of stopping the whole process + END; + END LOOP; + + ENV_MANAGER.LOG_PROCESS_EVENT('Total files deleted: ' || vFilesDeleted, 'INFO', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Error in DELETE_FOLDER_CONTENTS: ' || SQLERRM, 'ERROR', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END DELETE_FOLDER_CONTENTS; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + -- + -- Ubmrella procedure that calls + -- - REGISTER_SOURCE_FILE_RECEIVED + -- - CREATE_EXTERNAL_TABLE + -- - VALIDATE_SOURCE_FILE_RECEIVED + -- - DROP_EXTERNAL_TABLE + -- - MOVE_FILE + IS + vSourceFileId NUMBER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '||nvl(pSourceFileReceivedName, 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + ---- + vSourceFileId := REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName); + CREATE_EXTERNAL_TABLE(vSourceFileId); + VALIDATE_SOURCE_FILE_RECEIVED(vSourceFileId); + DROP_EXTERNAL_TABLE(vSourceFileId); +-- COPY_FILE(vSourceFileId, 'ODS'); + MOVE_FILE(vSourceFileId, 'ODS'); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => vSourceFileId, pStatus => 'READY_FOR_INGESTION'); + + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + -- -20001 + WHEN ENV_MANAGER.ERR_EMPTY_FILEURI_AND_RECKEY THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + -- -20002 + WHEN ENV_MANAGER.ERR_NO_CONFIG_MATCH_FOR_FILEURI THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH_FOR_FILEURI, ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI); + -- -20003 + WHEN ENV_MANAGER.ERR_MULTIPLE_MATCH_FOR_SRCFILE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE, ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE); + -- -20004 + WHEN ENV_MANAGER.ERR_MISSING_COLUMN_DATE_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT); + -- -20005 + WHEN ENV_MANAGER.ERR_MULTIPLE_COLUMN_DATE_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTIPLE_COLUMN_DATE_FORMAT), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MULTIPLE_COLUMN_DATE_FORMAT); + -- -20006 + WHEN ENV_MANAGER.ERR_DIDNT_GET_LOAD_OPERATION_ID THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + -- -20007 + WHEN ENV_MANAGER.ERR_NO_CONFIG_FOR_RECEIVED_FILE THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE); + -- -20008 + WHEN ENV_MANAGER.ERR_MULTI_CONFIG_FOR_RECEIVED_FILE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTI_CONFIG_FOR_RECEIVED_FILE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTI_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_MULTI_CONFIG_FOR_RECEIVED_FILE); + -- -20009 + WHEN ENV_MANAGER.ERR_FILE_NOT_FOUND_ON_CLOUD THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_FILE_NOT_FOUND_ON_CLOUD), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_FOUND_ON_CLOUD, ENV_MANAGER.MSG_FILE_NOT_FOUND_ON_CLOUD); + -- -20010 + WHEN ENV_MANAGER.ERR_FILE_VALIDATION_FAILED THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_FILE_VALIDATION_FAILED), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, ENV_MANAGER.MSG_FILE_VALIDATION_FAILED); + -- -20011 + WHEN ENV_MANAGER.ERR_NO_CONFIG_MATCH THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_NO_CONFIG_MATCH), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH, ENV_MANAGER.MSG_NO_CONFIG_MATCH); + -- -20012 + WHEN ENV_MANAGER.ERR_UNKNOWN_PREFIX THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_UNKNOWN_PREFIX), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN_PREFIX, ENV_MANAGER.MSG_UNKNOWN_PREFIX); + -- -20013 + WHEN ENV_MANAGER.ERR_TABLE_NOT_EXISTS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_TABLE_NOT_EXISTS), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_TABLE_NOT_EXISTS, ENV_MANAGER.MSG_TABLE_NOT_EXISTS); + -- -20014 + WHEN ENV_MANAGER.ERR_COLUMN_NOT_EXISTS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_COLUMN_NOT_EXISTS), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_COLUMN_NOT_EXISTS, ENV_MANAGER.MSG_COLUMN_NOT_EXISTS); + -- -20015 + WHEN ENV_MANAGER.ERR_UNSUPPORTED_DATA_TYPE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_UNSUPPORTED_DATA_TYPE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNSUPPORTED_DATA_TYPE, ENV_MANAGER.MSG_UNSUPPORTED_DATA_TYPE); + -- -20016 + WHEN ENV_MANAGER.ERR_MISSING_SOURCE_KEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MISSING_SOURCE_KEY), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_SOURCE_KEY, ENV_MANAGER.MSG_MISSING_SOURCE_KEY); + -- -20017 + WHEN ENV_MANAGER.ERR_NULL_SOURCE_FILE_CONFIG_KEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_NULL_SOURCE_FILE_CONFIG_KEY), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NULL_SOURCE_FILE_CONFIG_KEY, ENV_MANAGER.MSG_NULL_SOURCE_FILE_CONFIG_KEY); + -- -20018 + WHEN ENV_MANAGER.ERR_DUPLICATED_SOURCE_KEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_DUPLICATED_SOURCE_KEY), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DUPLICATED_SOURCE_KEY, ENV_MANAGER.MSG_DUPLICATED_SOURCE_KEY); + -- -20019 + WHEN ENV_MANAGER.ERR_MISSING_CONTAINER_CONFIG THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MISSING_CONTAINER_CONFIG), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_CONTAINER_CONFIG, ENV_MANAGER.MSG_MISSING_CONTAINER_CONFIG); + -- -20020 + WHEN ENV_MANAGER.ERR_MULTIPLE_CONTAINER_ENTRIES THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTIPLE_CONTAINER_ENTRIES), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_CONTAINER_ENTRIES, ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES); + -- -20021 + WHEN ENV_MANAGER.ERR_WRONG_DESTINATION_PARAM THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + -- -20022 + WHEN ENV_MANAGER.ERR_FILE_NOT_EXISTS_ON_CLOUD THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_EXISTS_ON_CLOUD, ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD); + -- -20023 + WHEN ENV_MANAGER.ERR_FILE_ALREADY_REGISTERED THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED, ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED); + -- -20024 + WHEN ENV_MANAGER.ERR_WRONG_DATE_TIMESTAMP_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_WRONG_DATE_TIMESTAMP_FORMAT), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DATE_TIMESTAMP_FORMAT, ENV_MANAGER.MSG_WRONG_DATE_TIMESTAMP_FORMAT); + + -- -20011 + WHEN ENV_MANAGER.ERR_EXCESS_COLUMNS_DETECTED THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_EXCESS_COLUMNS_DETECTED, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EXCESS_COLUMNS_DETECTED, ENV_MANAGER.MSG_EXCESS_COLUMNS_DETECTED); + + -- -20999 + WHEN ENV_MANAGER.ERR_UNKNOWN THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_UNKNOWN), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END PROCESS_SOURCE_FILE; + + +---------------------------------------------------------------------------------------------------- + + FUNCTION PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + RETURN PLS_INTEGER + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '||nvl(pSourceFileReceivedName, 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + ---- + PROCESS_SOURCE_FILE(pSourceFileReceivedName => pSourceFileReceivedName); + ---- + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + RETURN SQLCODE; + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RETURN SQLCODE; + END PROCESS_SOURCE_FILE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DATE_FORMAT( + pTemplateTableName IN VARCHAR2, + pColumnName IN VARCHAR2 + ) + RETURN VARCHAR2 + IS + vDateFormat A_COLUMN_DATE_FORMAT.DATE_FORMAT%TYPE; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vColumnName VARCHAR2(200); + vGetDefault BOOLEAN := FALSE; + BEGIN + vColumnName := trim(pColumnName); + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pColumnName => '''||nvl(vColumnName, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + BEGIN + SELECT DATE_FORMAT + INTO vDateFormat + FROM CT_MRDS.A_COLUMN_DATE_FORMAT F + WHERE F.TEMPLATE_TABLE_NAME = pTemplateTableName + AND F.COLUMN_NAME = vColumnName; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + vGetDefault := TRUE; + WHEN TOO_MANY_ROWS THEN + -- Below error should not happened because: + -- Unique constraint added on table A_COLUMN_DATE_FORMAT on columns: (TEMPLATE_TABLE_NAME, COLUMN_NAME) + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MULTIPLE_COLUMN_DATE_FORMAT); + END; + IF vGetDefault THEN + BEGIN + SELECT DATE_FORMAT + INTO vDateFormat + FROM CT_MRDS.A_COLUMN_DATE_FORMAT F + WHERE F.TEMPLATE_TABLE_NAME = pTemplateTableName + AND F.COLUMN_NAME = 'DEFAULT'; + EXCEPTION + WHEN NO_DATA_FOUND THEN + vDateFormat := ENV_MANAGER.gvDefaultDateFormat; + END; + END IF; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vDateFormat; + END GET_DATE_FORMAT; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE GENERATE_EXTERNAL_TABLE_PARAMS ( + + pTemplateTableName IN VARCHAR2, + pColumnList OUT CLOB, + pFieldList OUT CLOB + ) + IS + vSchemaName VARCHAR2(200); + vTableName VARCHAR2(200); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vMaxColumnNameLength PLS_INTEGER := 0; + vColType varchar2(200); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pColumnList => '''||nvl(pColumnList, 'NULL')||'''' + ,'pFieldList = '''||nvl(pFieldList, 'NULL')||'''' + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + vSchemaName := REPLACE(REGEXP_SUBSTR(pTemplateTableName,'.*\.'),'.',''); + ENV_MANAGER.LOG_PROCESS_EVENT('vSchemaName','DEBUG', vSchemaName); + vTableName := REPLACE(REGEXP_SUBSTR(pTemplateTableName,'\..*'),'.',''); + ENV_MANAGER.LOG_PROCESS_EVENT('vTableName','DEBUG', vTableName); + FOR rec IN ( + SELECT + '"'||column_name||'"' as quoted_column_name, + column_name, + data_type, + data_length, + char_length, -- MARS-1056: Added for CHAR/BYTE semantics + char_used, -- MARS-1056: Added for CHAR/BYTE semantics + data_precision, + data_scale, + column_id, + max(length(column_name)+1) over (partition by table_name) as max_column_name_length + FROM all_tab_columns + WHERE table_name = UPPER(vTableName) + AND owner = NVL(UPPER(vSchemaName), USER) + ORDER BY column_id + ) LOOP + -- Build the column_list string + rec.quoted_column_name := rpad(rec.quoted_column_name, rec.max_column_name_length+2, ' '); + + vColType := + CASE + -- MARS-1056: Fixed VARCHAR2 definition logic to preserve CHAR/BYTE semantics + WHEN rec.data_type = 'VARCHAR2' THEN + CASE + WHEN rec.char_used = 'C' THEN + rec.quoted_column_name || ' VARCHAR2(' || rec.char_length || ' CHAR)' + WHEN rec.char_used = 'B' THEN + rec.quoted_column_name || ' VARCHAR2(' || rec.data_length || ' BYTE)' + ELSE + -- Fallback for NULL char_used (should not occur but handle gracefully) + rec.quoted_column_name || ' VARCHAR2(' || rec.data_length || ')' + END + -- Other character types (preserve original logic) + -- MARS-1468: Fixed CHAR to use char_used/char_length (same as VARCHAR2 fix in MARS-1056) + WHEN rec.data_type = 'CHAR' THEN + CASE + WHEN rec.char_used = 'C' THEN + rec.quoted_column_name || ' CHAR(' || rec.char_length || ' CHAR)' + WHEN rec.char_used = 'B' THEN + rec.quoted_column_name || ' CHAR(' || rec.data_length || ' BYTE)' + ELSE + rec.quoted_column_name || ' CHAR(' || rec.data_length || ')' + END + -- MARS-1468: NCHAR/NVARCHAR2 - use char_length (data_length stores bytes in AL16UTF16, e.g. NCHAR(1) => data_length=2 but char_length=1) + WHEN rec.data_type IN ('NCHAR', 'NVARCHAR2') THEN + rec.quoted_column_name || ' ' || rec.data_type || '(' || rec.char_length || ')' + WHEN rec.data_type = 'NUMBER' THEN + rec.quoted_column_name || ' ' || rec.data_type || + CASE + WHEN rec.data_precision IS NOT NULL AND rec.data_scale IS NOT NULL THEN + '(' || rec.data_precision || ',' || rec.data_scale || ')' + WHEN rec.data_precision IS NOT NULL THEN + '(' || rec.data_precision || ')' + ELSE + '' + END + WHEN rec.data_type IN ('RAW') THEN + rec.quoted_column_name || ' ' || rec.data_type || '(' || rec.data_length || ')' + WHEN REGEXP_SUBSTR(rec.data_type, '^[A-Z]+') IN ('DATE', 'TIMESTAMP') THEN + rec.quoted_column_name || ' ' || rec.data_type + ELSE + rec.quoted_column_name || ' ' || rec.data_type + END; + pColumnList := pColumnList ||vColType ||cgBL|| ','; + -- Build the field_list string + -- Note: field_list uses CHAR() for CSV field definitions - this is correct behavior + pFieldList := pFieldList || + CASE + WHEN rec.data_type = 'DATE' THEN + -- MARS-1046: DATE format - wrap with NORMALIZE_DATE_FORMAT to fix ISO 8601 'T' separator + rec.quoted_column_name || ' DATE ' || CHR(39) || NORMALIZE_DATE_FORMAT(GET_DATE_FORMAT(pTemplateTableName => pTemplateTableName, pColumnName => rec.column_name)) || CHR(39) + WHEN rec.data_type LIKE 'TIMESTAMP%WITH TIME ZONE' THEN + -- MARS-1046: TIMESTAMP WITH TIME ZONE format for ISO 8601 with fractional seconds and timezone + -- Syntax: column_name CHAR(length) DATE_FORMAT TIMESTAMP WITH TIME ZONE MASK "format" + -- Use fixed length of 50 for ISO 8601 format (e.g., "2012-03-02T14:16:23.798+01:00" = 29 chars) + rec.quoted_column_name || ' CHAR(50) DATE_FORMAT TIMESTAMP WITH TIME ZONE MASK ' || CHR(39) || NORMALIZE_DATE_FORMAT(GET_DATE_FORMAT(pTemplateTableName => pTemplateTableName, pColumnName => rec.column_name)) || CHR(39) + WHEN REGEXP_SUBSTR(rec.data_type, '^[A-Z]+') = 'TIMESTAMP' THEN + -- Other TIMESTAMP types (without timezone) + -- SQL*Loader syntax: CHAR(length) DATE_FORMAT TIMESTAMP MASK "format" (not: TIMESTAMP 'format') + rec.quoted_column_name || ' CHAR(35) DATE_FORMAT TIMESTAMP MASK ' || CHR(39) || NORMALIZE_DATE_FORMAT(GET_DATE_FORMAT(pTemplateTableName => pTemplateTableName, pColumnName => rec.column_name)) || CHR(39) + WHEN rec.data_type IN ('VARCHAR2', 'CHAR') THEN + -- MARS-1468: For CHAR use char_length when char semantics (C), otherwise data_length + rec.quoted_column_name || ' CHAR(' || + CASE WHEN rec.char_used = 'C' THEN rec.char_length ELSE rec.data_length END + || ')' + WHEN rec.data_type IN ('NCHAR', 'NVARCHAR2') THEN + -- For CSV field definitions, use data_length for NCHAR/NVARCHAR2 + rec.quoted_column_name || ' CHAR(' || rec.data_length || ')' + ELSE + rec.quoted_column_name + END ||cgBL|| ','; + + + END LOOP; + + -- Remove the trailing comma and space from the strings + pColumnList := ' '||RTRIM(pColumnList, ','); + pFieldList := ' '||RTRIM(pFieldList, ','); + ENV_MANAGER.LOG_PROCESS_EVENT('vColumnList', 'DEBUG', pColumnList); + -- TO_DO !!! + -- Add check if pColumnList/pFieldList is empty or not + -- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + + -- Output the generated column_list and field_list + ENV_MANAGER.LOG_PROCESS_EVENT('column_list' ,'DEBUG',pColumnList); + ENV_MANAGER.LOG_PROCESS_EVENT('field_list' ,'DEBUG',pFieldList); + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + EXCEPTION +-- WHEN ENV_MANAGER.ERR_MISSING_COLUMN_DATE_FORMAT THEN +-- ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT, 'ERROR', vParameters); +-- RAISE_ERROR(ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END GENERATE_EXTERNAL_TABLE_PARAMS; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE ADD_SOURCE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE, + pSourceName IN CT_MRDS.A_SOURCE.SOURCE_NAME%TYPE + ) IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + INSERT INTO CT_MRDS.A_SOURCE(A_SOURCE_KEY, SOURCE_NAME) VALUES (pSourceKey, pSourceName); + COMMIT; + EXCEPTION + WHEN DUP_VAL_ON_INDEX THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_DUPLICATED_SOURCE_KEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DUPLICATED_SOURCE_KEY, ENV_MANAGER.MSG_DUPLICATED_SOURCE_KEY); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END ADD_SOURCE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE DELETE_SOURCE_CASCADE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE + ) IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vSharedTemplateCount PLS_INTEGER := 0; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceKey => '''||nvl(pSourceKey, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + -- First pass: Delete container files (those that have CONTAINER_FILE_KEY set) + for rec in (select A_SOURCE_FILE_CONFIG_KEY, TEMPLATE_TABLE_NAME from CT_MRDS.A_SOURCE_FILE_CONFIG + where A_SOURCE_KEY = pSourceKey AND CONTAINER_FILE_KEY IS NOT NULL + ORDER BY A_SOURCE_FILE_CONFIG_KEY) loop + -- Delete processed file records + delete from CT_MRDS.A_SOURCE_FILE_RECEIVED WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_SOURCE_FILE_RECEIVED records for container config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + + -- Check if TEMPLATE_TABLE_NAME is shared with other source systems before deleting date formats + IF rec.TEMPLATE_TABLE_NAME IS NOT NULL THEN + SELECT COUNT(DISTINCT A_SOURCE_KEY) + INTO vSharedTemplateCount + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME + AND A_SOURCE_KEY <> pSourceKey; -- Exclude current source being deleted + + -- Only delete date formats if template table is not shared with other sources + IF vSharedTemplateCount = 0 THEN + delete from CT_MRDS.A_COLUMN_DATE_FORMAT WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_COLUMN_DATE_FORMAT records for template: '||rec.TEMPLATE_TABLE_NAME,'DEBUG', vParameters); + ELSE + ENV_MANAGER.LOG_PROCESS_EVENT('Skipping A_COLUMN_DATE_FORMAT deletion - template table '||rec.TEMPLATE_TABLE_NAME||' is shared with '||vSharedTemplateCount||' other source systems','WARNING', vParameters); + END IF; + END IF; + + -- Delete container file configuration + delete from CT_MRDS.A_SOURCE_FILE_CONFIG WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted container A_SOURCE_FILE_CONFIG record for config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + end loop; + COMMIT; -- Commit container deletions + + -- Second pass: Delete parent files (those that do NOT have CONTAINER_FILE_KEY set) + for rec in (select A_SOURCE_FILE_CONFIG_KEY, TEMPLATE_TABLE_NAME from CT_MRDS.A_SOURCE_FILE_CONFIG + where A_SOURCE_KEY = pSourceKey AND CONTAINER_FILE_KEY IS NULL + ORDER BY A_SOURCE_FILE_CONFIG_KEY) loop + -- Delete processed file records + delete from CT_MRDS.A_SOURCE_FILE_RECEIVED WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_SOURCE_FILE_RECEIVED records for parent config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + + -- Check if TEMPLATE_TABLE_NAME is shared with other source systems before deleting date formats + IF rec.TEMPLATE_TABLE_NAME IS NOT NULL THEN + SELECT COUNT(DISTINCT A_SOURCE_KEY) + INTO vSharedTemplateCount + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME + AND A_SOURCE_KEY <> pSourceKey; -- Exclude current source being deleted + + -- Only delete date formats if template table is not shared with other sources + IF vSharedTemplateCount = 0 THEN + delete from CT_MRDS.A_COLUMN_DATE_FORMAT WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_COLUMN_DATE_FORMAT records for template: '||rec.TEMPLATE_TABLE_NAME,'DEBUG', vParameters); + ELSE + ENV_MANAGER.LOG_PROCESS_EVENT('Skipping A_COLUMN_DATE_FORMAT deletion - template table '||rec.TEMPLATE_TABLE_NAME||' is shared with '||vSharedTemplateCount||' other source systems','WARNING', vParameters); + END IF; + END IF; + + -- Delete parent file configuration + delete from CT_MRDS.A_SOURCE_FILE_CONFIG WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted parent A_SOURCE_FILE_CONFIG record for config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + end loop; + COMMIT; -- Commit parent deletions + + -- Delete source system record + DELETE FROM CT_MRDS.A_SOURCE where A_SOURCE_KEY = pSourceKey; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_SOURCE record for source key: '||pSourceKey,'DEBUG', vParameters); + COMMIT; -- Final commit for source deletion + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END DELETE_SOURCE_CASCADE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_CONTAINER_SOURCE_FILE_CONFIG_KEY ( + pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ) RETURN PLS_INTEGER + IS + vSourceFileConfigKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileId => '||nvl(to_char(pSourceFileId), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + SELECT A_SOURCE_FILE_CONFIG_KEY + INTO vSourceFileConfigKey + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE SOURCE_FILE_ID = pSourceFileId + AND SOURCE_FILE_TYPE = 'CONTAINER'; + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vSourceFileConfigKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.MSG_MISSING_CONTAINER_CONFIG := 'No match in A_SOURCE_FILE_CONFIG where SOURCE_FILE_TYPE=''CONTAINER'' and SOURCE_FILE_ID = '''||pSourceFileId||''''; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_CONTAINER_CONFIG, 'WARNING', vParameters); + RETURN NULL; + WHEN TOO_MANY_ROWS THEN + ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES := 'GET_CONTAINER_SOURCE_FILE_CONFIG_KEY: Multiple SOURCE_FILE_TYPE=''CONTAINER'' matches for SOURCE_FILE_ID: '||pSourceFileId||' in A_SOURCE_FILE_CONFIG'; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_CONTAINER_ENTRIES, ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + END GET_CONTAINER_SOURCE_FILE_CONFIG_KEY; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_SOURCE_FILE_CONFIG_KEY ( + pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE DEFAULT 'INPUT' + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE + ) RETURN PLS_INTEGER + IS + vSourceFileConfigKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileType => '''||nvl(pSourceFileType, 'NULL')||'''' + ,'pSourceFileId => '''||nvl(pSourceFileId, 'NULL')||'''' + ,'pTableId => '''||nvl(pTableId, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + SELECT A_SOURCE_FILE_CONFIG_KEY + INTO vSourceFileConfigKey + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE SOURCE_FILE_TYPE = pSourceFileType + AND SOURCE_FILE_ID = pSourceFileId + AND TABLE_ID = pTableId; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG', vParameters); + RETURN vSourceFileConfigKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + RETURN NULL; + WHEN TOO_MANY_ROWS THEN + vgMsgTmp := ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE + ||cgBL||' '||'GET_SOURCE_FILE_CONFIG_KEY: Multiple matches in A_SOURCE_FILE_CONFIG table WHERE' + ||cgBL||' '||'SOURCE_FILE_TYPE: '||pSourceFileType + ||cgBL||' '||'SOURCE_FILE_ID: '||pSourceFileId + ||cgBL||' '||'TABLE_ID: '||pTableId; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE, vgMsgTmp); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + END GET_SOURCE_FILE_CONFIG_KEY; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE ADD_SOURCE_FILE_CONFIG ( + pSourceKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_KEY%TYPE + ,pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pSourceFileDesc IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_DESC%TYPE + ,pSourceFileNamePattern IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_NAME_PATTERN%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE DEFAULT NULL + ,pTemplateTableName IN CT_MRDS.A_SOURCE_FILE_CONFIG.TEMPLATE_TABLE_NAME%TYPE DEFAULT NULL + ,pContainerFileKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.CONTAINER_FILE_KEY%TYPE DEFAULT NULL + ,pEncoding IN CT_MRDS.A_SOURCE_FILE_CONFIG.ENCODING%TYPE DEFAULT NULL -- MARS-1049 + ,pIsWorkflowSuccessRequired IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_WORKFLOW_SUCCESS_REQUIRED%TYPE DEFAULT 'Y' -- MARS-1409 + ,pIsArchiveEnabled IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_ARCHIVE_ENABLED%TYPE DEFAULT 'N' -- MARS-828 + ,pIsKeptInTrash IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_KEPT_IN_TRASH%TYPE DEFAULT 'Y' -- MARS-828 + ,pArchivalStrategy IN CT_MRDS.A_SOURCE_FILE_CONFIG.ARCHIVAL_STRATEGY%TYPE DEFAULT 'THRESHOLD_BASED' -- MARS-828 + ,pMinimumAgeMonths IN CT_MRDS.A_SOURCE_FILE_CONFIG.MINIMUM_AGE_MONTHS%TYPE DEFAULT 0 -- MARS-828 + ) IS + vSourceFileConfigKey PLS_INTEGER; + vSourceKeyExists PLS_INTEGER := 0; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pSourceKey => '''||nvl(to_char(pSourceKey), 'NULL')||'''' + ,'pSourceFileType => '''||nvl(to_char(pSourceFileType), 'NULL')||'''' + ,'pSourceFileId => '''||nvl(to_char(pSourceFileId), 'NULL')||'''' + ,'pSourceFileDesc => '''||nvl(to_char(pSourceFileDesc), 'NULL')||'''' + ,'pSourceFileNamePattern => '''||nvl(to_char(pSourceFileNamePattern), 'NULL')||'''' + ,'pTableId => '''||nvl(to_char(pTableId), 'NULL')||'''' + ,'pTemplateTableName => '''||nvl(to_char(pTemplateTableName), 'NULL')||'''' + ,'pContainerFileKey => '''||nvl(to_char(pContainerFileKey), 'NULL')||'''' + ,'pEncoding => '''||nvl(to_char(pEncoding), 'NULL')||'''' -- MARS-1049: NOWY + ,'pIsWorkflowSuccessRequired => '''||nvl(to_char(pIsWorkflowSuccessRequired), 'NULL')||'''' -- MARS-1409 + ,'pIsArchiveEnabled => '''||nvl(to_char(pIsArchiveEnabled), 'NULL')||'''' -- MARS-828 + ,'pIsKeptInTrash => '''||nvl(to_char(pIsKeptInTrash), 'NULL')||'''' -- MARS-828 + ,'pArchivalStrategy => '''||nvl(to_char(pArchivalStrategy), 'NULL')||'''' -- MARS-828 + ,'pMinimumAgeMonths => '''||nvl(to_char(pMinimumAgeMonths), 'NULL')||'''' -- MARS-828 + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + INSERT INTO CT_MRDS.A_SOURCE_FILE_CONFIG(A_SOURCE_KEY, SOURCE_FILE_TYPE, SOURCE_FILE_ID, SOURCE_FILE_DESC, SOURCE_FILE_NAME_PATTERN, TABLE_ID, TEMPLATE_TABLE_NAME, CONTAINER_FILE_KEY, ENCODING, IS_WORKFLOW_SUCCESS_REQUIRED, IS_ARCHIVE_ENABLED, IS_KEPT_IN_TRASH, ARCHIVAL_STRATEGY, MINIMUM_AGE_MONTHS) + VALUES (pSourceKey, pSourceFileType, pSourceFileId, pSourceFileDesc, pSourceFileNamePattern, pTableId, pTemplateTableName, pContainerFileKey, pEncoding, pIsWorkflowSuccessRequired, pIsArchiveEnabled, pIsKeptInTrash, pArchivalStrategy, pMinimumAgeMonths); + COMMIT; + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + EXCEPTION + + WHEN OTHERS THEN + IF SQLCODE = -2291 THEN + ENV_MANAGER.MSG_MISSING_SOURCE_KEY := 'The Source with A_SOURCE_KEY: '''||pSourceKey||''' not found in parent table A_SOURCE.' + ||cgBL||'First add a record to A_SOURCE:' + ||cgBL||'call file_manager.add_source(pSourceKey => '''||pSourceKey||''', pSourceName => ''...'')'; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_SOURCE_KEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_SOURCE_KEY, ENV_MANAGER.MSG_MISSING_SOURCE_KEY); + ELSE + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END IF; + + END ADD_SOURCE_FILE_CONFIG; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE ADD_COLUMN_DATE_FORMAT ( + pTemplateTableName IN CT_MRDS.A_COLUMN_DATE_FORMAT.TEMPLATE_TABLE_NAME%TYPE + ,pColumnName IN CT_MRDS.A_COLUMN_DATE_FORMAT.COLUMN_NAME%TYPE + ,pDateFormat IN CT_MRDS.A_COLUMN_DATE_FORMAT.DATE_FORMAT%TYPE + ) IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pColumnName => '''||nvl(pColumnName, 'NULL')||'''' + ,'pDateFormat => '''||nvl(pDateFormat, 'NULL')||'''' + )); + + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + INSERT INTO CT_MRDS.A_COLUMN_DATE_FORMAT(TEMPLATE_TABLE_NAME, COLUMN_NAME, DATE_FORMAT) + VALUES (pTemplateTableName, pColumnName, pDateFormat); + COMMIT; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END ADD_COLUMN_DATE_FORMAT; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUCKET_URI(pBucketArea VARCHAR2) + RETURN VARCHAR2 + IS + BEGIN + CASE pBucketArea + WHEN 'INBOX' THEN RETURN ENV_MANAGER.gvInboxBucketUri; + WHEN 'ODS' THEN RETURN ENV_MANAGER.gvDataBucketUri; + WHEN 'DATA' THEN RETURN ENV_MANAGER.gvDataBucketUri; + WHEN 'ARCHIVE' THEN RETURN ENV_MANAGER.gvArchiveBucketUri; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_INVALID_BUCKET_AREA, + ENV_MANAGER.MSG_INVALID_BUCKET_AREA || ' Provided: ''' || pBucketArea || ''''); + END CASE; + END; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DET_SOURCE_FILE_CONFIG_INFO ( + pSourceFileConfigKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY%TYPE + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2 + ---- + -- Function to get info about File Configuration entry + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vContainerFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vCount PLS_INTEGER := 0; + addColHeader BOOLEAN := TRUE; + vMsgTmp VARCHAR2(32000):= ''; + CURSOR cColumnFormat(vTableName A_COLUMN_DATE_FORMAT.TEMPLATE_TABLE_NAME%TYPE) IS + SELECT * + FROM CT_MRDS.A_COLUMN_DATE_FORMAT + WHERE TEMPLATE_TABLE_NAME = vTableName; + vColumnDateFormat A_COLUMN_DATE_FORMAT%ROWTYPE; + + FUNCTION FORMAT_CONFIG( pSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE, + pTypeInfo VARCHAR2 DEFAULT 'File Configuration', + pLevel VARCHAR2 DEFAULT '') + RETURN VARCHAR2 + IS + vMsg VARCHAR2(9999); + BEGIN + vMsg := '' + ||cgBL||pLevel||''||'Details about '||pTypeInfo||':' + ||cgBL||pLevel||''||'--------------------------------' + ||cgBL||pLevel||'A_SOURCE_FILE_CONFIG_KEY = '||pSourceFileConfig.A_SOURCE_FILE_CONFIG_KEY + ||cgBL||pLevel||'A_SOURCE_KEY = '||pSourceFileConfig.A_SOURCE_KEY + ||cgBL||pLevel||'SOURCE_FILE_TYPE = '||pSourceFileConfig.SOURCE_FILE_TYPE + ||cgBL||pLevel||'SOURCE_FILE_ID = '||pSourceFileConfig.SOURCE_FILE_ID + ||cgBL||pLevel||'SOURCE_FILE_DESC = '||pSourceFileConfig.SOURCE_FILE_DESC + ||cgBL||pLevel||'SOURCE_FILE_NAME_PATTERN = '||pSourceFileConfig.SOURCE_FILE_NAME_PATTERN + ||cgBL||pLevel||'TABLE_ID = '||pSourceFileConfig.TABLE_ID + ||cgBL||pLevel||'TEMPLATE_TABLE_NAME = '||pSourceFileConfig.TEMPLATE_TABLE_NAME + ||cgBL||pLevel||'CONTAINER_FILE_KEY = '||pSourceFileConfig.CONTAINER_FILE_KEY + ||cgBL||pLevel||'ODS_SCHEMA_NAME = '||pSourceFileConfig.ODS_SCHEMA_NAME + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_DAYS = '||pSourceFileConfig.ARCHIVE_THRESHOLD_DAYS + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_FILES_COUNT = '||pSourceFileConfig.ARCHIVE_THRESHOLD_FILES_COUNT + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_BYTES_SUM = '||pSourceFileConfig.ARCHIVE_THRESHOLD_BYTES_SUM + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_ROWS_COUNT = '||pSourceFileConfig.ARCHIVE_THRESHOLD_ROWS_COUNT + ||cgBL||pLevel||'HOURS_TO_EXPIRE_STATISTICS = '||pSourceFileConfig.HOURS_TO_EXPIRE_STATISTICS + ||cgBL||pLevel||'ENCODING = '||pSourceFileConfig.ENCODING + ||cgBL||pLevel||'IS_ARCHIVE_ENABLED = '||pSourceFileConfig.IS_ARCHIVE_ENABLED + ||cgBL||pLevel||'IS_KEPT_IN_TRASH = '||pSourceFileConfig.IS_KEPT_IN_TRASH + ||cgBL||pLevel||'ARCHIVAL_STRATEGY = '||pSourceFileConfig.ARCHIVAL_STRATEGY + ||cgBL||pLevel||'MINIMUM_AGE_MONTHS = '||pSourceFileConfig.MINIMUM_AGE_MONTHS + ||cgBL||pLevel||'IS_WORKFLOW_SUCCESS_REQUIRED = '||pSourceFileConfig.IS_WORKFLOW_SUCCESS_REQUIRED + + ||cgBL||pLevel||''||'--------------------------------' + ; + RETURN vMsg; + END FORMAT_CONFIG; + + BEGIN + vMsgTmp := ''; + -- Get Main Config + BEGIN + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE A_SOURCE_FILE_CONFIG_KEY = pSourceFileConfigKey; + + vMsgTmp := FORMAT_CONFIG(pSourceFileConfig => vSourceFileConfig, pTypeInfo => 'File Configuration'); + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := 'There is no config entry in A_SOURCE_FILE_CONFIG where A_SOURCE_FILE_CONFIG_KEY = '||pSourceFileConfigKey; + RETURN vMsgTmp; + END; + + -- Get Container Config + IF pIncludeContainerInfo > 0 THEN + BEGIN + SELECT * + INTO vContainerFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE A_SOURCE_FILE_CONFIG_KEY = vSourceFileConfig.CONTAINER_FILE_KEY; + + vMsgTmp := vMsgTmp || cgBL || FORMAT_CONFIG(pSourceFileConfig => vContainerFileConfig, pTypeInfo => 'related Container Config', pLevel => ' '); + + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := vMsgTmp|| cgBL || 'There is no CONTAINER config entry in A_SOURCE_FILE_CONFIG.'; + -- RETURN vMsgTmp; + END; + END IF; + + -- Get Column Date Format Config + IF pIncludeColumnFormatInfo > 0 THEN + BEGIN + OPEN cColumnFormat(vTableName => vSourceFileConfig.TEMPLATE_TABLE_NAME); + LOOP + FETCH cColumnFormat INTO vColumnDateFormat; + IF cColumnFormat%FOUND AND addColHeader THEN + vCount := 1; + vMsgTmp := vMsgTmp||cgBL|| cgBL || ' Column Date Format config entries:'; + vMsgTmp := vMsgTmp||cgBL||''||' --------------------------------'; + addColHeader := FALSE; + END IF; + EXIT WHEN cColumnFormat%NOTFOUND; + vMsgTmp := vMsgTmp + ||cgBL||' TEMPLATE_TABLE_NAME = '||vColumnDateFormat.TEMPLATE_TABLE_NAME + ||cgBL||' COLUMN_NAME = '||vColumnDateFormat.COLUMN_NAME + ||cgBL||' DATE_FORMAT = '||vColumnDateFormat.DATE_FORMAT + ||cgBL||''||' --------------------------------'; + END LOOP; + If vCount=0 THEN + vMsgTmp := vMsgTmp || cgBL || 'There is no Column Date Format config entries in A_COLUMN_DATE_FORMAT where TEMPLATE_TABLE_NAME = '||NVL(vSourceFileConfig.TEMPLATE_TABLE_NAME,'NULL'); + END IF; + CLOSE cColumnFormat; + END; + END IF; + + RETURN vMsgTmp; + EXCEPTION + + WHEN OTHERS THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END GET_DET_SOURCE_FILE_CONFIG_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DET_SOURCE_FILE_RECEIVED_INFO ( + pSourceFileReceivedKey IN CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE + ,pIncludeConfigInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2 + ---- + -- Function to get info about File Received entry + IS + vSourceFileReceived CT_MRDS.A_SOURCE_FILE_RECEIVED%ROWTYPE; + vMsgTmp VARCHAR2(32000):= ''; + BEGIN + + BEGIN + SELECT * + INTO vSourceFileReceived + FROM CT_MRDS.A_SOURCE_FILE_RECEIVED + WHERE A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + vMsgTmp := '' + ||cgBL||''||'Details about received file:' + ||cgBL||''||'--------------------------------' + ||cgBL||'A_SOURCE_FILE_RECEIVED_KEY = '||vSourceFileReceived.A_SOURCE_FILE_RECEIVED_KEY + ||cgBL||'A_SOURCE_FILE_CONFIG_KEY = '||vSourceFileReceived.A_SOURCE_FILE_CONFIG_KEY + ||cgBL||'SOURCE_FILE_NAME = '||vSourceFileReceived.SOURCE_FILE_NAME + ||cgBL||'CHECKSUM = '||vSourceFileReceived.CHECKSUM + ||cgBL||'CREATED = '||vSourceFileReceived.CREATED + ||cgBL||'BYTES = '||vSourceFileReceived.BYTES + ||cgBL||'RECEPTION_DATE = '||vSourceFileReceived.RECEPTION_DATE + ||cgBL||'PROCESSING_STATUS = '||vSourceFileReceived.PROCESSING_STATUS + ||cgBL||'EXTERNAL_TABLE_NAME = '||vSourceFileReceived.EXTERNAL_TABLE_NAME + ||cgBL||'PARTITION_YEAR = '||vSourceFileReceived.PARTITION_YEAR + ||cgBL||'PARTITION_MONTH = '||vSourceFileReceived.PARTITION_MONTH + ||cgBL||''||'--------------------------------' + ; + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := 'There is no data in A_SOURCE_FILE_RECEIVED where A_SOURCE_FILE_RECEIVED_KEY = '||pSourceFileReceivedKey; + RETURN vMsgTmp; + END; + + IF pIncludeConfigInfo>0 THEN + vMsgTmp := vMsgTmp || cgBL || CT_MRDS.FILE_MANAGER.GET_DET_SOURCE_FILE_CONFIG_INFO(vSourceFileReceived.A_SOURCE_FILE_CONFIG_KEY,pIncludeContainerInfo,pIncludeColumnFormatInfo); + END IF; + RETURN vMsgTmp; + EXCEPTION + + WHEN OTHERS THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END GET_DET_SOURCE_FILE_RECEIVED_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DET_USER_LOAD_OPERATIONS ( + pOperationId PLS_INTEGER + ) RETURN VARCHAR2 + ---- + -- Function to get info about File Received entry + IS + vUserLoadOperations USER_LOAD_OPERATIONS%ROWTYPE; + vMsgTmp VARCHAR2(32000):= ''; + BEGIN + + BEGIN + SELECT * + INTO vUserLoadOperations + FROM USER_LOAD_OPERATIONS + WHERE id = pOperationId; + + vMsgTmp := '' + ||''||'Details about USER_LOAD_OPERATIONS where ID = '||pOperationId + ||cgBL||''||'--------------------------------' + ||cgBL||'ID = '||vUserLoadOperations.ID + ||cgBL||'TYPE = '||vUserLoadOperations.TYPE + ||cgBL||'SID = '||vUserLoadOperations.SID + ||cgBL||'SERIAL# = '||vUserLoadOperations.SERIAL# + ||cgBL||'START_TIME = '||vUserLoadOperations.START_TIME + ||cgBL||'UPDATE_TIME = '||vUserLoadOperations.UPDATE_TIME + ||cgBL||'STATUS = '||vUserLoadOperations.STATUS + ||cgBL||'OWNER_NAME = '||vUserLoadOperations.OWNER_NAME + ||cgBL||'TABLE_NAME = '||vUserLoadOperations.TABLE_NAME + ||cgBL||'PARTITION_NAME = '||vUserLoadOperations.PARTITION_NAME + ||cgBL||'SUBPARTITION_NAME = '||vUserLoadOperations.SUBPARTITION_NAME + ||cgBL||'FILE_URI_LIST = '||vUserLoadOperations.FILE_URI_LIST + ||cgBL||'ROWS_LOADED = '||vUserLoadOperations.ROWS_LOADED + ||cgBL||'LOGFILE_TABLE = '||vUserLoadOperations.LOGFILE_TABLE + ||cgBL||'BADFILE_TABLE = '||vUserLoadOperations.BADFILE_TABLE + ||cgBL||'STATUS_TABLE = '||vUserLoadOperations.STATUS_TABLE + ||cgBL||'TEMPEXT_TABLE = '||vUserLoadOperations.TEMPEXT_TABLE + ||cgBL||'CREDENTIAL_NAME = '||vUserLoadOperations.CREDENTIAL_NAME + ||cgBL||'EXPIRATION_TIME = '||vUserLoadOperations.EXPIRATION_TIME + ||cgBL||''||'--------------------------------' + ; + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := 'There is no data in USER_LOAD_OPERATIONS where ID = '||pOperationId; + RETURN vMsgTmp; + END; + + RETURN vMsgTmp; + EXCEPTION + + WHEN OTHERS THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END GET_DET_USER_LOAD_OPERATIONS; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION ANALYZE_VALIDATION_ERRORS( + pSourceFileReceivedKey IN NUMBER + ) RETURN VARCHAR2 + IS + vSourceFileReceived CT_MRDS.A_SOURCE_FILE_RECEIVED%ROWTYPE; + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vValidationLogTable VARCHAR2(128); + vTemplateSchema VARCHAR2(128); + vTemplateTable VARCHAR2(128); + vCsvFileUri VARCHAR2(4000); + vParameters VARCHAR2(4000); + vResult VARCHAR2(32000); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( + 'pSourceFileReceivedKey => ' || pSourceFileReceivedKey + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start', 'DEBUG', vParameters); + + -- Get file and config information + BEGIN + -- Get source file received data first + SELECT * + INTO vSourceFileReceived + FROM CT_MRDS.A_SOURCE_FILE_RECEIVED sfr + WHERE sfr.A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + -- Get source file config data + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG sfc + WHERE sfc.A_SOURCE_FILE_CONFIG_KEY = vSourceFileReceived.A_SOURCE_FILE_CONFIG_KEY; + EXCEPTION + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.LOG_PROCESS_ERROR('Source file or config not found for key: ' || pSourceFileReceivedKey, vParameters); + RETURN 'Error: Source file with key ' || pSourceFileReceivedKey || ' not found in A_SOURCE_FILE_RECEIVED table'; + END; + + -- Extract template schema and table from template table name + vTemplateSchema := REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME, '^([^.]+)'); + vTemplateTable := REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME, '\.(.+)$', 1, 1, NULL, 1); + + -- Build CSV file URI + vCsvFileUri := ENV_MANAGER.gvInboxBucketUri || + 'INBOX/' || vSourceFileConfig.A_SOURCE_KEY || '/' || + vSourceFileConfig.SOURCE_FILE_ID || '/' || + vSourceFileConfig.TABLE_ID || '/' || + vSourceFileReceived.SOURCE_FILE_NAME; + + -- Find validation log table (most recent one with errors) + -- Look for validation log tables related to this external table + BEGIN + SELECT table_name + INTO vValidationLogTable + FROM ( + SELECT table_name, + REGEXP_SUBSTR(table_name, '\$([0-9]+)_', 1, 1, NULL, 1) as log_number + FROM USER_TABLES + WHERE table_name LIKE 'VALIDATE$%_LOG' + ORDER BY TO_NUMBER(log_number) DESC + ) + WHERE ROWNUM = 1; + EXCEPTION + WHEN NO_DATA_FOUND THEN + -- If no validation log tables found, use a default name + vValidationLogTable := 'VALIDATE$999_LOG'; + END; + + ENV_MANAGER.LOG_PROCESS_EVENT('Calling ENV_MANAGER.ANALYZE_VALIDATION_ERRORS with parameters: ' || + 'LogTable=' || vValidationLogTable || + ', Schema=' || vTemplateSchema || + ', Table=' || vTemplateTable || + ', URI=' || SUBSTR(vCsvFileUri, 1, 100) || '...', 'DEBUG', vParameters); + + -- Call the main function with derived parameters + vResult := ENV_MANAGER.ANALYZE_VALIDATION_ERRORS( + pValidationLogTable => vValidationLogTable, + pTemplateSchema => vTemplateSchema, + pTemplateTable => vTemplateTable, + pCsvFileUri => vCsvFileUri + ); + + ENV_MANAGER.LOG_PROCESS_EVENT('End', 'DEBUG', vParameters); + RETURN vResult; + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_ERROR('Error in ANALYZE_VALIDATION_ERRORS: ' || SQLERRM, vParameters); + RETURN 'Error analyzing validation errors: ' || SQLERRM || + cgBL || 'Source File Key: ' || pSourceFileReceivedKey || + cgBL || 'Check A_SOURCE_FILE_RECEIVED and A_SOURCE_FILE_CONFIG tables for data integrity.'; + END ANALYZE_VALIDATION_ERRORS; + + ---------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS IMPLEMENTATION + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_VERSION; + END GET_VERSION; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUILD_INFO + RETURN VARCHAR2 + IS + BEGIN + RETURN ENV_MANAGER.GET_PACKAGE_VERSION_INFO( + pPackageName => 'FILE_MANAGER', + pVersion => PACKAGE_VERSION, + pBuildDate => PACKAGE_BUILD_DATE, + pAuthor => PACKAGE_AUTHOR + ); + END GET_BUILD_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION_HISTORY + RETURN VARCHAR2 + IS + BEGIN + RETURN ENV_MANAGER.FORMAT_VERSION_HISTORY( + pPackageName => 'FILE_MANAGER', + pVersionHistory => VERSION_HISTORY + ); + END GET_VERSION_HISTORY; + + ---------------------------------------------------------------------------------------------------- + +END; + +/ + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER.pkg b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER.pkg new file mode 100644 index 0000000..157b618 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER.pkg @@ -0,0 +1,658 @@ +create or replace PACKAGE CT_MRDS.FILE_MANAGER +AUTHID CURRENT_USER +AS + /** + * General comment for package: Please put comments for functions and procedures as shown in below example. + * It is a standard. + * The structure of comment is used by GET_PACKAGE_DOCUMENTATION function + * which returns documentation text for confluence page (to Copy-Paste it). + **/ + + -- Example comment: + /** + * @name EX_PROCEDURE_NAME + * @desc Procedure description + * @example select FILE_MANAGER.EX_PROCEDURE_NAME(pParameter => 129) from dual; + * @ex_rslt Example Result + **/ + + -- Package Version Information (Semantic Versioning: MAJOR.MINOR.PATCH) + PACKAGE_VERSION CONSTANT VARCHAR2(10) := '3.6.3'; + PACKAGE_BUILD_DATE CONSTANT VARCHAR2(20) := '2026-03-17 12:30:00'; + PACKAGE_AUTHOR CONSTANT VARCHAR2(100) := 'Grzegorz Michalski'; + + -- Version History (Latest changes first) + VERSION_HISTORY CONSTANT VARCHAR2(4000) := + '3.6.3 (2026-03-17): MARS-828 - Added pIsArchiveEnabled, pIsKeptInTrash, pArchivalStrategy, pMinimumAgeMonths to ADD_SOURCE_FILE_CONFIG; FORMAT_CONFIG now shows all A_SOURCE_FILE_CONFIG columns' || CHR(13)||CHR(10) || + '3.6.2 (2026-03-17): MARS-1409 - Added pIsWorkflowSuccessRequired parameter to ADD_SOURCE_FILE_CONFIG; IS_WORKFLOW_SUCCESS_REQUIRED shown in GET_DET_SOURCE_FILE_CONFIG_INFO output' || CHR(13)||CHR(10) || + '3.6.1 (2026-03-13): MARS-1468 - Fixed CHAR/NCHAR/NVARCHAR2 column definitions in GENERATE_EXTERNAL_TABLE_PARAMS: CHAR now uses char_used/char_length semantics; NCHAR/NVARCHAR2 use char_length (data_length stores bytes in AL16UTF16)' || CHR(13)||CHR(10) || + '3.6.0 (2026-02-27): MARS-1409 - Added A_WORKFLOW_HISTORY_KEY tracking in A_SOURCE_FILE_RECEIVED. Each file now stores its workflow execution key extracted during VALIDATE_SOURCE_FILE_RECEIVED' || CHR(13)||CHR(10) || + '3.5.1 (2026-02-24): Fixed TIMESTAMP field syntax in GENERATE_EXTERNAL_TABLE_PARAMS for SQL*Loader compatibility (CHAR(35) DATE_FORMAT TIMESTAMP MASK format)' || CHR(13)||CHR(10) || + '3.3.2 (2026-02-20): MARS-828 - Fixed threshold column names in GET_DET_SOURCE_FILE_CONFIG_INFO for MARS-828 compatibility' || CHR(13)||CHR(10) || + '3.3.1 (2025-11-27): MARS-1046 - Fixed ISO 8601 datetime format parsing with milliseconds and timezone (e.g., 2012-03-02T14:16:23.798+01:00)' || CHR(13)||CHR(10) || + '3.3.0 (2025-11-26): MARS-1056 - Fixed VARCHAR2 definitions in GENERATE_EXTERNAL_TABLE_PARAMS to preserve CHAR/BYTE semantics from template tables' || CHR(13)||CHR(10) || + '3.2.1 (2025-11-24): MARS-1049 - Added pEncoding parameter support for CSV character set specification' || CHR(13)||CHR(10) || + '3.2.0 (2025-10-22): Added package versioning system using centralized ENV_MANAGER functions' || CHR(13)||CHR(10) || + '3.1.0 (2025-10-20): Enhanced PROCESS_SOURCE_FILE with 6-step validation workflow' || CHR(13)||CHR(10) || + '3.0.0 (2025-10-15): Separated export procedures into dedicated DATA_EXPORTER package' || CHR(13)||CHR(10) || + '2.5.0 (2025-10-10): Added DELETE_SOURCE_CASCADE for safe configuration removal' || CHR(13)||CHR(10) || + '2.0.0 (2025-09-25): Added official path patterns support (INBOX 3-level, ODS 2-level, ARCHIVE 2-level)' || CHR(13)||CHR(10) || + '1.0.0 (2025-09-01): Initial release with file processing and validation capabilities'; + + TYPE tSourceFileReceived IS RECORD + ( + A_SOURCE_FILE_RECEIVED_KEY CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE, + A_SOURCE_FILE_CONFIG_KEY CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_CONFIG_KEY%TYPE, + SOURCE_FILE_PREFIX_INBOX VARCHAR2(430), + SOURCE_FILE_PREFIX_ODS VARCHAR2(430), + SOURCE_FILE_PREFIX_QUARANTINE VARCHAR2(430), + SOURCE_FILE_PREFIX_ARCHIVE VARCHAR2(430), + SOURCE_FILE_NAME CT_MRDS.A_SOURCE_FILE_RECEIVED.SOURCE_FILE_NAME%TYPE, + RECEPTION_DATE CT_MRDS.A_SOURCE_FILE_RECEIVED.RECEPTION_DATE%TYPE, + PROCESSING_STATUS CT_MRDS.A_SOURCE_FILE_RECEIVED.PROCESSING_STATUS%TYPE, + EXTERNAL_TABLE_NAME CT_MRDS.A_SOURCE_FILE_RECEIVED.EXTERNAL_TABLE_NAME%TYPE + ); + + cgBL CONSTANT VARCHAR2(2) := CHR(13)||CHR(10); + vgSourceFileConfigKey PLS_INTEGER; + vgMsgTmp VARCHAR2(32000); + + + + --------------------------------------------------------------------------------------------------------------------------- + --------------------------------------------------------------------------------------------------------------------------- + + /** + * @name GET_SOURCE_FILE_CONFIG + * @desc Get source file type by matching the source file name against source file type naming patterns + * or by specifying the id of a received source file. + * @example ... + * @ex_rslt "CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE" + **/ + FUNCTION GET_SOURCE_FILE_CONFIG(pFileUri IN VARCHAR2 DEFAULT NULL + , pSourceFileReceivedKey IN NUMBER DEFAULT NULL + , pSourceFileConfigKey IN NUMBER DEFAULT NULL) + RETURN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + + + + /** + * @name REGISTER_SOURCE_FILE_RECEIVED + * @desc Register a newly received source file in A_SOURCE_FILE_RECEIVED table. + * This overload automatically determines source file type from the file name. + * It returns the value of A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY column for newly added record. + * @example vSourceFileReceivedKey := FILE_MANAGER.REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv'); + * @ex_rslt 3245 + **/ + FUNCTION REGISTER_SOURCE_FILE_RECEIVED ( + pSourceFileReceivedName IN VARCHAR2 + ) + RETURN PLS_INTEGER; + + + + /** + * @name REGISTER_SOURCE_FILE_RECEIVED + * @desc Register a new new source file in A_SOURCE_FILE_RECEIVED table based on pSourceFileReceivedName and pSourceFileConfig. + * Then it returns the value of A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY column for newly added record. + * @example vSourceFileReceivedKey := FILE_MANAGER.REGISTER_SOURCE_FILE_RECEIVED( + * pSourceFileReceivedName => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv' + * ,pSourceFileConfig => ...A_SOURCE_FILE_CONFIG%ROWTYPE... ); + * @ex_rslt 3245 + **/ + FUNCTION REGISTER_SOURCE_FILE_RECEIVED ( + pSourceFileReceivedName IN VARCHAR2, + pSourceFileConfig IN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE + ) + RETURN PLS_INTEGER; + + + + /** + * @name SET_SOURCE_FILE_RECEIVED_STATUS + * @desc Set status of file in A_SOURCE_FILE_RECEIVED table - PROCESSING_STATUS column + * based on A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY + * and provided value of pStatus parameter + * @example exec FILE_MANAGER.SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => 377, pStatus => 'READY_FOR_INGESTION'); + **/ + PROCEDURE SET_SOURCE_FILE_RECEIVED_STATUS( + pSourceFileReceivedKey IN PLS_INTEGER, + pStatus IN VARCHAR2 + ); + + + + /** + * @name GET_EXTERNAL_TABLE_COLUMNS + * @desc Function used to get string with all table columns definitions based on pTargetTableTemplate "TEMPLATE TABLE" name. + * It used for creating "EXTERNAL TABLE" using CREATE_EXTERNAL_TABLE procedure. + * @example select FILE_MANAGER.GET_EXTERNAL_TABLE_COLUMNS(pTargetTableTemplate => 'CT_ET_TEMPLATES.LM_STANDING_FACILITIES_HEADER') from dual; + * @ex_rslt "A_KEY" NUMBER(38,0) NOT NULL ENABLE, + * "A_WORKFLOW_HISTORY_KEY" NUMBER(38,0) NOT NULL ENABLE, + * "REV_NUMBER" NUMBER(28,0), + * "REF_DATE" DATE, + * "FREE_TEXT" VARCHAR2(1000 CHAR), + * "MLF_BS_TOTAL" NUMBER(28,10), + * "DF_BS_TOTAL" NUMBER(28,10), + * "MLF_SF_TOTAL" NUMBER(28,10), + * "DF_SF_TOTAL" NUMBER(28,10) + **/ + FUNCTION GET_EXTERNAL_TABLE_COLUMNS ( + pTargetTableTemplate IN VARCHAR2 + ) + RETURN CLOB; + + + + /** + * @name CREATE_EXTERNAL_TABLE + * @desc A wrapper procedure for DBMS_CLOUD.CREATE_EXTERNAL_TABLE which creates External Table + * MARS-1049: Added pEncoding parameter for CSV character set specification + * @param pEncoding - Character set encoding for CSV files (e.g., 'UTF8', 'WE8MSWIN1252') + * If provided, adds CHARACTERSET clause to external table definition + * @example + * begin + * FILE_MANAGER.CREATE_EXTERNAL_TABLE( + * pTableName => 'STANDING_FACILITIES_HEADER', + * pTemplateTableName => 'CT_ET_TEMPLATES.LM_STANDING_FACILITIES_HEADER', + * pPrefix => 'ODS/LM/STANDING_FACILITIES_HEADER/', + * pBucketUri => 'https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/frcnomajoc7v/b/mrds_data_tst/o/', + * pFileName => NULL, + * pDelimiter => ',', + * pEncoding => 'UTF8' + * ); + * end; + **/ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL -- MARS-1049: NOWY PARAMETR + ); + + + + /** + * @name CREATE_EXTERNAL_TABLE + * @desc Creates External Table for single file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * @example exec FILE_MANAGER.CREATE_EXTERNAL_TABLE(pSourceFileReceivedKey => 377);; + **/ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pSourceFileReceivedKey IN NUMBER + ); + + + + /** + * @name VALIDATE_SOURCE_FILE_RECEIVED + * @desc A wrapper procedure for DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE + * It validate External table build upon single file + * provided by pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * @example exec FILE_MANAGER.VALIDATE_SOURCE_FILE_RECEIVED(pSourceFileReceivedKey => 377); + **/ + PROCEDURE VALIDATE_SOURCE_FILE_RECEIVED + ( + pSourceFileReceivedKey IN NUMBER + ); + + + /** + * @name VALIDATE_EXTERNAL_TABLE + * @desc A wrapper function for DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE. + * It validates External Table provided by parameter pTableName. + * It returns: PASSED or FAILED. + * @example + * declare + * vStatus VARCHAR2(100); + * begin + * vStatus := FILE_MANAGER.VALIDATE_EXTERNAL_TABLE(pTableName => 'STANDING_FACILITIES_HEADER'); + * DBMS_OUTPUT.PUT_LINE('vStatus = '||vStatus); + * end; + * + * @ex_rslt FAILED + **/ + FUNCTION VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2; + + + /** + * @name S_VALIDATE_EXTERNAL_TABLE + * @desc A function which checks if SELECT query reterns any rows. + * It trys to selects External Table provided by parameter pTableName. + * It returns: PASSED or FAILED. + * @example + * declare + * vStatus VARCHAR2(100); + * begin + * vStatus := FILE_MANAGER.S_VALIDATE_EXTERNAL_TABLE(pTableName => 'STANDING_FACILITIES_HEADER'); + * DBMS_OUTPUT.PUT_LINE('vStatus = '||vStatus); + * end; + * + * @ex_rslt PASSED + **/ + FUNCTION S_VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2; + + + + /** + * @name DROP_EXTERNAL_TABLE + * @desc It drops External Table for single file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * @example exec FILE_MANAGER.DROP_EXTERNAL_TABLE(pSourceFileReceivedKey => 377); + **/ + PROCEDURE DROP_EXTERNAL_TABLE ( + pSourceFileReceivedKey IN NUMBER + ); + + + + /** + * @name COPY_FILE + * @desc It copies file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * into destination provided by pDestination parameter. + * pDestination parameter allowed values are: 'ODS' + * @example exec FILE_MANAGER.COPY_FILE(pSourceFileReceivedKey => 377, pDestination => 'ODS'); + **/ + PROCEDURE COPY_FILE( + pSourceFileReceivedKey IN NUMBER, + pDestination IN VARCHAR2 + ); + + + + + /** + * @name MOVE_FILE + * @desc It moves file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * into destination provided by pDestination parameter. + * pDestination parameter allowed values are: 'ODS', 'QUARANTINE' + * @example exec FILE_MANAGER.MOVE_FILE(pSourceFileReceivedKey => 377, pDestination => 'ODS'); + **/ + PROCEDURE MOVE_FILE( + pSourceFileReceivedKey IN NUMBER, + pDestination IN VARCHAR2 + ); + + + + /** + * @name DELETE_FOLDER_CONTENTS + * @desc It deletes all files from specified folder in the cloud storage. + * The procedure lists all objects in the specified folder prefix and deletes them one by one. + * pBucketArea parameter specifies which bucket to use: 'INBOX', 'DATA', 'ARCHIVE' + * pFolderPrefix parameter specifies the folder path within the bucket (e.g., 'C2D/UC_DISSEM/UC_NMA_DISSEM/') + * @example exec FILE_MANAGER.DELETE_FOLDER_CONTENTS(pBucketArea => 'INBOX', pFolderPrefix => 'C2D/UC_DISSEM/UC_NMA_DISSEM/'); + **/ + PROCEDURE DELETE_FOLDER_CONTENTS( + pBucketArea IN VARCHAR2, + pFolderPrefix IN VARCHAR2 + ); + + + + + /** + * @name PROCESS_SOURCE_FILE + * @desc It process file provided by pSourceFileReceivedName parameter. + * Ubmrella procedure that calls: + * - REGISTER_SOURCE_FILE_RECEIVED; + * - CREATE_EXTERNAL_TABLE; + * - VALIDATE_SOURCE_FILE_RECEIVED; + * - DROP_EXTERNAL_TABLE; + * - MOVE_FILE; + * @example exec FILE_MANAGER.PROCESS_SOURCE_FILE(pSourceFileReceivedName => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv'); + **/ + PROCEDURE PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + ; + + + + /** + * @name PROCESS_SOURCE_FILE + * @desc It process file provided by pSourceFileReceivedName parameter and return processing result value. + * It returns (success/failure) => 0 / -(value). + * Ubmrella function that calls PROCESS_SOURCE_FILE procedure. + * @example + * declare + * vResult PLS_INTEGER; + * begin + * vResult := CT_MRDS.FILE_MANAGER.PROCESS_SOURCE_FILE(PSOURCEFILERECEIVEDNAME => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv'); + * DBMS_OUTPUT.PUT_LINE('vResult = ' || vResult); + * end; + * @ex_rslt 0 + * -20021 + **/ + FUNCTION PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + RETURN PLS_INTEGER; + + + + /** + * @name GET_DATE_FORMAT + * @desc Returns date format for specified template table name and column name. + * Date is taken from configuration A_COLUMN_DATE_FORMAT table. + * @example select FILE_MANAGER.GET_DATE_FORMAT( + * pTemplateTableName => 'STANDING_FACILITIES_HEADER', + * pColumnName => 'SNAPSHOT_DATE') + * from dual; + * @ex_rslt DD/MM/YYYY HH24:MI:SS + **/ + FUNCTION GET_DATE_FORMAT( + pTemplateTableName IN VARCHAR2, + pColumnName IN VARCHAR2 + ) RETURN VARCHAR2; + + + + /** + * @name GENERATE_EXTERNAL_TABLE_PARAMS + * @desc It builds two strings: pColumnList and pFieldList for specified Template Table name, by parameter: pTemplateTableName. + * @example + * declare + * vColumnList CLOB; + * vFieldList CLOB; + * begin + * FILE_MANAGER.GENERATE_EXTERNAL_TABLE_PARAMS ( + * pTemplateTableName => 'CT_ET_TEMPLATES.LM_STANDING_FACILITIES_HEADER' + * ,pColumnList => vColumnList + * ,pFieldList => vFieldList + * ); + * DBMS_OUTPUT.PUT_LINE('vColumnList = '||vColumnList); + * DBMS_OUTPUT.PUT_LINE('vFieldList = '||vFieldList); + * end; + * / + **/ + PROCEDURE GENERATE_EXTERNAL_TABLE_PARAMS ( + pTemplateTableName IN VARCHAR2, + pColumnList OUT CLOB, + pFieldList OUT CLOB + ); + + + + + + /** + * @name ADD_SOURCE + * @desc Insert a new record to A_SOURCE table. + * pSourceKey is a PRIMARY KEY value. + **/ + PROCEDURE ADD_SOURCE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE, + pSourceName IN CT_MRDS.A_SOURCE.SOURCE_NAME%TYPE + ); + + + + /** + * @name DELETE_SOURCE_CASCADE + * @desc Safely deletes a SOURCE specified by pSourceKey parameter from A_SOURCE table and all dependent tables: + * - A_SOURCE_FILE_CONFIG + * - A_SOURCE_FILE_RECEIVED + * - A_COLUMN_DATE_FORMAT (only if template table is not shared with other source systems) + * The procedure checks if template tables are shared before deleting date format configurations. + * If a template table is used by multiple source systems, date formats are preserved. + * @example CALL CT_MRDS.FILE_MANAGER.DELETE_SOURCE_CASCADE(pSourceKey => 'TEST_SYS'); + **/ + PROCEDURE DELETE_SOURCE_CASCADE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE + ); + + + + /** + * @name GET_CONTAINER_SOURCE_FILE_CONFIG_KEY + * @desc For specified parameter pSourceFileId (A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID) + * it returns A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY for related CONTAINER record. + * @example select FILE_MANAGER.GET_CONTAINER_SOURCE_FILE_CONFIG_KEY( + * pSourceFileId => 'UC_DISSEM') + * from dual; + * @ex_rslt 126 + **/ + FUNCTION GET_CONTAINER_SOURCE_FILE_CONFIG_KEY ( + pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ) RETURN PLS_INTEGER; + + + + /** + * @name GET_SOURCE_FILE_CONFIG_KEY + * @desc For specified input parameters, + * it returns A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY. + * @example select FILE_MANAGER.GET_SOURCE_FILE_CONFIG_KEY ( + * pSourceFileType => 'INPUT' + * ,pSourceFileId => 'UC_DISSEM' + * ,pTableId => 'UC_NMA_DISSEM') + * from dual; + * @ex_rslt 126 + **/ + FUNCTION GET_SOURCE_FILE_CONFIG_KEY ( + pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE DEFAULT 'INPUT' + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE + ) RETURN PLS_INTEGER; + + + + /** + * @name ADD_SOURCE_FILE_CONFIG + * @desc Insert a new record to A_SOURCE_FILE_CONFIG table. + * MARS-1049: Added pEncoding parameter for CSV character set specification. + * MARS-1409: Added pIsWorkflowSuccessRequired parameter. + * MARS-828: Added pIsArchiveEnabled, pIsKeptInTrash, pArchivalStrategy, pMinimumAgeMonths. + * @param pEncoding - Character set encoding for CSV files (e.g., 'UTF8', 'WE8MSWIN1252', 'EE8ISO8859P2') + * If NULL, no CHARACTERSET clause is added to external table definitions + * @param pIsWorkflowSuccessRequired - 'Y' (default) = archivization requires WORKFLOW_SUCCESSFUL='Y' (standard DBT flow) + * 'N' = archive regardless of workflow status (bypass for manual/non-DBT sources) + * @param pIsArchiveEnabled - 'Y' = enable automatic archivization for this config; 'N' (default) = disabled + * @param pIsKeptInTrash - 'Y' = move files to trash before purge; 'N' (default) = purge directly + * @param pArchivalStrategy - Archival strategy: 'MINIMUM_AGE_MONTHS' or NULL + * @param pMinimumAgeMonths - Minimum age in months before file eligible for archivization (used with MINIMUM_AGE_MONTHS strategy) + * @example CALL CT_MRDS.FILE_MANAGER.ADD_SOURCE_FILE_CONFIG( + * pSourceKey => 'C2D', pSourceFileType => 'INPUT', + * pSourceFileId => 'UC_DISSEM', pTableId => 'METADATA_LOADS', + * pTemplateTableName => 'CT_ET_TEMPLATES.C2D_A_UC_DISSEM_METADATA_LOADS', + * pEncoding => 'UTF8', pIsWorkflowSuccessRequired => 'Y', + * pIsArchiveEnabled => 'Y', pIsKeptInTrash => 'N', + * pArchivalStrategy => 'MINIMUM_AGE_MONTHS', pMinimumAgeMonths => 3 + * ); + **/ + PROCEDURE ADD_SOURCE_FILE_CONFIG ( + pSourceKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_KEY%TYPE + ,pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pSourceFileDesc IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_DESC%TYPE + ,pSourceFileNamePattern IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_NAME_PATTERN%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE DEFAULT NULL + ,pTemplateTableName IN CT_MRDS.A_SOURCE_FILE_CONFIG.TEMPLATE_TABLE_NAME%TYPE DEFAULT NULL + ,pContainerFileKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.CONTAINER_FILE_KEY%TYPE DEFAULT NULL + ,pEncoding IN CT_MRDS.A_SOURCE_FILE_CONFIG.ENCODING%TYPE DEFAULT NULL -- MARS-1049 + ,pIsWorkflowSuccessRequired IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_WORKFLOW_SUCCESS_REQUIRED%TYPE DEFAULT 'Y' -- MARS-1409 + ,pIsArchiveEnabled IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_ARCHIVE_ENABLED%TYPE DEFAULT 'N' -- MARS-828 + ,pIsKeptInTrash IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_KEPT_IN_TRASH%TYPE DEFAULT 'Y' -- MARS-828 + ,pArchivalStrategy IN CT_MRDS.A_SOURCE_FILE_CONFIG.ARCHIVAL_STRATEGY%TYPE DEFAULT 'THRESHOLD_BASED' -- MARS-828 + ,pMinimumAgeMonths IN CT_MRDS.A_SOURCE_FILE_CONFIG.MINIMUM_AGE_MONTHS%TYPE DEFAULT 0 -- MARS-828 + ); + + + + /** + * @name ADD_COLUMN_DATE_FORMAT + * @desc Insert a new record to A_COLUMN_DATE_FORMAT table. + **/ + PROCEDURE ADD_COLUMN_DATE_FORMAT ( + pTemplateTableName IN CT_MRDS.A_COLUMN_DATE_FORMAT.TEMPLATE_TABLE_NAME%TYPE + ,pColumnName IN CT_MRDS.A_COLUMN_DATE_FORMAT.COLUMN_NAME%TYPE + ,pDateFormat IN CT_MRDS.A_COLUMN_DATE_FORMAT.DATE_FORMAT%TYPE + ); + + + + /** + * @name GET_BUCKET_URI + * @desc Function used to get string with bucket http url. + * Possible input values for pBucketArea are: 'INBOX', 'ODS', 'DATA', 'ARCHIVE' + * @example select FILE_MANAGER.GET_BUCKET_URI(pBucketArea => 'ODS') from dual; + * @ex_rslt https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/frcnomajoc7v/b/mrds_data_tst/o/ + **/ + FUNCTION GET_BUCKET_URI(pBucketArea VARCHAR2) + RETURN VARCHAR2; + + + + /** + * @name GET_DET_SOURCE_FILE_CONFIG_INFO + * @desc Function returns details about A_SOURCE_FILE_CONFIG record + * for specified pSourceFileConfigKey (A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY). + * If pIncludeContainerInfo is <> 0 it returns additional info about related Container config record (A_SOURCE_FILE_CONFIG) + * If pIncludeColumnFormatInfo is <> 0 it returns additional info about related ColumnFormat config record (A_COLUMN_DATE_FORMAT) + * @example select FILE_MANAGER.GET_DET_SOURCE_FILE_CONFIG_INFO ( + * pSourceFileConfigKey => 128 + * ,pIncludeContainerInfo => 1 + * ,pIncludeColumnFormatInfo => 1 + * ) from dual; + * @ex_rslt + * Details about File Configuration: + * -------------------------------- + * A_SOURCE_FILE_CONFIG_KEY = 128 + * A_SOURCE_KEY = C2D + * ... + * -------------------------------- + * + * Details about related Container Config: + * -------------------------------- + * A_SOURCE_FILE_CONFIG_KEY = 126 + * A_SOURCE_KEY = C2D + * ... + * -------------------------------- + * + * Column Date Format config entries: + * -------------------------------- + * TEMPLATE_TABLE_NAME = CT_ET_TEMPLATES.C2D_UC_MA_DISSEM + * ... + * -------------------------------- + **/ + FUNCTION GET_DET_SOURCE_FILE_CONFIG_INFO ( + pSourceFileConfigKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY%TYPE + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2; + + + + /** + * @name GET_DET_SOURCE_FILE_RECEIVED_INFO + * @desc Function returns details about A_SOURCE_FILE_RECEIVED record + * for specified pSourceFileReceivedKey (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY). + * If pIncludeConfigInfo is <> 0 it returns additional info about related Container config record (A_SOURCE_FILE_CONFIG) + * If pIncludeContainerInfo is <> 0 it returns additional info about related Container config record (A_SOURCE_FILE_CONFIG) + * If pIncludeColumnFormatInfo is <> 0 it returns additional info about related ColumnFormat config record (A_COLUMN_DATE_FORMAT) + * @example select FILE_MANAGER.GET_DET_SOURCE_FILE_RECEIVED_INFO ( + * pSourceFileReceivedKey => 377 + * ,pIncludeConfigInfo => 1 + * ,pIncludeContainerInfo => 1 + * ,pIncludeColumnFormatInfo => 1 + * ) from dual; + * + **/ + FUNCTION GET_DET_SOURCE_FILE_RECEIVED_INFO ( + pSourceFileReceivedKey IN CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE + ,pIncludeConfigInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2; + + + + /** + * @name GET_DET_USER_LOAD_OPERATIONS + * @desc Function returns details from USER_LOAD_OPERATIONS table + * for specified pOperationId. + * @example select FILE_MANAGER.GET_DET_USER_LOAD_OPERATIONS (pOperationId => 3608) from dual; + * @ex_rslt + * Details about USER_LOAD_OPERATIONS where ID = 3608 + * -------------------------------- + * ID = 3608 + * TYPE = VALIDATE + * SID = 31260 + * SERIAL# = 52915 + * START_TIME = 2025-05-20 10.08.24.436983 EUROPE/BELGRADE + * UPDATE_TIME = 2025-05-20 10.08.24.458643 EUROPE/BELGRADE + * STATUS = FAILED + * OWNER_NAME = CT_MRDS + * TABLE_NAME = STANDING_FACILITIES_HEADER + * PARTITION_NAME = + * SUBPARTITION_NAME = + * FILE_URI_LIST = + * ROWS_LOADED = + * LOGFILE_TABLE = VALIDATE$3608_LOG + * BADFILE_TABLE = VALIDATE$3608_BAD + * STATUS_TABLE = + * TEMPEXT_TABLE = + * CREDENTIAL_NAME = + * EXPIRATION_TIME = 2025-05-22 10.08.24.436983000 EUROPE/BELGRADE + * -------------------------------- + **/ + FUNCTION GET_DET_USER_LOAD_OPERATIONS ( + pOperationId PLS_INTEGER + ) RETURN VARCHAR2; + + /** + * @name ANALYZE_VALIDATION_ERRORS + * @desc Wrapper function that analyzes validation errors for a source file using its received key. + * Automatically derives template schema, table name, CSV URI and validation log table + * from file metadata and calls ENV_MANAGER.ANALYZE_VALIDATION_ERRORS. + * @example SELECT FILE_MANAGER.ANALYZE_VALIDATION_ERRORS(63) FROM DUAL; + * @ex_rslt Detailed validation analysis report with column mismatches and solutions + **/ + FUNCTION ANALYZE_VALIDATION_ERRORS( + pSourceFileReceivedKey IN NUMBER + ) RETURN VARCHAR2; + + --------------------------------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS + --------------------------------------------------------------------------------------------------------------------------- + + /** + * @name GET_VERSION + * @desc Returns the current version number of the FILE_MANAGER package. + * Uses semantic versioning format (MAJOR.MINOR.PATCH). + * @example SELECT FILE_MANAGER.GET_VERSION() FROM DUAL; + * @ex_rslt 3.2.0 + **/ + FUNCTION GET_VERSION RETURN VARCHAR2; + + /** + * @name GET_BUILD_INFO + * @desc Returns comprehensive build information including version, build date, and author. + * Uses centralized ENV_MANAGER.GET_PACKAGE_VERSION_INFO function. + * @example SELECT FILE_MANAGER.GET_BUILD_INFO() FROM DUAL; + * @ex_rslt Package: FILE_MANAGER + * Version: 3.2.0 + * Build Date: 2025-10-22 16:30:00 + * Author: Grzegorz Michalski + **/ + FUNCTION GET_BUILD_INFO RETURN VARCHAR2; + + /** + * @name GET_VERSION_HISTORY + * @desc Returns complete version history with all releases and changes. + * Uses centralized ENV_MANAGER.FORMAT_VERSION_HISTORY function. + * @example SELECT FILE_MANAGER.GET_VERSION_HISTORY() FROM DUAL; + * @ex_rslt FILE_MANAGER Version History: + * 3.2.0 (2025-10-22): Added package versioning system... + **/ + FUNCTION GET_VERSION_HISTORY RETURN VARCHAR2; + +END; + +/ + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER_ODS.pkb b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER_ODS.pkb new file mode 100644 index 0000000..b147983 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER_ODS.pkb @@ -0,0 +1,232 @@ +create or replace PACKAGE BODY ODS.FILE_MANAGER_ODS +AS + + /** + * CREATE_EXTERNAL_TABLE - Wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE + */ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT CT_MRDS.ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL + ) + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + -- Log the start of the procedure + vParameters := CT_MRDS.ENV_MANAGER.FORMAT_PARAMETERS( + SYS.ODCIVARCHAR2LIST( + 'pTableName => ''' || pTableName || '''', + 'pTemplateTableName => ''' || pTemplateTableName || '''', + 'pPrefix => ''' || pPrefix || '''', + 'pBucketUri => ''' || pBucketUri || '''', + 'pFileName => ''' || NVL(pFileName, 'NULL') || '''', + 'pDelimiter => ''' || pDelimiter || '''', + 'pEncoding => ''' || NVL(pEncoding, 'NULL') || '''' + ) + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('Start FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE', 'INFO', vParameters); + + -- Call the original CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE procedure + -- This ensures all logic remains centralized in the original package + -- and ODS wrapper simply delegates execution with DEFINER rights + CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE( + pTableName => pTableName, + pTemplateTableName => pTemplateTableName, + pPrefix => pPrefix, + pBucketUri => pBucketUri, + pFileName => pFileName, + pDelimiter => pDelimiter, + pEncoding => pEncoding + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('End FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE', 'INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT( + 'Error in ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE: ' || SQLERRM, + 'ERROR', + vParameters + ); + RAISE; + END CREATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + /** + * CREATE_EXTERNAL_TABLES_SET - Wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_SET + */ + PROCEDURE CREATE_EXTERNAL_TABLES_SET ( + pSourceFileConfigKey IN NUMBER, + pRecreate IN BOOLEAN DEFAULT FALSE, + pRestoreGrants IN BOOLEAN DEFAULT TRUE, + pArea IN VARCHAR2 DEFAULT 'ALL' + ) + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vRecreateStr VARCHAR2(10); + vRestoreGrantsStr VARCHAR2(10); + BEGIN + -- Convert BOOLEAN to VARCHAR2 for logging + vRecreateStr := CASE WHEN pRecreate THEN 'TRUE' ELSE 'FALSE' END; + vRestoreGrantsStr := CASE WHEN pRestoreGrants THEN 'TRUE' ELSE 'FALSE' END; + + -- Log the start of the procedure + vParameters := CT_MRDS.ENV_MANAGER.FORMAT_PARAMETERS( + SYS.ODCIVARCHAR2LIST( + 'pSourceFileConfigKey => ' || pSourceFileConfigKey, + 'pRecreate => ' || vRecreateStr, + 'pRestoreGrants => ' || vRestoreGrantsStr, + 'pArea => ''' || pArea || '''' + ) + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('Start FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_SET', 'INFO', vParameters); + + -- Call the original CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_SET procedure + -- This ensures all logic remains centralized in the original package + -- and ODS wrapper simply delegates execution with DEFINER rights + CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_SET( + pSourceFileConfigKey => pSourceFileConfigKey, + pRecreate => pRecreate, + pRestoreGrants => pRestoreGrants, + pArea => pArea + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('End FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_SET', 'INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT( + 'Error in ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_SET: ' || SQLERRM, + 'ERROR', + vParameters + ); + RAISE; + END CREATE_EXTERNAL_TABLES_SET; + + ---------------------------------------------------------------------------------------------------- + + /** + * CREATE_EXTERNAL_TABLES_BATCH - Wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_BATCH + */ + PROCEDURE CREATE_EXTERNAL_TABLES_BATCH ( + pSourceKey IN VARCHAR2 DEFAULT NULL, + pSourceFileId IN VARCHAR2 DEFAULT NULL, + pTableId IN VARCHAR2 DEFAULT NULL, + pRecreate IN BOOLEAN DEFAULT FALSE, + pRestoreGrants IN BOOLEAN DEFAULT TRUE, + pArea IN VARCHAR2 DEFAULT 'ALL' + ) + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vRecreateStr VARCHAR2(10); + vRestoreGrantsStr VARCHAR2(10); + BEGIN + -- Convert BOOLEAN to VARCHAR2 for logging + vRecreateStr := CASE WHEN pRecreate THEN 'TRUE' ELSE 'FALSE' END; + vRestoreGrantsStr := CASE WHEN pRestoreGrants THEN 'TRUE' ELSE 'FALSE' END; + + -- Log the start of the procedure + vParameters := CT_MRDS.ENV_MANAGER.FORMAT_PARAMETERS( + SYS.ODCIVARCHAR2LIST( + 'pSourceKey => ''' || NVL(pSourceKey, 'NULL') || '''', + 'pSourceFileId => ''' || NVL(pSourceFileId, 'NULL') || '''', + 'pTableId => ''' || NVL(pTableId, 'NULL') || '''', + 'pRecreate => ' || vRecreateStr, + 'pRestoreGrants => ' || vRestoreGrantsStr, + 'pArea => ''' || pArea || '''' + ) + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('Start FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_BATCH', 'INFO', vParameters); + + -- Call the original CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_BATCH procedure + -- This ensures all logic remains centralized in the original package + -- and ODS wrapper simply delegates execution with DEFINER rights + CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_BATCH( + pSourceKey => pSourceKey, + pSourceFileId => pSourceFileId, + pTableId => pTableId, + pRecreate => pRecreate, + pRestoreGrants => pRestoreGrants, + pArea => pArea + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('End FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_BATCH', 'INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT( + 'Error in ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_BATCH: ' || SQLERRM, + 'ERROR', + vParameters + ); + RAISE; + END CREATE_EXTERNAL_TABLES_BATCH; + + ---------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS IMPLEMENTATION + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_VERSION; + END GET_VERSION; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUILD_DATE + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_BUILD_DATE; + END GET_BUILD_DATE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_AUTHOR + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_AUTHOR; + END GET_AUTHOR; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUILD_INFO + RETURN VARCHAR2 + IS + BEGIN + RETURN CT_MRDS.ENV_MANAGER.GET_PACKAGE_VERSION_INFO( + pPackageName => 'FILE_MANAGER_ODS', + pVersion => PACKAGE_VERSION, + pBuildDate => PACKAGE_BUILD_DATE, + pAuthor => PACKAGE_AUTHOR + ); + END GET_BUILD_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION_HISTORY + RETURN VARCHAR2 + IS + BEGIN + RETURN CT_MRDS.ENV_MANAGER.FORMAT_VERSION_HISTORY( + pPackageName => 'FILE_MANAGER_ODS', + pVersionHistory => VERSION_HISTORY + ); + END GET_VERSION_HISTORY; + + ---------------------------------------------------------------------------------------------------- + +END FILE_MANAGER_ODS; + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER_ODS.pkg b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER_ODS.pkg new file mode 100644 index 0000000..4ce6264 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/new_version/FILE_MANAGER_ODS.pkg @@ -0,0 +1,189 @@ +create or replace PACKAGE ODS.FILE_MANAGER_ODS +AUTHID DEFINER +AS + /** + * FILE_MANAGER_ODS - Wrapper package for CT_MRDS.FILE_MANAGER + * + * This package serves as a wrapper for CT_MRDS.FILE_MANAGER procedures. + * The key difference is that this package uses AUTHID DEFINER instead of + * AUTHID CURRENT_USER, which means all objects will be created in the ODS + * schema regardless of which user executes the procedures. + * + * This solves the execution context issue where users need to connect as + * ODS user to use CT_MRDS.FILE_MANAGER procedures. + * + * MARS-1049: Added pEncoding parameter support for CSV character set specification + */ + + -- Package Version Information (Semantic Versioning: MAJOR.MINOR.PATCH) + PACKAGE_VERSION CONSTANT VARCHAR2(10) := '2.4.0'; + PACKAGE_BUILD_DATE CONSTANT VARCHAR2(20) := '2026-02-18 16:00:00'; + PACKAGE_AUTHOR CONSTANT VARCHAR2(100) := 'Grzegorz Michalski'; + + -- Version History (Latest changes first) + VERSION_HISTORY CONSTANT VARCHAR2(4000) := + '2.4.0 (2026-03-26): MARS-1443-PREHOOK - Added pArea parameter for selective table creation (INBOX/ODS/ARCHIVE/ALL)' || CHR(13)||CHR(10) || + '2.3.0 (2026-03-26): MARS-1443-PREHOOK - Added pRestoreGrants parameter support for grant preservation during table recreate' || CHR(13)||CHR(10) || + '2.2.0 (2026-03-26): MARS-1443-PREHOOK - Added CREATE_EXTERNAL_TABLES_SET and CREATE_EXTERNAL_TABLES_BATCH wrappers for batch external table creation' || CHR(13)||CHR(10) || + '2.1.0 (2025-11-24): MARS-1049 - Added pEncoding parameter support for CSV character set specification' || CHR(13)||CHR(10) || + '2.0.0 (2025-10-22): Added package versioning system using centralized ENV_MANAGER functions' || CHR(13)||CHR(10) || + '1.5.0 (2025-10-12): Enhanced external table creation with official path patterns support' || CHR(13)||CHR(10) || + '1.0.0 (2025-09-20): Initial release with AUTHID DEFINER wrapper for external table creation'; + + /** + * @name CREATE_EXTERNAL_TABLE + * @desc Creates external tables that can read data from Oracle Cloud Storage. + * This is a wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE + * but uses DEFINER rights to ensure objects are created in ODS schema. + * + * MARS-1049: Added support for CSV character set encoding specification + * @param pTableName - Name of the external table to create + * @param pTemplateTableName - Template table defining the structure + * @param pPrefix - Storage path prefix in Oracle Cloud Storage + * @param pBucketUri - URI of the target bucket (default: ENV_MANAGER.gvInboxBucketUri) + * @param pFileName - Specific file name (optional) + * @param pDelimiter - Field delimiter (default: ',') + * @param pEncoding - Character set encoding for CSV files (e.g., 'UTF-8', 'WE8MSWIN1252', 'EE8ISO8859P2') + * If NULL, no CHARACTERSET clause is added to external table definition + * @example EXEC ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE( + * 'C2D_A_UC_DISSEM_METADATA_LOADS_INBOX', + * 'CT_ET_TEMPLATES.C2D_A_UC_DISSEM_METADATA_LOADS', + * 'INBOX/C2D/UC_DISSEM/A_UC_DISSEM_METADATA_LOADS', + * CT_MRDS.ENV_MANAGER.gvInboxBucketUri, + * NULL, ',', 'UTF-8' + * ); + */ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT CT_MRDS.ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL + ); + + /** + * @name CREATE_EXTERNAL_TABLES_SET + * @desc Creates a complete set of external tables for a single configuration from A_SOURCE_FILE_CONFIG table. + * Automatically generates table names and paths following official path patterns. + * Wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_SET with DEFINER rights. + * If pRestoreGrants is TRUE, saves and restores table grants during recreate operation. + * The pArea parameter allows selective table creation. + * @param pSourceFileConfigKey - Primary key from A_SOURCE_FILE_CONFIG table + * @param pRecreate - If TRUE, drops existing tables before creating new ones; if FALSE, fails if tables exist + * @param pRestoreGrants - If TRUE, saves grants before DROP and restores after CREATE (only when pRecreate=TRUE) + * Uses DBA_TAB_PRIVS - requires SELECT ANY DICTIONARY or SELECT ON DBA_TAB_PRIVS privilege + * @param pArea - Specifies which tables to create: 'INBOX', 'ODS', 'ARCHIVE', or 'ALL' (default) + * @example -- Create only INBOX table + * EXEC ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_SET( + * pSourceFileConfigKey => 123, + * pArea => 'INBOX' + * ); + * + * -- Create all tables with grant preservation + * EXEC ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_SET( + * pSourceFileConfigKey => 123, + * pRecreate => TRUE, + * pRestoreGrants => TRUE, + * pArea => 'ALL' + * ); + * @ex_rslt Creates external table(s) in ODS schema based on pArea parameter + */ + PROCEDURE CREATE_EXTERNAL_TABLES_SET ( + pSourceFileConfigKey IN NUMBER, + pRecreate IN BOOLEAN DEFAULT FALSE, + pRestoreGrants IN BOOLEAN DEFAULT TRUE, + pArea IN VARCHAR2 DEFAULT 'ALL' + ); + + /** + * @name CREATE_EXTERNAL_TABLES_BATCH + * @desc Creates external table sets for multiple configurations based on filter criteria. + * Processes only INPUT type files from A_SOURCE_FILE_CONFIG. Creates tables based on pArea parameter. + * Wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLES_BATCH with DEFINER rights. + * If pRestoreGrants is TRUE, saves and restores table grants during recreate operations. + * @param pSourceKey - Filter by A_SOURCE_KEY (NULL = all sources) + * @param pSourceFileId - Filter by SOURCE_FILE_ID (NULL = all file types) + * @param pTableId - Filter by TABLE_ID (NULL = all tables) + * @param pRecreate - If TRUE, drops and recreates existing tables; if FALSE, skips if tables exist + * @param pRestoreGrants - If TRUE, saves grants before DROP and restores after CREATE (only when pRecreate=TRUE) + * Uses DBA_TAB_PRIVS - requires SELECT ANY DICTIONARY or SELECT ON DBA_TAB_PRIVS privilege + * @param pArea - Specifies which tables to create: 'INBOX', 'ODS', 'ARCHIVE', or 'ALL' (default) + * @example -- Create only INBOX tables for C2D source + * EXEC ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_BATCH( + * pSourceKey => 'C2D', + * pArea => 'INBOX' + * ); + * + * -- Create all tables with grant preservation + * EXEC ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLES_BATCH( + * pRecreate => TRUE, + * pRestoreGrants => TRUE, + * pArea => 'ALL' + * ); + * @ex_rslt Returns summary: Total: 10, Processed: 9, Failed: 1 + */ + PROCEDURE CREATE_EXTERNAL_TABLES_BATCH ( + pSourceKey IN VARCHAR2 DEFAULT NULL, + pSourceFileId IN VARCHAR2 DEFAULT NULL, + pTableId IN VARCHAR2 DEFAULT NULL, + pRecreate IN BOOLEAN DEFAULT FALSE, + pRestoreGrants IN BOOLEAN DEFAULT TRUE, + pArea IN VARCHAR2 DEFAULT 'ALL' + ); + + --------------------------------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS + --------------------------------------------------------------------------------------------------------------------------- + + /** + * @name GET_VERSION + * @desc Returns the current version number of the FILE_MANAGER_ODS package. + * Uses semantic versioning format (MAJOR.MINOR.PATCH). + * @example SELECT FILE_MANAGER_ODS.GET_VERSION() FROM DUAL; + * @ex_rslt 2.1.0 + **/ + FUNCTION GET_VERSION RETURN VARCHAR2; + + /** + * @name GET_BUILD_DATE + * @desc Returns the build date of the FILE_MANAGER_ODS package. + * @return VARCHAR2 - Build date in format 'YYYY-MM-DD HH24:MI:SS' + * @example SELECT ODS.FILE_MANAGER_ODS.GET_BUILD_DATE() FROM DUAL; + */ + FUNCTION GET_BUILD_DATE RETURN VARCHAR2; + + /** + * @name GET_AUTHOR + * @desc Returns the author of the FILE_MANAGER_ODS package. + * @return VARCHAR2 - Package author name + * @example SELECT ODS.FILE_MANAGER_ODS.GET_AUTHOR() FROM DUAL; + */ + FUNCTION GET_AUTHOR RETURN VARCHAR2; + + /** + * @name GET_BUILD_INFO + * @desc Returns comprehensive build information including version, build date, and author. + * Uses centralized ENV_MANAGER.GET_PACKAGE_VERSION_INFO function. + * @example SELECT FILE_MANAGER_ODS.GET_BUILD_INFO() FROM DUAL; + * @ex_rslt Package: FILE_MANAGER_ODS + * Version: 2.2.0 + * Build Date: 2025-11-27 15:00:00 + * Author: Grzegorz Michalski + **/ + FUNCTION GET_BUILD_INFO RETURN VARCHAR2; + + /** + * @name GET_VERSION_HISTORY + * @desc Returns complete version history with all releases and changes. + * Uses centralized ENV_MANAGER.FORMAT_VERSION_HISTORY function. + * @example SELECT FILE_MANAGER_ODS.GET_VERSION_HISTORY() FROM DUAL; + * @ex_rslt FILE_MANAGER_ODS Version History: + * 2.0.0 (2025-10-22): Added package versioning system... + **/ + FUNCTION GET_VERSION_HISTORY RETURN VARCHAR2; + +END FILE_MANAGER_ODS; + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_mars1443_prehook.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_mars1443_prehook.sql new file mode 100644 index 0000000..a9b3a42 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_mars1443_prehook.sql @@ -0,0 +1,91 @@ +-- =================================================================== +-- MARS-1443-PREHOOK ROLLBACK SCRIPT +-- =================================================================== +-- Purpose: Rollback pre-hook changes for MARS-1443 +-- Author: Grzegorz Michalski +-- Date: 2026-03-26 + +-- Dynamic spool file generation (using SYS_CONTEXT - no DBA privileges required) +-- Log files are automatically created in log/ subdirectory +-- IMPORTANT: Ensure log/ directory exists before SPOOL (use host mkdir) +host mkdir log 2>nul + +var filename VARCHAR2(100) +BEGIN + :filename := 'log/ROLLBACK_MARS_1443_PREHOOK_' || SYS_CONTEXT('USERENV', 'CON_NAME') || '_' || TO_CHAR(SYSDATE,'YYYYMMDD_HH24MISS') || '.log'; +END; +/ +column filename new_value _filename +select :filename filename from dual; +spool &_filename + +SET ECHO OFF +SET TIMING ON +SET SERVEROUTPUT ON SIZE UNLIMITED +SET PAUSE OFF + +PROMPT ========================================================================= +PROMPT MARS-1443-PREHOOK: Rollback +PROMPT ========================================================================= +PROMPT WARNING: This will reverse all changes from MARS-1443-PREHOOK installation! +PROMPT ========================================================================= + +-- Confirm rollback with user +ACCEPT continue CHAR PROMPT 'Type YES to continue with rollback, or Ctrl+C to abort: ' +WHENEVER SQLERROR EXIT SQL.SQLCODE +BEGIN + IF '&continue' IS NULL OR TRIM('&continue') IS NULL OR UPPER(TRIM('&continue')) != 'YES' THEN + RAISE_APPLICATION_ERROR(-20001, 'Rollback aborted by user'); + END IF; +END; +/ +WHENEVER SQLERROR CONTINUE + +-- Execute rollback scripts in REVERSE order (94, 93, 92, 91) +PROMPT +PROMPT ========================================================================= +PROMPT Step 1: Rollback FILE_MANAGER Package Body to v3.3.0 +PROMPT ========================================================================= +@@92_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_BODY.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 2: Rollback FILE_MANAGER Package Specification to v3.3.0 +PROMPT ========================================================================= +@@91_MARS_1443_PREHOOK_rollback_CT_MRDS_FILE_MANAGER_SPEC.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 3: Rollback FILE_MANAGER_ODS Package Body to v2.1.0 +PROMPT ========================================================================= +@@93_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_BODY.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 4: Rollback FILE_MANAGER_ODS Package Specification to v2.1.0 +PROMPT ========================================================================= +@@94_MARS_1443_PREHOOK_rollback_ODS_FILE_MANAGER_ODS_SPEC.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 5: Track Rollback Version +PROMPT ========================================================================= +@@track_package_versions.sql + +PROMPT +PROMPT ========================================================================= +PROMPT Step 6: Verify All Tracked Packages +PROMPT ========================================================================= +@@verify_packages_version.sql + +PROMPT +PROMPT ========================================================================= +PROMPT MARS-1443-PREHOOK Rollback - COMPLETED +PROMPT ========================================================================= +PROMPT Check the log file for complete rollback details. +PROMPT Log file: log/ROLLBACK_MARS_1443_PREHOOK__.log +PROMPT ========================================================================= + +spool off + +quit; diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER.pkb b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER.pkb new file mode 100644 index 0000000..74b686e --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER.pkb @@ -0,0 +1,2097 @@ +create or replace PACKAGE BODY CT_MRDS.FILE_MANAGER +AS + + ---------------------------------------------------------------------------------------------------- + -- PRIVATE FUNCTION: NORMALIZE_DATE_FORMAT + ---------------------------------------------------------------------------------------------------- + /** + * Purpose: Normalize Oracle date format strings for use in external tables + * + * Problem: ISO 8601 formats like 'YYYY-MM-DDTHH24:MI:SS.FF3TZH:TZM' fail because + * literal character 'T' must be enclosed in double quotes for Oracle + * external table DATE column definitions. + * + * Solution: Detect unquoted 'T' separator and wrap it in double quotes + * + * Parameters: + * pDateFormat - Original date format from A_COLUMN_DATE_FORMAT table + * + * Returns: Normalized format with quoted 'T' if applicable + * + * Examples: + * Input: 'YYYY-MM-DDTHH24:MI:SS.FF3TZH:TZM' + * Output: 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM' + * + * Input: 'DD/MM/YYYY HH24:MI:SS' (no T) + * Output: 'DD/MM/YYYY HH24:MI:SS' (unchanged) + * + * Input: 'YYYY-MM-DD"T"HH24:MI:SS' (already quoted) + * Output: 'YYYY-MM-DD"T"HH24:MI:SS' (unchanged) + * + * Author: Grzegorz Michalski + * Date: 2025-11-27 + * Version: 1.0.0 (MARS-1046) + */ + FUNCTION NORMALIZE_DATE_FORMAT(pDateFormat VARCHAR2) RETURN VARCHAR2 IS + vNormalizedFormat VARCHAR2(500); + BEGIN + -- Return NULL if input is NULL + IF pDateFormat IS NULL THEN + RETURN NULL; + END IF; + + vNormalizedFormat := pDateFormat; + + -- Check if 'T' separator exists and is NOT already quoted + -- Pattern: [YMD]T[HM] (date component + T + time component) + IF INSTR(vNormalizedFormat, '"T"') = 0 AND + REGEXP_LIKE(vNormalizedFormat, '[YMD]T[HM]') THEN + + -- Wrap 'T' in double quotes using regex replace + -- Pattern matches: (date format char) + T + (time format char) + -- Replacement: \1 + "T" + \2 + vNormalizedFormat := REGEXP_REPLACE(vNormalizedFormat, '([YMD])T([HM])', '\1"T"\2'); + END IF; + + RETURN vNormalizedFormat; + + EXCEPTION + WHEN OTHERS THEN + -- If normalization fails, return original format (safety fallback) + RETURN pDateFormat; + END NORMALIZE_DATE_FORMAT; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_SOURCE_FILE_CONFIG(pFileUri IN VARCHAR2 DEFAULT NULL + , pSourceFileReceivedKey IN NUMBER DEFAULT NULL + , pSourceFileConfigKey IN NUMBER DEFAULT NULL) + RETURN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pFileUri => '''||nvl(pFileUri,'NULL')||'''' + ,'pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey),'NULL') + ,'pSourceFileConfigKey => '||nvl(to_char(pSourceFileConfigKey),'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + + BEGIN + IF pFileUri IS NOT NULL THEN + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE REGEXP_LIKE(pFileUri, A_SOURCE_KEY||'/'||SOURCE_FILE_ID||'/'||TABLE_ID||'/'||SOURCE_FILE_NAME_PATTERN); + ELSIF pSourceFileReceivedKey IS NOT NULL THEN + SELECT T.* + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG T, CT_MRDS.A_SOURCE_FILE_RECEIVED R + WHERE T.A_SOURCE_FILE_CONFIG_KEY = R.A_SOURCE_FILE_CONFIG_KEY + AND R.A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + ELSIF pSourceFileConfigKey IS NOT NULL THEN + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE A_SOURCE_FILE_CONFIG_KEY = pSourceFileConfigKey; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + END IF; + -- Set global package variable vgSourceFileConfigKey - used in error messages + vgSourceFileConfigKey := vSourceFileConfig.A_SOURCE_FILE_CONFIG_KEY; + EXCEPTION + WHEN ENV_MANAGER.ERR_EMPTY_FILEURI_AND_RECKEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH_FOR_FILEURI, ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI); + + WHEN TOO_MANY_ROWS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE, ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE); + + WHEN OTHERS THEN + -- Log complete error details including full stack trace and backtrace + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_UNKNOWN, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END; + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vSourceFileConfig; + + END GET_SOURCE_FILE_CONFIG; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey IN NUMBER DEFAULT NULL) + -- + -- Get source file received info + -- + RETURN tSourceFileReceived + IS + vSourceFileReceived tSourceFileReceived; + vBucket VARCHAR2(400); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey),'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + BEGIN + SELECT R.A_SOURCE_FILE_RECEIVED_KEY, R.A_SOURCE_FILE_CONFIG_KEY, + 'INBOX'||'/'||T.A_SOURCE_KEY||'/'||T.SOURCE_FILE_ID||'/'||T.TABLE_ID||'/' as SOURCE_FILE_PREFIX_INBOX, + 'ODS'||'/'||T.A_SOURCE_KEY||'/'||T.TABLE_ID||'/' as SOURCE_FILE_PREFIX_ODS, + 'QUARANTINE'||'/'||T.A_SOURCE_KEY||'/'||T.TABLE_ID||'/' as SOURCE_FILE_PREFIX_QUARANTINE, + 'ARCHIVE'||'/'||T.A_SOURCE_KEY||'/'||T.SOURCE_FILE_ID||'/' as SOURCE_FILE_PREFIX_ARCHIVE, + R.SOURCE_FILE_NAME, + R.RECEPTION_DATE, R.PROCESSING_STATUS, R.EXTERNAL_TABLE_NAME + INTO vSourceFileReceived + FROM CT_MRDS.A_SOURCE_FILE_RECEIVED R, CT_MRDS.A_SOURCE_FILE_CONFIG T + WHERE R.A_SOURCE_FILE_CONFIG_KEY = T.A_SOURCE_FILE_CONFIG_KEY + AND A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE); + WHEN TOO_MANY_ROWS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MULTI_CONFIG_FOR_RECEIVED_FILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTI_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_MULTI_CONFIG_FOR_RECEIVED_FILE); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END; + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + RETURN vSourceFileReceived; + + END GET_SOURCE_FILE_RECEIVED_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName IN VARCHAR2) + RETURN PLS_INTEGER + -- + -- Register a newly received source file A_SOURCE_FILE_RECEIVED + -- This overload automatically determines source file type from the file name + -- + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vSourceFileReceivedKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '''||nvl(pSourceFileReceivedName, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO',vParameters); + + vSourceFileConfig := GET_SOURCE_FILE_CONFIG(pSourceFileReceivedName); + vSourceFileReceivedKey := REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName, vSourceFileConfig); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + RETURN vSourceFileReceivedKey; + EXCEPTION + + WHEN ENV_MANAGER.ERR_EMPTY_FILEURI_AND_RECKEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + + WHEN ENV_MANAGER.ERR_NO_CONFIG_MATCH_FOR_FILEURI THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH_FOR_FILEURI, ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI); + + WHEN ENV_MANAGER.ERR_FILE_NOT_EXISTS_ON_CLOUD THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_EXISTS_ON_CLOUD, ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD); + + WHEN ENV_MANAGER.ERR_FILE_ALREADY_REGISTERED THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED, ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED); + + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_UNKNOWN, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END REGISTER_SOURCE_FILE_RECEIVED; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION REGISTER_SOURCE_FILE_RECEIVED( + pSourceFileReceivedName IN VARCHAR2 + ,pSourceFileConfig IN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE) + RETURN PLS_INTEGER + -- + -- Register a newly received source file A_SOURCE_FILE_RECEIVED + -- + IS + vExternalTableName VARCHAR2(200); + vDirName VARCHAR2(1000); + vFileName VARCHAR2(1000); + vChecksum A_SOURCE_FILE_RECEIVED.CHECKSUM%TYPE; + vCreated A_SOURCE_FILE_RECEIVED.CREATED%TYPE; + vBytes A_SOURCE_FILE_RECEIVED.BYTES%TYPE; + vSourceFileReceivedKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vRow CT_MRDS.A_SOURCE_FILE_RECEIVED%ROWTYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '''||nvl(pSourceFileReceivedName, 'NULL')||'''' + ,'pSourceFileConfig => '||'tSourceFileConfig record type')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + vDirName := REGEXP_SUBSTR(pSourceFileReceivedName, '(.*/)(.*)', 1, 1, NULL, 1); + -- Remove prefix from file name + vFileName := REGEXP_SUBSTR(pSourceFileReceivedName,'[^/]*$'); + + ENV_MANAGER.LOG_PROCESS_EVENT('gvCredentialName','DEBUG',ENV_MANAGER.gvCredentialName); + ENV_MANAGER.LOG_PROCESS_EVENT('gvInboxBucketUri','DEBUG',ENV_MANAGER.gvInboxBucketUri); + ENV_MANAGER.LOG_PROCESS_EVENT('vDirName','DEBUG',vDirName); + + SELECT + checksum, created, bytes + INTO + vChecksum, vCreated, vBytes + FROM DBMS_CLOUD.LIST_OBJECTS(ENV_MANAGER.gvCredentialName, + ENV_MANAGER.gvInboxBucketUri || vDirName + ) + WHERE object_name = vFileName + ; + vSourceFileReceivedKey := CT_MRDS.A_SOURCE_FILE_RECEIVED_KEY_SEQ.NEXTVAL; + vExternalTableName := REPLACE( + REGEXP_SUBSTR(pSourceFileConfig.TEMPLATE_TABLE_NAME||'_'||vSourceFileReceivedKey, + '\..*'), + '.',''); + + INSERT INTO CT_MRDS.A_SOURCE_FILE_RECEIVED + (A_SOURCE_FILE_RECEIVED_KEY, A_SOURCE_FILE_CONFIG_KEY, + SOURCE_FILE_NAME, RECEPTION_DATE, + PROCESSING_STATUS, EXTERNAL_TABLE_NAME, + CHECKSUM, CREATED, BYTES) + VALUES (vSourceFileReceivedKey, pSourceFileConfig.A_SOURCE_FILE_CONFIG_KEY, + vFileName, SYSDATE, + 'RECEIVED', vExternalTableName, + vChecksum, vCreated, vBytes); + COMMIT; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vSourceFileReceivedKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + vgMsgTmp := ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD + ||cgBL||' '||'File: '||ENV_MANAGER.gvInboxBucketUri || vDirName || vFileName; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_EXISTS_ON_CLOUD, vgMsgTmp); + + WHEN DUP_VAL_ON_INDEX THEN + select * into vRow + from CT_MRDS.A_SOURCE_FILE_RECEIVED + where CHECKSUM = vChecksum + and CREATED = vCreated + and BYTES = vBytes + ; + vgMsgTmp := ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED + ||cgBL||' '||'Details about existing File: ' + ||cgBL||' '||'-------------------------' + ||cgBL||' '||'A_SOURCE_FILE_RECEIVED_KEY = '||vRow.A_SOURCE_FILE_RECEIVED_KEY + ||cgBL||' '||'A_SOURCE_FILE_CONFIG_KEY = '||vRow.A_SOURCE_FILE_CONFIG_KEY + ||cgBL||' '||'SOURCE_FILE_NAME = '||vRow.SOURCE_FILE_NAME + ||cgBL||' '||'CHECKSUM = '||vRow.CHECKSUM + ||cgBL||' '||'CREATED = '||vRow.CREATED + ||cgBL||' '||'BYTES = '||vRow.BYTES + ||cgBL||' '||'RECEPTION_DATE = '||vRow.RECEPTION_DATE + ||cgBL||' '||'PROCESSING_STATUS = '||vRow.PROCESSING_STATUS + ||cgBL||' '||'EXTERNAL_TABLE_NAME = '||vRow.EXTERNAL_TABLE_NAME + ||cgBL||' '||'-------------------------' + ||cgBL||' '||'There cannot be two files with the same values for (CHECKSUM, CREATED, BYTES)' + ; + + +-- vChecksum, vCreated, vBytes + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED, vgMsgTmp); + + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END REGISTER_SOURCE_FILE_RECEIVED; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey IN PLS_INTEGER, pStatus IN VARCHAR2) + -- + -- Change status of file in the A_SOURCE_FILE_RECEIVED table + -- + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey),'NULL') + ,'pStatus => '''||nvl(pStatus, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + UPDATE CT_MRDS.A_SOURCE_FILE_RECEIVED + SET PROCESSING_STATUS=pStatus + WHERE A_SOURCE_FILE_RECEIVED_KEY=pSourceFileReceivedKey; + COMMIT; + ENV_MANAGER.LOG_PROCESS_EVENT('File status changed to '||pStatus,'INFO', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + END SET_SOURCE_FILE_RECEIVED_STATUS; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_EXTERNAL_TABLE_COLUMNS(pTargetTableTemplate IN VARCHAR2) + RETURN CLOB + -- + -- Create list of columns for DBMS_CLOUD.CREATE_EXTERNAL_TABLE from existing template table + -- + IS + vColumnList CLOB; + vTableName VARCHAR2(200); + vSchemaName VARCHAR2(200); + BEGIN + vSchemaName := REPLACE(REGEXP_SUBSTR(pTargetTableTemplate,'.*\.'),'.',''); + vTableName := REPLACE(REGEXP_SUBSTR(pTargetTableTemplate,'\..*'),'.',''); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'SQLTERMINATOR', True); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'COLLATION_CLAUSE', 'NEVER'); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'REF_CONSTRAINTS', False); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'STORAGE', False); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'TABLESPACE', False); + DBMS_METADATA.SET_TRANSFORM_PARAM(-1, 'SEGMENT_ATTRIBUTES', False); + vColumnList := RTRIM( + LTRIM( + REGEXP_SUBSTR(DBMS_METADATA.GET_DDL('TABLE', vTableName, vSchemaName),'\(.*\)',1,1,'mn'), + '('), + ')'); + RETURN vColumnList; + END GET_EXTERNAL_TABLE_COLUMNS; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL -- MARS-1049: NEW PARAMETER FOR FILE ENCODING + ) + -- + -- Create external table for a single source file to validate the file structure + -- + IS + vTableName VARCHAR2(200); + vColumnList CLOB; + vFieldList CLOB; + vFormat VARCHAR2(200); + + vPrefix VARCHAR2(200); + vFileName VARCHAR2(1000); + vFileExtension VARCHAR2(200); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pTableName => '''||nvl(pTableName, 'NULL')||'''' + ,'pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pPrefix => '''||nvl(pPrefix, 'NULL')||'''' + ,'pBucketUri => '''||nvl(pBucketUri, 'NULL')||'''' + ,'pFileName => '''||nvl(pFileName, 'NULL')||'''' + ,'pDelimiter => '''||nvl(pDelimiter, 'NULL')||'''' + ,'pEncoding => '''||nvl(pEncoding, 'NULL')||'''' -- MARS-1049: NOWY + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + -- Strip off leading and trailing slashes from prefix + vPrefix := TRIM(BOTH '/' FROM pPrefix); + + -- Generate column and field list from template table + GENERATE_EXTERNAL_TABLE_PARAMS (pTemplateTableName, vColumnList, vFieldList); + + --vFormat evaluation based on pBucketUri first, then pPrefix + -- Archive bucket should use parquet + implicit partitioning regardless of prefix + IF INSTR(pBucketUri, ENV_MANAGER.gvArchiveBucketName)>0 THEN + vFormat := '{"type": "parquet" + ,"implicit_partition_type": "hive" + ,"implicit_partition_columns":["PARTITION_YEAR","PARTITION_MONTH"]}'; + vColumnList := vColumnList||cgBL||' , "PARTITION_YEAR" varchar2(4)'||cgBL||', "PARTITION_MONTH" varchar2(2)'; + vFieldList := NULL; + vFileExtension := '.parquet'; + + -- For INBOX, ODS, and other ARCHIVE prefixes (not in archive bucket) use CSV + ELSIF SUBSTR(pPrefix,1,5) = 'INBOX' OR SUBSTR(pPrefix,1,3) = 'ODS' + OR SUBSTR(pPrefix,1,7) = 'ARCHIVE' + THEN + -- MARS-1049: Create format with encoding if specified + IF pDelimiter = '|' THEN + IF pEncoding IS NOT NULL AND LENGTH(TRIM(pEncoding)) > 0 THEN + vFormat := json_object( + 'delimiter' VALUE '|', + 'skipheaders' VALUE '1', + 'characterset' VALUE pEncoding + ); + ELSE + vFormat := json_object('delimiter' VALUE '|', 'skipheaders' VALUE '1'); + END IF; + ELSE + IF pEncoding IS NOT NULL AND LENGTH(TRIM(pEncoding)) > 0 THEN + vFormat := json_object( + 'type' VALUE 'CSV', + 'skipheaders' VALUE '1', + 'ignoremissingcolumns' VALUE 'true', + 'characterset' VALUE pEncoding + ); + ELSE + vFormat := json_object('type' VALUE 'CSV', 'skipheaders' VALUE '1', 'ignoremissingcolumns' value 'true'); + END IF; + END IF; + + vFileExtension := '.csv'; + + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN_PREFIX, ENV_MANAGER.MSG_UNKNOWN_PREFIX); + END IF; + + -- No filename give: Match all csv files + IF pFileName IS NOT NULL THEN + vFileName := pFileName; + ELSE + vFileName := pBucketUri||vPrefix||'/*'||vFileExtension; + END IF; + + ENV_MANAGER.LOG_PROCESS_EVENT('pTableName', 'DEBUG', pTableName); + ENV_MANAGER.LOG_PROCESS_EVENT('ENV_MANAGER.vpCredentialName', 'DEBUG', ENV_MANAGER.gvCredentialName); + ENV_MANAGER.LOG_PROCESS_EVENT('vFileName', 'DEBUG', vFileName); + ENV_MANAGER.LOG_PROCESS_EVENT('vColumnList', 'DEBUG', vColumnList); + ENV_MANAGER.LOG_PROCESS_EVENT('vFieldList', 'DEBUG', vFieldList); + ENV_MANAGER.LOG_PROCESS_EVENT('vFormat', 'DEBUG', vFormat); + + -- Pre-validation: Check CSV column count for CSV files only + IF SUBSTR(pPrefix,1,5) = 'INBOX' AND pFileName IS NOT NULL THEN + DECLARE + vCsvFirstLine VARCHAR2(4000); + vCsvColCount NUMBER := 0; + vTemplateColCount NUMBER := 0; + vExcessColumns VARCHAR2(2000); + + -- Get template column count + CURSOR c_template_count IS + SELECT COUNT(*) as col_count + FROM ALL_TAB_COLUMNS + WHERE OWNER = UPPER(REPLACE(REGEXP_SUBSTR(pTemplateTableName,'.*\.'),'.','')) + AND TABLE_NAME = UPPER(REGEXP_REPLACE(pTemplateTableName,'^.*\.','')); + + BEGIN + -- Get template column count + FOR rec IN c_template_count LOOP + vTemplateColCount := rec.col_count; + END LOOP; + + -- Read first line of CSV to count columns + BEGIN + SELECT UTL_RAW.CAST_TO_VARCHAR2( + DBMS_LOB.SUBSTR( + DBMS_CLOUD.GET_OBJECT( + credential_name => ENV_MANAGER.gvCredentialName, + object_uri => pFileName + ), + 4000, 1 + ) + ) INTO vCsvFirstLine FROM DUAL; + + -- Count commas in header line + 1 for total columns + vCsvColCount := REGEXP_COUNT(REGEXP_SUBSTR(vCsvFirstLine, '[^'||chr(10)||']*'), ',') + 1; + + ENV_MANAGER.LOG_PROCESS_EVENT('CSV Column Count: ' || vCsvColCount || ', Template Column Count: ' || vTemplateColCount, 'INFO', vParameters); + + -- Check for excess columns + IF vCsvColCount > vTemplateColCount THEN + vgMsgTmp := ENV_MANAGER.MSG_EXCESS_COLUMNS_DETECTED + ||cgBL||'EXCESS COLUMNS DETECTED!' + ||cgBL||'CSV file has ' || vCsvColCount || ' columns but template expects only ' || vTemplateColCount + ||cgBL||'Excess columns: ' || (vCsvColCount - vTemplateColCount) + ||cgBL||'CSV header: ' || SUBSTR(REGEXP_SUBSTR(vCsvFirstLine, '[^'||chr(10)||']*'), 1, 200) + ||cgBL||'POSSIBLE SOLUTIONS:' + ||cgBL||' 1. Remove excess columns from CSV file before processing' + ||cgBL||' 2. Add excess columns to template table: ' || pTemplateTableName; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EXCESS_COLUMNS_DETECTED, vgMsgTmp); + END IF; + + EXCEPTION + WHEN ENV_MANAGER.ERR_EXCESS_COLUMNS_DETECTED THEN + RAISE; -- Re-raise the excess columns error + WHEN ENV_MANAGER.ERR_FILE_VALIDATION_FAILED THEN + RAISE; -- Re-raise the validation error + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Warning: Could not perform pre-validation column count check: ' || SQLERRM, 'WARN', vParameters); + -- Continue with normal processing if pre-validation fails + END; + END; + END IF; + + DBMS_CLOUD.CREATE_EXTERNAL_TABLE( + TABLE_NAME => pTableName, + CREDENTIAL_NAME => ENV_MANAGER.gvCredentialName, + FILE_URI_LIST => vFileName, + COLUMN_LIST => vColumnList, + FIELD_LIST => vFieldList, + FORMAT => vFormat + ); + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + EXCEPTION + WHEN ENV_MANAGER.ERR_EXCESS_COLUMNS_DETECTED THEN + RAISE; -- Re-raise the excess columns error with specific code -20011 + WHEN ENV_MANAGER.ERR_UNKNOWN_PREFIX THEN + vgMsgTmp := ENV_MANAGER.MSG_UNKNOWN_PREFIX || ': ' || pPrefix; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN_PREFIX, vgMsgTmp); + WHEN ENV_MANAGER.ERR_MISSING_COLUMN_DATE_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END CREATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE CREATE_EXTERNAL_TABLE(pSourceFileReceivedKey IN NUMBER) + -- + -- Create external table for a single source file to validate the file structure + -- + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vSourceFileReceived tSourceFileReceived; + vTableName VARCHAR2(200); + vFileName VARCHAR2(1000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + vSourceFileConfig := GET_SOURCE_FILE_CONFIG(pSourceFileReceivedKey => pSourceFileReceivedKey); + vSourceFileReceived := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + vTableName := vSourceFileConfig.TEMPLATE_TABLE_NAME; + + vFileName := ENV_MANAGER.gvInboxBucketUri ||vSourceFileReceived.SOURCE_FILE_PREFIX_INBOX||vSourceFileReceived.SOURCE_FILE_NAME; + + CREATE_EXTERNAL_TABLE( + pTableName => vSourceFileReceived.EXTERNAL_TABLE_NAME, + pTemplateTableName => vSourceFileConfig.TEMPLATE_TABLE_NAME, + pPrefix => vSourceFileReceived.SOURCE_FILE_PREFIX_INBOX, + pBucketUri => ENV_MANAGER.gvInboxBucketUri, + pFileName => vFileName, + pDelimiter => ',', + pEncoding => vSourceFileConfig.ENCODING -- MARS-1049: NOWY PARAMETR + ); + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + END CREATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE VALIDATE_SOURCE_FILE_RECEIVED(pSourceFileReceivedKey IN NUMBER) + -- + -- Check the structure of the received file using DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE + -- + IS + vSourceFileReceived tSourceFileReceived; + vOperationId NUMBER := -1; + vBadfileTable USER_LOAD_OPERATIONS.BADFILE_TABLE%TYPE; + vStatus USER_LOAD_OPERATIONS.STATUS%TYPE; + vErrors NUMBER := 0; + vNumRows NUMBER := 0; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + vSourceFileReceived := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + ENV_MANAGER.LOG_PROCESS_EVENT('vSourceFileReceived.EXTERNAL_TABLE_NAME: '||vSourceFileReceived.EXTERNAL_TABLE_NAME,'DEBUG', vParameters); + BEGIN + DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE(vSourceFileReceived.EXTERNAL_TABLE_NAME, vOperationId); + EXCEPTION + WHEN OTHERS THEN + -- Log complete error details including full stack trace and backtrace + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_FILE_VALIDATION_FAILED, vParameters, 'FILE_MANAGER'); + + -- Call detailed validation error analysis and log the results + DECLARE + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vValidationLogTable VARCHAR2(200); + vTemplateSchema VARCHAR2(200); + vTemplateTable VARCHAR2(200); + vCsvFileUri VARCHAR2(2000); + vAnalysisResult VARCHAR2(32000); + vFailedOperationId NUMBER; + BEGIN + -- Get source file configuration + vSourceFileConfig := GET_SOURCE_FILE_CONFIG(pSourceFileReceivedKey => pSourceFileReceivedKey); + + -- Extract template schema and table from template table name + vTemplateSchema := REPLACE(REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME,'.*\.'),'.',''); + vTemplateTable := REPLACE(REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME,'\..*'),'.',''); + + -- Construct CSV file URI + vCsvFileUri := ENV_MANAGER.gvInboxBucketUri || vSourceFileReceived.SOURCE_FILE_PREFIX_INBOX || vSourceFileReceived.SOURCE_FILE_NAME; + + -- Find the failed validation operation ID + SELECT MAX(ID) INTO vFailedOperationId + FROM USER_LOAD_OPERATIONS + WHERE TABLE_NAME = vSourceFileReceived.EXTERNAL_TABLE_NAME + AND TYPE = 'VALIDATE' + AND STATUS != 'COMPLETED'; + + -- Get validation log table name + IF vFailedOperationId IS NOT NULL THEN + SELECT LOGFILE_TABLE INTO vValidationLogTable + FROM USER_LOAD_OPERATIONS + WHERE ID = vFailedOperationId; + + -- Call detailed error analysis + vAnalysisResult := ENV_MANAGER.ANALYZE_VALIDATION_ERRORS( + pValidationLogTable => vValidationLogTable, + pTemplateSchema => vTemplateSchema, + pTemplateTable => vTemplateTable, + pCsvFileUri => vCsvFileUri + ); + + -- Log detailed analysis results + ENV_MANAGER.LOG_PROCESS_EVENT('DETAILED VALIDATION ERROR ANALYSIS:', 'ERROR', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT(vAnalysisResult, 'ERROR', vParameters); + END IF; + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Error during validation analysis: ' || SQLERRM, 'ERROR', vParameters); + END; + + MOVE_FILE(pSourceFileReceivedKey => pSourceFileReceivedKey, pDestination => 'QUARANTINE'); + -- Ensure the status change is committed before raising exception + COMMIT; + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, ENV_MANAGER.MSG_FILE_VALIDATION_FAILED); + END; + ENV_MANAGER.LOG_PROCESS_EVENT('vOperationId of validation: '||vOperationId,'DEBUG', vParameters); + IF vOperationId = -1 + THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + END IF; + + SELECT BADFILE_TABLE, ROWS_LOADED, STATUS + INTO vBadfileTable, vNumRows, vStatus + FROM USER_LOAD_OPERATIONS + WHERE ID = vOperationId; + + -- MARS-1409: Extract and validate A_WORKFLOW_HISTORY_KEY from external table + DECLARE + vWorkflowHistoryKey NUMBER; + vWorkflowKeyCount NUMBER; + vWorkflowKeyDistinct NUMBER; + vDynamicSQL VARCHAR2(1000); + BEGIN + -- Build dynamic SQL to count distinct A_WORKFLOW_HISTORY_KEY values + vDynamicSQL := 'SELECT COUNT(*), COUNT(DISTINCT A_WORKFLOW_HISTORY_KEY) FROM ' || vSourceFileReceived.EXTERNAL_TABLE_NAME; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Extracting A_WORKFLOW_HISTORY_KEY from external table', 'DEBUG', vParameters); + + -- Count total rows and distinct workflow keys + EXECUTE IMMEDIATE vDynamicSQL INTO vWorkflowKeyCount, vWorkflowKeyDistinct; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Total rows: ' || vWorkflowKeyCount || ', Distinct A_WORKFLOW_HISTORY_KEY values: ' || vWorkflowKeyDistinct, 'DEBUG', vParameters); + + -- Validate workflow key presence and uniqueness + IF vWorkflowKeyDistinct = 0 OR vWorkflowKeyDistinct IS NULL THEN + -- No A_WORKFLOW_HISTORY_KEY found or all values are NULL + vgMsgTmp := ENV_MANAGER.MSG_WORKFLOW_KEY_NULL || ' [File: ' || vSourceFileReceived.SOURCE_FILE_NAME || ']'; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WORKFLOW_KEY_NULL, vgMsgTmp); + + ELSIF vWorkflowKeyDistinct > 1 THEN + -- Multiple different A_WORKFLOW_HISTORY_KEY values found + vgMsgTmp := ENV_MANAGER.MSG_MULTIPLE_WORKFLOW_KEYS || ' [Found: ' || vWorkflowKeyDistinct || ' distinct values in file: ' || vSourceFileReceived.SOURCE_FILE_NAME || ']'; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_WORKFLOW_KEYS, vgMsgTmp); + + ELSE + -- Exactly one A_WORKFLOW_HISTORY_KEY value - fetch and save it + vDynamicSQL := 'SELECT DISTINCT A_WORKFLOW_HISTORY_KEY FROM ' || vSourceFileReceived.EXTERNAL_TABLE_NAME; + EXECUTE IMMEDIATE vDynamicSQL INTO vWorkflowHistoryKey; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Extracted A_WORKFLOW_HISTORY_KEY: ' || vWorkflowHistoryKey, 'DEBUG', vParameters); + + -- Update A_SOURCE_FILE_RECEIVED with workflow history key + UPDATE CT_MRDS.A_SOURCE_FILE_RECEIVED + SET A_WORKFLOW_HISTORY_KEY = vWorkflowHistoryKey + WHERE A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + ENV_MANAGER.LOG_PROCESS_EVENT('MARS-1409: Updated A_SOURCE_FILE_RECEIVED with A_WORKFLOW_HISTORY_KEY: ' || vWorkflowHistoryKey, 'INFO', vParameters); + END IF; + EXCEPTION + WHEN ENV_MANAGER.ERR_WORKFLOW_KEY_NULL THEN + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE; + WHEN ENV_MANAGER.ERR_MULTIPLE_WORKFLOW_KEYS THEN + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE; + WHEN OTHERS THEN + vgMsgTmp := 'MARS-1409: Error extracting A_WORKFLOW_HISTORY_KEY: ' || SQLERRM; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, vgMsgTmp); + END; + +-- DBMS_OUTPUT.PUT_LINE(vStatus); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATED'); + ENV_MANAGER.LOG_PROCESS_EVENT('File status changed to VALIDATED','DEBUG', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + WHEN ENV_MANAGER.ERR_DIDNT_GET_LOAD_OPERATION_ID THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + + WHEN ENV_MANAGER.ERR_FILE_VALIDATION_FAILED THEN + vgMsgTmp := ENV_MANAGER.MSG_FILE_VALIDATION_FAILED; + ENV_MANAGER.LOG_PROCESS_ERROR(vgMsgTmp, vParameters, 'FILE_MANAGER'); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, vgMsgTmp); + + WHEN ENV_MANAGER.ERR_WORKFLOW_KEY_NULL THEN + vgMsgTmp := ENV_MANAGER.MSG_WORKFLOW_KEY_NULL; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WORKFLOW_KEY_NULL, vgMsgTmp); + + WHEN ENV_MANAGER.ERR_MULTIPLE_WORKFLOW_KEYS THEN + vgMsgTmp := ENV_MANAGER.MSG_MULTIPLE_WORKFLOW_KEYS; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => 'VALIDATION_FAILED'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_WORKFLOW_KEYS, vgMsgTmp); + + WHEN OTHERS THEN + IF SQLCODE = -20404 THEN + vgMsgTmp := ENV_MANAGER.MSG_FILE_NOT_FOUND_ON_CLOUD||cgBL||SQLERRM; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_FOUND_ON_CLOUD, vgMsgTmp); + + ELSIF SQLCODE = -20003 THEN + execute immediate 'select LISTAGG(record, '''||cgBL||''') from (select * from '||REGEXP_SUBSTR(SQLERRM, '"([^"]+)"."([^"]+)"')||' order by rownum desc) where rownum <=2' + into vgMsgTmp; + vgMsgTmp := ENV_MANAGER.MSG_FILE_VALIDATION_FAILED||cgBL||SQLERRM||cgBL||vgMsgTmp; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, vgMsgTmp); +-- ELSIF SQLCODE = -20000 THEN + -- TO_DO Add additional info about current config + -- ENV_MANAGER.MSG_FILE_VALIDATION_FAILED := ENV_MANAGER.MSG_FILE_VALIDATION_FAILED||cgBL||SQLERRM||cgBL||FILE_MANAGER.OUTPUT_SOURCE_FILE_CONFIG_INFO( ..config key value.. ); +-- dbms_output.put_line(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); +-- ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_WRONG_DATE_TIMESTAMP_FORMAT, 'ERROR', vParameters); +-- ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); +-- RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DATE_TIMESTAMP_FORMAT, ENV_MANAGER.MSG_WRONG_DATE_TIMESTAMP_FORMAT); + + ELSE + -- Log complete error details including full stack trace and backtrace + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_UNKNOWN, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END IF; + END VALIDATE_SOURCE_FILE_RECEIVED; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2 + -- + -- wrapper for DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE + -- + IS + vOperationId NUMBER := -1; + vBadfileTable USER_LOAD_OPERATIONS.BADFILE_TABLE%TYPE; + vLogfileTable USER_LOAD_OPERATIONS.LOGFILE_TABLE%TYPE; + vStatus USER_LOAD_OPERATIONS.STATUS%TYPE; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vDetails clob; + TYPE TCURSOR is REF CURSOR; + vCursor TCURSOR; + vQuery VARCHAR2(1000); + vRecord VARCHAR2(10000); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pTableName => '''||nvl(pTableName, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE(pTableName, vOperationId); + ENV_MANAGER.LOG_PROCESS_EVENT('vOperationId of validation: '||vOperationId,'DEBUG', vParameters); + IF vOperationId = -1 + THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + END IF; + + SELECT decode(STATUS, 'COMPLETED', 'PASSED', STATUS), LOGFILE_TABLE, BADFILE_TABLE + INTO vStatus, vLogfileTable, vBadfileTable + FROM USER_LOAD_OPERATIONS + WHERE ID = vOperationId; + + RETURN vStatus; + EXCEPTION + WHEN ENV_MANAGER.ERR_DIDNT_GET_LOAD_OPERATION_ID THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + + WHEN OTHERS THEN + SELECT decode(STATUS, 'COMPLETED', 'PASSED', STATUS), LOGFILE_TABLE, BADFILE_TABLE + INTO vStatus, vLogfileTable, vBadfileTable + FROM USER_LOAD_OPERATIONS + WHERE ID = vOperationId; + vQuery := 'select record from ( + select + nvl(l.record,''----------------------------------------------------'') as record + ,rownum as lp + ,max(case when nvl(instr(l.record, ''error'' ),0) > 0 then rownum else 0 end) over (partition by 1) as ENV_MANAGER.ERR_row + from '||vLogfileTable||' l + ) + where lp >= ENV_MANAGER.ERR_row + order by rownum'; + + vDetails := vStatus||cgBL||'----------------------------------------------------'||cgBL; + + OPEN vCursor for vQuery; + loop + fetch vCursor into vRecord; + EXIT WHEN vCursor%NOTFOUND; + vDetails := vDetails ||vRecord ||cgBL; +-- for i in loop +-- vDetails := vDetails ||i.record ||cgBL; + end loop; + CLOSE vCursor; + vDetails := vDetails||'More details can be found in below tables:'||cgBL|| + ' SELECT * FROM USER_LOAD_OPERATIONS WHERE ID = '||vOperationId||';'||cgBL|| + ' SELECT * FROM '||vLogfileTable||';'||cgBL|| + ' SELECT * FROM '||vBadfileTable||';' + ; + + RETURN vDetails; + + END VALIDATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION S_VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2 + -- + -- Simple + -- + IS + vCount PLS_INTEGER; + BEGIN + execute immediate 'select count(1) from '||pTableName into vCount; + IF vCount >= 0 + THEN + RETURN 'PASSED'; + END IF; + + RETURN 'FAILED'; + EXCEPTION + WHEN OTHERS THEN + RETURN 'FAILED'; + END S_VALIDATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE DROP_EXTERNAL_TABLE(pSourceFileReceivedKey IN NUMBER) + -- + -- Drop external table created to validate the file structure + -- + IS + vSourceFileReceived tSourceFileReceived; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => '||nvl(to_char(pSourceFileReceivedKey), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + vSourceFileReceived := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + EXECUTE IMMEDIATE 'DROP TABLE '||vSourceFileReceived.EXTERNAL_TABLE_NAME; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END DROP_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE COPY_FILE(pSourceFileReceivedKey IN NUMBER, pDestination IN VARCHAR2) + -- + -- Possible pDestination values are: 'ODS' or 'ARCHIVE' + -- + IS + vSourceFileReceivedInfo tSourceFileReceived; + vSourceObject VARCHAR2(2000); + vTargetObject VARCHAR2(2000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; +-- vStatus VARCHAR2(20); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => ' ||nvl(to_char(pSourceFileReceivedKey), 'NULL'), + 'pDestination => '''||nvl(pDestination, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + vSourceFileReceivedInfo := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + + IF pDestination = 'ODS' THEN + vSourceObject := ENV_MANAGER.gvInboxBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_INBOX ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vTargetObject := ENV_MANAGER.gvDataBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_ODS ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + END IF; + + DBMS_CLOUD.COPY_OBJECT(source_credential_name => ENV_MANAGER.gvCredentialName, + source_object_uri => vSourceObject, + target_object_uri => vTargetObject, + target_credential_name => ENV_MANAGER.gvCredentialName + ); + ENV_MANAGER.LOG_PROCESS_EVENT('File copied to '||pDestination||' target location','DEBUG', vTargetObject); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END COPY_FILE; + + ---------------------------------------------------------------------------------------------------- + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE MOVE_FILE(pSourceFileReceivedKey IN NUMBER, pDestination IN VARCHAR2) + -- + -- Possible pDestination values are: 'ODS' or 'ARCHIVE' + -- + IS + vSourceFileReceivedInfo tSourceFileReceived; + vSourceObject VARCHAR2(2000); + vTargetObject VARCHAR2(2000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vStatus VARCHAR2(20); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedKey => ' ||nvl(to_char(pSourceFileReceivedKey), 'NULL'), + 'pDestination => '''||nvl(pDestination, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + + vSourceFileReceivedInfo := GET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey); + + IF pDestination = 'ODS' THEN + vSourceObject := ENV_MANAGER.gvInboxBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_INBOX ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vTargetObject := ENV_MANAGER.gvDataBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_ODS ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vStatus := 'READY_FOR_INGESTION'; + ELSIF pDestination = 'QUARANTINE' THEN + vSourceObject := ENV_MANAGER.gvInboxBucketUri ||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_INBOX ||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vTargetObject := ENV_MANAGER.gvInboxBucketUri||vSourceFileReceivedInfo.SOURCE_FILE_PREFIX_QUARANTINE||vSourceFileReceivedInfo.SOURCE_FILE_NAME; + vStatus := 'VALIDATION_FAILED'; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + END IF; + + DBMS_CLOUD.MOVE_OBJECT(source_credential_name => ENV_MANAGER.gvCredentialName, + source_object_uri => vSourceObject, + target_object_uri => vTargetObject, + target_credential_name => ENV_MANAGER.gvCredentialName + ); + ENV_MANAGER.LOG_PROCESS_EVENT('File moved to '||pDestination||' target location','DEBUG', vTargetObject); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => pSourceFileReceivedKey, pStatus => vStatus); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + WHEN ENV_MANAGER.ERR_WRONG_DESTINATION_PARAM THEN + ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM := ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM + ||cgBL||' '||'Possible parameters are: ''ODS'' or ''ARCHIVE''' + ||cgBL||' '||'Provided destination parameter: '''||pDestination||''''; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + WHEN ENV_MANAGER.ERR_NO_CONFIG_FOR_RECEIVED_FILE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END MOVE_FILE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE DELETE_FOLDER_CONTENTS(pBucketArea IN VARCHAR2, pFolderPrefix IN VARCHAR2) + -- + -- Delete all files from specified folder in cloud storage + -- pBucketArea: 'INBOX', 'DATA', 'ARCHIVE' + -- pFolderPrefix: folder path within bucket (e.g., 'C2D/UC_DISSEM/UC_NMA_DISSEM/') + -- + IS + vBucketUri VARCHAR2(2000); + vFolderUri VARCHAR2(2000); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vFilesDeleted PLS_INTEGER := 0; + vObjectName VARCHAR2(4000); + vFullObjectUri VARCHAR2(4000); + + -- Cursor to list all objects in the folder + CURSOR c_objects IS + SELECT object_name + FROM TABLE(DBMS_CLOUD.LIST_OBJECTS( + credential_name => ENV_MANAGER.gvCredentialName, + location_uri => vBucketUri + )) + WHERE object_name IS NOT NULL + AND object_name LIKE pFolderPrefix || '%'; + + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( + 'pBucketArea => '''||nvl(pBucketArea, 'NULL')||'''', + 'pFolderPrefix => '''||nvl(pFolderPrefix, 'NULL')||'''' + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + -- Get bucket URI based on bucket area + vBucketUri := GET_BUCKET_URI(pBucketArea); + + ENV_MANAGER.LOG_PROCESS_EVENT('Listing objects in bucket with prefix: ' || pFolderPrefix, 'DEBUG', vBucketUri); + + -- List and delete all objects in the folder + FOR obj_rec IN c_objects LOOP + vObjectName := obj_rec.object_name; + vFullObjectUri := vBucketUri || vObjectName; + + BEGIN + ENV_MANAGER.LOG_PROCESS_EVENT('Deleting object', 'DEBUG', vFullObjectUri); + + DBMS_CLOUD.DELETE_OBJECT( + credential_name => ENV_MANAGER.gvCredentialName, + object_uri => vFullObjectUri + ); + + vFilesDeleted := vFilesDeleted + 1; + ENV_MANAGER.LOG_PROCESS_EVENT('Object deleted successfully', 'DEBUG', vObjectName); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Error deleting object: ' || vObjectName || ' - ' || SQLERRM, 'ERROR', vParameters); + -- Continue with next file instead of stopping the whole process + END; + END LOOP; + + ENV_MANAGER.LOG_PROCESS_EVENT('Total files deleted: ' || vFilesDeleted, 'INFO', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT('Error in DELETE_FOLDER_CONTENTS: ' || SQLERRM, 'ERROR', vParameters); + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + END DELETE_FOLDER_CONTENTS; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + -- + -- Ubmrella procedure that calls + -- - REGISTER_SOURCE_FILE_RECEIVED + -- - CREATE_EXTERNAL_TABLE + -- - VALIDATE_SOURCE_FILE_RECEIVED + -- - DROP_EXTERNAL_TABLE + -- - MOVE_FILE + IS + vSourceFileId NUMBER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '||nvl(pSourceFileReceivedName, 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + ---- + vSourceFileId := REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName); + CREATE_EXTERNAL_TABLE(vSourceFileId); + VALIDATE_SOURCE_FILE_RECEIVED(vSourceFileId); + DROP_EXTERNAL_TABLE(vSourceFileId); +-- COPY_FILE(vSourceFileId, 'ODS'); + MOVE_FILE(vSourceFileId, 'ODS'); + SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => vSourceFileId, pStatus => 'READY_FOR_INGESTION'); + + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + + EXCEPTION + -- -20001 + WHEN ENV_MANAGER.ERR_EMPTY_FILEURI_AND_RECKEY THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EMPTY_FILEURI_AND_RECKEY, ENV_MANAGER.MSG_EMPTY_FILEURI_AND_RECKEY); + -- -20002 + WHEN ENV_MANAGER.ERR_NO_CONFIG_MATCH_FOR_FILEURI THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH_FOR_FILEURI, ENV_MANAGER.MSG_NO_CONFIG_MATCH_FOR_FILEURI); + -- -20003 + WHEN ENV_MANAGER.ERR_MULTIPLE_MATCH_FOR_SRCFILE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE, ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE); + -- -20004 + WHEN ENV_MANAGER.ERR_MISSING_COLUMN_DATE_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT); + -- -20005 + WHEN ENV_MANAGER.ERR_MULTIPLE_COLUMN_DATE_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTIPLE_COLUMN_DATE_FORMAT), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MULTIPLE_COLUMN_DATE_FORMAT); + -- -20006 + WHEN ENV_MANAGER.ERR_DIDNT_GET_LOAD_OPERATION_ID THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DIDNT_GET_LOAD_OPERATION_ID, ENV_MANAGER.MSG_DIDNT_GET_LOAD_OPERATION_ID); + -- -20007 + WHEN ENV_MANAGER.ERR_NO_CONFIG_FOR_RECEIVED_FILE THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_NO_CONFIG_FOR_RECEIVED_FILE); + -- -20008 + WHEN ENV_MANAGER.ERR_MULTI_CONFIG_FOR_RECEIVED_FILE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTI_CONFIG_FOR_RECEIVED_FILE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTI_CONFIG_FOR_RECEIVED_FILE, ENV_MANAGER.MSG_MULTI_CONFIG_FOR_RECEIVED_FILE); + -- -20009 + WHEN ENV_MANAGER.ERR_FILE_NOT_FOUND_ON_CLOUD THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_FILE_NOT_FOUND_ON_CLOUD), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_FOUND_ON_CLOUD, ENV_MANAGER.MSG_FILE_NOT_FOUND_ON_CLOUD); + -- -20010 + WHEN ENV_MANAGER.ERR_FILE_VALIDATION_FAILED THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_FILE_VALIDATION_FAILED), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_VALIDATION_FAILED, ENV_MANAGER.MSG_FILE_VALIDATION_FAILED); + -- -20011 + WHEN ENV_MANAGER.ERR_NO_CONFIG_MATCH THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_NO_CONFIG_MATCH), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NO_CONFIG_MATCH, ENV_MANAGER.MSG_NO_CONFIG_MATCH); + -- -20012 + WHEN ENV_MANAGER.ERR_UNKNOWN_PREFIX THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_UNKNOWN_PREFIX), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN_PREFIX, ENV_MANAGER.MSG_UNKNOWN_PREFIX); + -- -20013 + WHEN ENV_MANAGER.ERR_TABLE_NOT_EXISTS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_TABLE_NOT_EXISTS), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_TABLE_NOT_EXISTS, ENV_MANAGER.MSG_TABLE_NOT_EXISTS); + -- -20014 + WHEN ENV_MANAGER.ERR_COLUMN_NOT_EXISTS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_COLUMN_NOT_EXISTS), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_COLUMN_NOT_EXISTS, ENV_MANAGER.MSG_COLUMN_NOT_EXISTS); + -- -20015 + WHEN ENV_MANAGER.ERR_UNSUPPORTED_DATA_TYPE THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_UNSUPPORTED_DATA_TYPE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNSUPPORTED_DATA_TYPE, ENV_MANAGER.MSG_UNSUPPORTED_DATA_TYPE); + -- -20016 + WHEN ENV_MANAGER.ERR_MISSING_SOURCE_KEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MISSING_SOURCE_KEY), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_SOURCE_KEY, ENV_MANAGER.MSG_MISSING_SOURCE_KEY); + -- -20017 + WHEN ENV_MANAGER.ERR_NULL_SOURCE_FILE_CONFIG_KEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_NULL_SOURCE_FILE_CONFIG_KEY), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_NULL_SOURCE_FILE_CONFIG_KEY, ENV_MANAGER.MSG_NULL_SOURCE_FILE_CONFIG_KEY); + -- -20018 + WHEN ENV_MANAGER.ERR_DUPLICATED_SOURCE_KEY THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_DUPLICATED_SOURCE_KEY), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DUPLICATED_SOURCE_KEY, ENV_MANAGER.MSG_DUPLICATED_SOURCE_KEY); + -- -20019 + WHEN ENV_MANAGER.ERR_MISSING_CONTAINER_CONFIG THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MISSING_CONTAINER_CONFIG), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_CONTAINER_CONFIG, ENV_MANAGER.MSG_MISSING_CONTAINER_CONFIG); + -- -20020 + WHEN ENV_MANAGER.ERR_MULTIPLE_CONTAINER_ENTRIES THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_MULTIPLE_CONTAINER_ENTRIES), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_CONTAINER_ENTRIES, ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES); + -- -20021 + WHEN ENV_MANAGER.ERR_WRONG_DESTINATION_PARAM THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DESTINATION_PARAM, ENV_MANAGER.MSG_WRONG_DESTINATION_PARAM); + -- -20022 + WHEN ENV_MANAGER.ERR_FILE_NOT_EXISTS_ON_CLOUD THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_NOT_EXISTS_ON_CLOUD, ENV_MANAGER.MSG_FILE_NOT_EXISTS_ON_CLOUD); + -- -20023 + WHEN ENV_MANAGER.ERR_FILE_ALREADY_REGISTERED THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_FILE_ALREADY_REGISTERED, ENV_MANAGER.MSG_FILE_ALREADY_REGISTERED); + -- -20024 + WHEN ENV_MANAGER.ERR_WRONG_DATE_TIMESTAMP_FORMAT THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_WRONG_DATE_TIMESTAMP_FORMAT), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_WRONG_DATE_TIMESTAMP_FORMAT, ENV_MANAGER.MSG_WRONG_DATE_TIMESTAMP_FORMAT); + + -- -20011 + WHEN ENV_MANAGER.ERR_EXCESS_COLUMNS_DETECTED THEN + ENV_MANAGER.LOG_PROCESS_ERROR(ENV_MANAGER.MSG_EXCESS_COLUMNS_DETECTED, vParameters, 'FILE_MANAGER'); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_EXCESS_COLUMNS_DETECTED, ENV_MANAGER.MSG_EXCESS_COLUMNS_DETECTED); + + -- -20999 + WHEN ENV_MANAGER.ERR_UNKNOWN THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> ENV_MANAGER.CODE_UNKNOWN), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END PROCESS_SOURCE_FILE; + + +---------------------------------------------------------------------------------------------------- + + FUNCTION PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + RETURN PLS_INTEGER + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileReceivedName => '||nvl(pSourceFileReceivedName, 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + ---- + PROCESS_SOURCE_FILE(pSourceFileReceivedName => pSourceFileReceivedName); + ---- + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + RETURN SQLCODE; + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RETURN SQLCODE; + END PROCESS_SOURCE_FILE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DATE_FORMAT( + pTemplateTableName IN VARCHAR2, + pColumnName IN VARCHAR2 + ) + RETURN VARCHAR2 + IS + vDateFormat A_COLUMN_DATE_FORMAT.DATE_FORMAT%TYPE; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vColumnName VARCHAR2(200); + vGetDefault BOOLEAN := FALSE; + BEGIN + vColumnName := trim(pColumnName); + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pColumnName => '''||nvl(vColumnName, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + BEGIN + SELECT DATE_FORMAT + INTO vDateFormat + FROM CT_MRDS.A_COLUMN_DATE_FORMAT F + WHERE F.TEMPLATE_TABLE_NAME = pTemplateTableName + AND F.COLUMN_NAME = vColumnName; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + vGetDefault := TRUE; + WHEN TOO_MANY_ROWS THEN + -- Below error should not happened because: + -- Unique constraint added on table A_COLUMN_DATE_FORMAT on columns: (TEMPLATE_TABLE_NAME, COLUMN_NAME) + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_COLUMN_DATE_FORMAT, ENV_MANAGER.MSG_MULTIPLE_COLUMN_DATE_FORMAT); + END; + IF vGetDefault THEN + BEGIN + SELECT DATE_FORMAT + INTO vDateFormat + FROM CT_MRDS.A_COLUMN_DATE_FORMAT F + WHERE F.TEMPLATE_TABLE_NAME = pTemplateTableName + AND F.COLUMN_NAME = 'DEFAULT'; + EXCEPTION + WHEN NO_DATA_FOUND THEN + vDateFormat := ENV_MANAGER.gvDefaultDateFormat; + END; + END IF; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vDateFormat; + END GET_DATE_FORMAT; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE GENERATE_EXTERNAL_TABLE_PARAMS ( + + pTemplateTableName IN VARCHAR2, + pColumnList OUT CLOB, + pFieldList OUT CLOB + ) + IS + vSchemaName VARCHAR2(200); + vTableName VARCHAR2(200); + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vMaxColumnNameLength PLS_INTEGER := 0; + vColType varchar2(200); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pColumnList => '''||nvl(pColumnList, 'NULL')||'''' + ,'pFieldList = '''||nvl(pFieldList, 'NULL')||'''' + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + vSchemaName := REPLACE(REGEXP_SUBSTR(pTemplateTableName,'.*\.'),'.',''); + ENV_MANAGER.LOG_PROCESS_EVENT('vSchemaName','DEBUG', vSchemaName); + vTableName := REPLACE(REGEXP_SUBSTR(pTemplateTableName,'\..*'),'.',''); + ENV_MANAGER.LOG_PROCESS_EVENT('vTableName','DEBUG', vTableName); + FOR rec IN ( + SELECT + '"'||column_name||'"' as quoted_column_name, + column_name, + data_type, + data_length, + char_length, -- MARS-1056: Added for CHAR/BYTE semantics + char_used, -- MARS-1056: Added for CHAR/BYTE semantics + data_precision, + data_scale, + column_id, + max(length(column_name)+1) over (partition by table_name) as max_column_name_length + FROM all_tab_columns + WHERE table_name = UPPER(vTableName) + AND owner = NVL(UPPER(vSchemaName), USER) + ORDER BY column_id + ) LOOP + -- Build the column_list string + rec.quoted_column_name := rpad(rec.quoted_column_name, rec.max_column_name_length+2, ' '); + + vColType := + CASE + -- MARS-1056: Fixed VARCHAR2 definition logic to preserve CHAR/BYTE semantics + WHEN rec.data_type = 'VARCHAR2' THEN + CASE + WHEN rec.char_used = 'C' THEN + rec.quoted_column_name || ' VARCHAR2(' || rec.char_length || ' CHAR)' + WHEN rec.char_used = 'B' THEN + rec.quoted_column_name || ' VARCHAR2(' || rec.data_length || ' BYTE)' + ELSE + -- Fallback for NULL char_used (should not occur but handle gracefully) + rec.quoted_column_name || ' VARCHAR2(' || rec.data_length || ')' + END + -- Other character types (preserve original logic) + -- MARS-1468: Fixed CHAR to use char_used/char_length (same as VARCHAR2 fix in MARS-1056) + WHEN rec.data_type = 'CHAR' THEN + CASE + WHEN rec.char_used = 'C' THEN + rec.quoted_column_name || ' CHAR(' || rec.char_length || ' CHAR)' + WHEN rec.char_used = 'B' THEN + rec.quoted_column_name || ' CHAR(' || rec.data_length || ' BYTE)' + ELSE + rec.quoted_column_name || ' CHAR(' || rec.data_length || ')' + END + -- MARS-1468: NCHAR/NVARCHAR2 - use char_length (data_length stores bytes in AL16UTF16, e.g. NCHAR(1) => data_length=2 but char_length=1) + WHEN rec.data_type IN ('NCHAR', 'NVARCHAR2') THEN + rec.quoted_column_name || ' ' || rec.data_type || '(' || rec.char_length || ')' + WHEN rec.data_type = 'NUMBER' THEN + rec.quoted_column_name || ' ' || rec.data_type || + CASE + WHEN rec.data_precision IS NOT NULL AND rec.data_scale IS NOT NULL THEN + '(' || rec.data_precision || ',' || rec.data_scale || ')' + WHEN rec.data_precision IS NOT NULL THEN + '(' || rec.data_precision || ')' + ELSE + '' + END + WHEN rec.data_type IN ('RAW') THEN + rec.quoted_column_name || ' ' || rec.data_type || '(' || rec.data_length || ')' + WHEN REGEXP_SUBSTR(rec.data_type, '^[A-Z]+') IN ('DATE', 'TIMESTAMP') THEN + rec.quoted_column_name || ' ' || rec.data_type + ELSE + rec.quoted_column_name || ' ' || rec.data_type + END; + pColumnList := pColumnList ||vColType ||cgBL|| ','; + -- Build the field_list string + -- Note: field_list uses CHAR() for CSV field definitions - this is correct behavior + pFieldList := pFieldList || + CASE + WHEN rec.data_type = 'DATE' THEN + -- MARS-1046: DATE format - wrap with NORMALIZE_DATE_FORMAT to fix ISO 8601 'T' separator + rec.quoted_column_name || ' DATE ' || CHR(39) || NORMALIZE_DATE_FORMAT(GET_DATE_FORMAT(pTemplateTableName => pTemplateTableName, pColumnName => rec.column_name)) || CHR(39) + WHEN rec.data_type LIKE 'TIMESTAMP%WITH TIME ZONE' THEN + -- MARS-1046: TIMESTAMP WITH TIME ZONE format for ISO 8601 with fractional seconds and timezone + -- Syntax: column_name CHAR(length) DATE_FORMAT TIMESTAMP WITH TIME ZONE MASK "format" + -- Use fixed length of 50 for ISO 8601 format (e.g., "2012-03-02T14:16:23.798+01:00" = 29 chars) + rec.quoted_column_name || ' CHAR(50) DATE_FORMAT TIMESTAMP WITH TIME ZONE MASK ' || CHR(39) || NORMALIZE_DATE_FORMAT(GET_DATE_FORMAT(pTemplateTableName => pTemplateTableName, pColumnName => rec.column_name)) || CHR(39) + WHEN REGEXP_SUBSTR(rec.data_type, '^[A-Z]+') = 'TIMESTAMP' THEN + -- Other TIMESTAMP types (without timezone) + -- SQL*Loader syntax: CHAR(length) DATE_FORMAT TIMESTAMP MASK "format" (not: TIMESTAMP 'format') + rec.quoted_column_name || ' CHAR(35) DATE_FORMAT TIMESTAMP MASK ' || CHR(39) || NORMALIZE_DATE_FORMAT(GET_DATE_FORMAT(pTemplateTableName => pTemplateTableName, pColumnName => rec.column_name)) || CHR(39) + WHEN rec.data_type IN ('VARCHAR2', 'CHAR') THEN + -- MARS-1468: For CHAR use char_length when char semantics (C), otherwise data_length + rec.quoted_column_name || ' CHAR(' || + CASE WHEN rec.char_used = 'C' THEN rec.char_length ELSE rec.data_length END + || ')' + WHEN rec.data_type IN ('NCHAR', 'NVARCHAR2') THEN + -- For CSV field definitions, use data_length for NCHAR/NVARCHAR2 + rec.quoted_column_name || ' CHAR(' || rec.data_length || ')' + ELSE + rec.quoted_column_name + END ||cgBL|| ','; + + + END LOOP; + + -- Remove the trailing comma and space from the strings + pColumnList := ' '||RTRIM(pColumnList, ','); + pFieldList := ' '||RTRIM(pFieldList, ','); + ENV_MANAGER.LOG_PROCESS_EVENT('vColumnList', 'DEBUG', pColumnList); + -- TO_DO !!! + -- Add check if pColumnList/pFieldList is empty or not + -- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + + -- Output the generated column_list and field_list + ENV_MANAGER.LOG_PROCESS_EVENT('column_list' ,'DEBUG',pColumnList); + ENV_MANAGER.LOG_PROCESS_EVENT('field_list' ,'DEBUG',pFieldList); + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + + EXCEPTION +-- WHEN ENV_MANAGER.ERR_MISSING_COLUMN_DATE_FORMAT THEN +-- ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_COLUMN_DATE_FORMAT, 'ERROR', vParameters); +-- RAISE_ERROR(ENV_MANAGER.CODE_MISSING_COLUMN_DATE_FORMAT); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END GENERATE_EXTERNAL_TABLE_PARAMS; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE ADD_SOURCE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE, + pSourceName IN CT_MRDS.A_SOURCE.SOURCE_NAME%TYPE + ) IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + INSERT INTO CT_MRDS.A_SOURCE(A_SOURCE_KEY, SOURCE_NAME) VALUES (pSourceKey, pSourceName); + COMMIT; + EXCEPTION + WHEN DUP_VAL_ON_INDEX THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_DUPLICATED_SOURCE_KEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_DUPLICATED_SOURCE_KEY, ENV_MANAGER.MSG_DUPLICATED_SOURCE_KEY); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END ADD_SOURCE; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE DELETE_SOURCE_CASCADE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE + ) IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + vSharedTemplateCount PLS_INTEGER := 0; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceKey => '''||nvl(pSourceKey, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + + -- First pass: Delete container files (those that have CONTAINER_FILE_KEY set) + for rec in (select A_SOURCE_FILE_CONFIG_KEY, TEMPLATE_TABLE_NAME from CT_MRDS.A_SOURCE_FILE_CONFIG + where A_SOURCE_KEY = pSourceKey AND CONTAINER_FILE_KEY IS NOT NULL + ORDER BY A_SOURCE_FILE_CONFIG_KEY) loop + -- Delete processed file records + delete from CT_MRDS.A_SOURCE_FILE_RECEIVED WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_SOURCE_FILE_RECEIVED records for container config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + + -- Check if TEMPLATE_TABLE_NAME is shared with other source systems before deleting date formats + IF rec.TEMPLATE_TABLE_NAME IS NOT NULL THEN + SELECT COUNT(DISTINCT A_SOURCE_KEY) + INTO vSharedTemplateCount + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME + AND A_SOURCE_KEY <> pSourceKey; -- Exclude current source being deleted + + -- Only delete date formats if template table is not shared with other sources + IF vSharedTemplateCount = 0 THEN + delete from CT_MRDS.A_COLUMN_DATE_FORMAT WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_COLUMN_DATE_FORMAT records for template: '||rec.TEMPLATE_TABLE_NAME,'DEBUG', vParameters); + ELSE + ENV_MANAGER.LOG_PROCESS_EVENT('Skipping A_COLUMN_DATE_FORMAT deletion - template table '||rec.TEMPLATE_TABLE_NAME||' is shared with '||vSharedTemplateCount||' other source systems','WARNING', vParameters); + END IF; + END IF; + + -- Delete container file configuration + delete from CT_MRDS.A_SOURCE_FILE_CONFIG WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted container A_SOURCE_FILE_CONFIG record for config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + end loop; + COMMIT; -- Commit container deletions + + -- Second pass: Delete parent files (those that do NOT have CONTAINER_FILE_KEY set) + for rec in (select A_SOURCE_FILE_CONFIG_KEY, TEMPLATE_TABLE_NAME from CT_MRDS.A_SOURCE_FILE_CONFIG + where A_SOURCE_KEY = pSourceKey AND CONTAINER_FILE_KEY IS NULL + ORDER BY A_SOURCE_FILE_CONFIG_KEY) loop + -- Delete processed file records + delete from CT_MRDS.A_SOURCE_FILE_RECEIVED WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_SOURCE_FILE_RECEIVED records for parent config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + + -- Check if TEMPLATE_TABLE_NAME is shared with other source systems before deleting date formats + IF rec.TEMPLATE_TABLE_NAME IS NOT NULL THEN + SELECT COUNT(DISTINCT A_SOURCE_KEY) + INTO vSharedTemplateCount + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME + AND A_SOURCE_KEY <> pSourceKey; -- Exclude current source being deleted + + -- Only delete date formats if template table is not shared with other sources + IF vSharedTemplateCount = 0 THEN + delete from CT_MRDS.A_COLUMN_DATE_FORMAT WHERE TEMPLATE_TABLE_NAME = rec.TEMPLATE_TABLE_NAME; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_COLUMN_DATE_FORMAT records for template: '||rec.TEMPLATE_TABLE_NAME,'DEBUG', vParameters); + ELSE + ENV_MANAGER.LOG_PROCESS_EVENT('Skipping A_COLUMN_DATE_FORMAT deletion - template table '||rec.TEMPLATE_TABLE_NAME||' is shared with '||vSharedTemplateCount||' other source systems','WARNING', vParameters); + END IF; + END IF; + + -- Delete parent file configuration + delete from CT_MRDS.A_SOURCE_FILE_CONFIG WHERE A_SOURCE_FILE_CONFIG_KEY = rec.A_SOURCE_FILE_CONFIG_KEY; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted parent A_SOURCE_FILE_CONFIG record for config key: '||rec.A_SOURCE_FILE_CONFIG_KEY,'DEBUG', vParameters); + end loop; + COMMIT; -- Commit parent deletions + + -- Delete source system record + DELETE FROM CT_MRDS.A_SOURCE where A_SOURCE_KEY = pSourceKey; + ENV_MANAGER.LOG_PROCESS_EVENT('Deleted A_SOURCE record for source key: '||pSourceKey,'DEBUG', vParameters); + COMMIT; -- Final commit for source deletion + + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END DELETE_SOURCE_CASCADE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_CONTAINER_SOURCE_FILE_CONFIG_KEY ( + pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ) RETURN PLS_INTEGER + IS + vSourceFileConfigKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileId => '||nvl(to_char(pSourceFileId), 'NULL'))); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + SELECT A_SOURCE_FILE_CONFIG_KEY + INTO vSourceFileConfigKey + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE SOURCE_FILE_ID = pSourceFileId + AND SOURCE_FILE_TYPE = 'CONTAINER'; + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + RETURN vSourceFileConfigKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.MSG_MISSING_CONTAINER_CONFIG := 'No match in A_SOURCE_FILE_CONFIG where SOURCE_FILE_TYPE=''CONTAINER'' and SOURCE_FILE_ID = '''||pSourceFileId||''''; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_CONTAINER_CONFIG, 'WARNING', vParameters); + RETURN NULL; + WHEN TOO_MANY_ROWS THEN + ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES := 'GET_CONTAINER_SOURCE_FILE_CONFIG_KEY: Multiple SOURCE_FILE_TYPE=''CONTAINER'' matches for SOURCE_FILE_ID: '||pSourceFileId||' in A_SOURCE_FILE_CONFIG'; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_CONTAINER_ENTRIES, ENV_MANAGER.MSG_MULTIPLE_CONTAINER_ENTRIES); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + END GET_CONTAINER_SOURCE_FILE_CONFIG_KEY; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_SOURCE_FILE_CONFIG_KEY ( + pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE DEFAULT 'INPUT' + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE + ) RETURN PLS_INTEGER + IS + vSourceFileConfigKey PLS_INTEGER; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST('pSourceFileType => '''||nvl(pSourceFileType, 'NULL')||'''' + ,'pSourceFileId => '''||nvl(pSourceFileId, 'NULL')||'''' + ,'pTableId => '''||nvl(pTableId, 'NULL')||'''')); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + SELECT A_SOURCE_FILE_CONFIG_KEY + INTO vSourceFileConfigKey + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE SOURCE_FILE_TYPE = pSourceFileType + AND SOURCE_FILE_ID = pSourceFileId + AND TABLE_ID = pTableId; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG', vParameters); + RETURN vSourceFileConfigKey; + + EXCEPTION + WHEN NO_DATA_FOUND THEN + RETURN NULL; + WHEN TOO_MANY_ROWS THEN + vgMsgTmp := ENV_MANAGER.MSG_MULTIPLE_MATCH_FOR_SRCFILE + ||cgBL||' '||'GET_SOURCE_FILE_CONFIG_KEY: Multiple matches in A_SOURCE_FILE_CONFIG table WHERE' + ||cgBL||' '||'SOURCE_FILE_TYPE: '||pSourceFileType + ||cgBL||' '||'SOURCE_FILE_ID: '||pSourceFileId + ||cgBL||' '||'TABLE_ID: '||pTableId; + ENV_MANAGER.LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MULTIPLE_MATCH_FOR_SRCFILE, vgMsgTmp); + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.MSG_UNKNOWN); + + END GET_SOURCE_FILE_CONFIG_KEY; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE ADD_SOURCE_FILE_CONFIG ( + pSourceKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_KEY%TYPE + ,pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pSourceFileDesc IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_DESC%TYPE + ,pSourceFileNamePattern IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_NAME_PATTERN%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE DEFAULT NULL + ,pTemplateTableName IN CT_MRDS.A_SOURCE_FILE_CONFIG.TEMPLATE_TABLE_NAME%TYPE DEFAULT NULL + ,pContainerFileKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.CONTAINER_FILE_KEY%TYPE DEFAULT NULL + ,pEncoding IN CT_MRDS.A_SOURCE_FILE_CONFIG.ENCODING%TYPE DEFAULT NULL -- MARS-1049 + ,pIsWorkflowSuccessRequired IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_WORKFLOW_SUCCESS_REQUIRED%TYPE DEFAULT 'Y' -- MARS-1409 + ,pIsArchiveEnabled IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_ARCHIVE_ENABLED%TYPE DEFAULT 'N' -- MARS-828 + ,pIsKeptInTrash IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_KEPT_IN_TRASH%TYPE DEFAULT 'Y' -- MARS-828 + ,pArchivalStrategy IN CT_MRDS.A_SOURCE_FILE_CONFIG.ARCHIVAL_STRATEGY%TYPE DEFAULT 'THRESHOLD_BASED' -- MARS-828 + ,pMinimumAgeMonths IN CT_MRDS.A_SOURCE_FILE_CONFIG.MINIMUM_AGE_MONTHS%TYPE DEFAULT 0 -- MARS-828 + ) IS + vSourceFileConfigKey PLS_INTEGER; + vSourceKeyExists PLS_INTEGER := 0; + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pSourceKey => '''||nvl(to_char(pSourceKey), 'NULL')||'''' + ,'pSourceFileType => '''||nvl(to_char(pSourceFileType), 'NULL')||'''' + ,'pSourceFileId => '''||nvl(to_char(pSourceFileId), 'NULL')||'''' + ,'pSourceFileDesc => '''||nvl(to_char(pSourceFileDesc), 'NULL')||'''' + ,'pSourceFileNamePattern => '''||nvl(to_char(pSourceFileNamePattern), 'NULL')||'''' + ,'pTableId => '''||nvl(to_char(pTableId), 'NULL')||'''' + ,'pTemplateTableName => '''||nvl(to_char(pTemplateTableName), 'NULL')||'''' + ,'pContainerFileKey => '''||nvl(to_char(pContainerFileKey), 'NULL')||'''' + ,'pEncoding => '''||nvl(to_char(pEncoding), 'NULL')||'''' -- MARS-1049: NOWY + ,'pIsWorkflowSuccessRequired => '''||nvl(to_char(pIsWorkflowSuccessRequired), 'NULL')||'''' -- MARS-1409 + ,'pIsArchiveEnabled => '''||nvl(to_char(pIsArchiveEnabled), 'NULL')||'''' -- MARS-828 + ,'pIsKeptInTrash => '''||nvl(to_char(pIsKeptInTrash), 'NULL')||'''' -- MARS-828 + ,'pArchivalStrategy => '''||nvl(to_char(pArchivalStrategy), 'NULL')||'''' -- MARS-828 + ,'pMinimumAgeMonths => '''||nvl(to_char(pMinimumAgeMonths), 'NULL')||'''' -- MARS-828 + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start','INFO', vParameters); + INSERT INTO CT_MRDS.A_SOURCE_FILE_CONFIG(A_SOURCE_KEY, SOURCE_FILE_TYPE, SOURCE_FILE_ID, SOURCE_FILE_DESC, SOURCE_FILE_NAME_PATTERN, TABLE_ID, TEMPLATE_TABLE_NAME, CONTAINER_FILE_KEY, ENCODING, IS_WORKFLOW_SUCCESS_REQUIRED, IS_ARCHIVE_ENABLED, IS_KEPT_IN_TRASH, ARCHIVAL_STRATEGY, MINIMUM_AGE_MONTHS) + VALUES (pSourceKey, pSourceFileType, pSourceFileId, pSourceFileDesc, pSourceFileNamePattern, pTableId, pTemplateTableName, pContainerFileKey, pEncoding, pIsWorkflowSuccessRequired, pIsArchiveEnabled, pIsKeptInTrash, pArchivalStrategy, pMinimumAgeMonths); + COMMIT; + ENV_MANAGER.LOG_PROCESS_EVENT('End','INFO',vParameters); + EXCEPTION + + WHEN OTHERS THEN + IF SQLCODE = -2291 THEN + ENV_MANAGER.MSG_MISSING_SOURCE_KEY := 'The Source with A_SOURCE_KEY: '''||pSourceKey||''' not found in parent table A_SOURCE.' + ||cgBL||'First add a record to A_SOURCE:' + ||cgBL||'call file_manager.add_source(pSourceKey => '''||pSourceKey||''', pSourceName => ''...'')'; + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.MSG_MISSING_SOURCE_KEY, 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_MISSING_SOURCE_KEY, ENV_MANAGER.MSG_MISSING_SOURCE_KEY); + ELSE + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END IF; + + END ADD_SOURCE_FILE_CONFIG; + + ---------------------------------------------------------------------------------------------------- + + PROCEDURE ADD_COLUMN_DATE_FORMAT ( + pTemplateTableName IN CT_MRDS.A_COLUMN_DATE_FORMAT.TEMPLATE_TABLE_NAME%TYPE + ,pColumnName IN CT_MRDS.A_COLUMN_DATE_FORMAT.COLUMN_NAME%TYPE + ,pDateFormat IN CT_MRDS.A_COLUMN_DATE_FORMAT.DATE_FORMAT%TYPE + ) IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( 'pTemplateTableName => '''||nvl(pTemplateTableName, 'NULL')||'''' + ,'pColumnName => '''||nvl(pColumnName, 'NULL')||'''' + ,'pDateFormat => '''||nvl(pDateFormat, 'NULL')||'''' + )); + + ENV_MANAGER.LOG_PROCESS_EVENT('Start','DEBUG', vParameters); + INSERT INTO CT_MRDS.A_COLUMN_DATE_FORMAT(TEMPLATE_TABLE_NAME, COLUMN_NAME, DATE_FORMAT) + VALUES (pTemplateTableName, pColumnName, pDateFormat); + COMMIT; + + ENV_MANAGER.LOG_PROCESS_EVENT('End','DEBUG',vParameters); + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_EVENT(ENV_MANAGER.GET_ERROR_STACK(pFormat => 'TABLE', pCode=> SQLCODE), 'ERROR', vParameters); + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + END ADD_COLUMN_DATE_FORMAT; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUCKET_URI(pBucketArea VARCHAR2) + RETURN VARCHAR2 + IS + BEGIN + CASE pBucketArea + WHEN 'INBOX' THEN RETURN ENV_MANAGER.gvInboxBucketUri; + WHEN 'ODS' THEN RETURN ENV_MANAGER.gvDataBucketUri; + WHEN 'DATA' THEN RETURN ENV_MANAGER.gvDataBucketUri; + WHEN 'ARCHIVE' THEN RETURN ENV_MANAGER.gvArchiveBucketUri; + ELSE + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_INVALID_BUCKET_AREA, + ENV_MANAGER.MSG_INVALID_BUCKET_AREA || ' Provided: ''' || pBucketArea || ''''); + END CASE; + END; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DET_SOURCE_FILE_CONFIG_INFO ( + pSourceFileConfigKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY%TYPE + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2 + ---- + -- Function to get info about File Configuration entry + IS + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vContainerFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vCount PLS_INTEGER := 0; + addColHeader BOOLEAN := TRUE; + vMsgTmp VARCHAR2(32000):= ''; + CURSOR cColumnFormat(vTableName A_COLUMN_DATE_FORMAT.TEMPLATE_TABLE_NAME%TYPE) IS + SELECT * + FROM CT_MRDS.A_COLUMN_DATE_FORMAT + WHERE TEMPLATE_TABLE_NAME = vTableName; + vColumnDateFormat A_COLUMN_DATE_FORMAT%ROWTYPE; + + FUNCTION FORMAT_CONFIG( pSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE, + pTypeInfo VARCHAR2 DEFAULT 'File Configuration', + pLevel VARCHAR2 DEFAULT '') + RETURN VARCHAR2 + IS + vMsg VARCHAR2(9999); + BEGIN + vMsg := '' + ||cgBL||pLevel||''||'Details about '||pTypeInfo||':' + ||cgBL||pLevel||''||'--------------------------------' + ||cgBL||pLevel||'A_SOURCE_FILE_CONFIG_KEY = '||pSourceFileConfig.A_SOURCE_FILE_CONFIG_KEY + ||cgBL||pLevel||'A_SOURCE_KEY = '||pSourceFileConfig.A_SOURCE_KEY + ||cgBL||pLevel||'SOURCE_FILE_TYPE = '||pSourceFileConfig.SOURCE_FILE_TYPE + ||cgBL||pLevel||'SOURCE_FILE_ID = '||pSourceFileConfig.SOURCE_FILE_ID + ||cgBL||pLevel||'SOURCE_FILE_DESC = '||pSourceFileConfig.SOURCE_FILE_DESC + ||cgBL||pLevel||'SOURCE_FILE_NAME_PATTERN = '||pSourceFileConfig.SOURCE_FILE_NAME_PATTERN + ||cgBL||pLevel||'TABLE_ID = '||pSourceFileConfig.TABLE_ID + ||cgBL||pLevel||'TEMPLATE_TABLE_NAME = '||pSourceFileConfig.TEMPLATE_TABLE_NAME + ||cgBL||pLevel||'CONTAINER_FILE_KEY = '||pSourceFileConfig.CONTAINER_FILE_KEY + ||cgBL||pLevel||'ODS_SCHEMA_NAME = '||pSourceFileConfig.ODS_SCHEMA_NAME + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_DAYS = '||pSourceFileConfig.ARCHIVE_THRESHOLD_DAYS + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_FILES_COUNT = '||pSourceFileConfig.ARCHIVE_THRESHOLD_FILES_COUNT + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_BYTES_SUM = '||pSourceFileConfig.ARCHIVE_THRESHOLD_BYTES_SUM + ||cgBL||pLevel||'ARCHIVE_THRESHOLD_ROWS_COUNT = '||pSourceFileConfig.ARCHIVE_THRESHOLD_ROWS_COUNT + ||cgBL||pLevel||'HOURS_TO_EXPIRE_STATISTICS = '||pSourceFileConfig.HOURS_TO_EXPIRE_STATISTICS + ||cgBL||pLevel||'ENCODING = '||pSourceFileConfig.ENCODING + ||cgBL||pLevel||'IS_ARCHIVE_ENABLED = '||pSourceFileConfig.IS_ARCHIVE_ENABLED + ||cgBL||pLevel||'IS_KEPT_IN_TRASH = '||pSourceFileConfig.IS_KEPT_IN_TRASH + ||cgBL||pLevel||'ARCHIVAL_STRATEGY = '||pSourceFileConfig.ARCHIVAL_STRATEGY + ||cgBL||pLevel||'MINIMUM_AGE_MONTHS = '||pSourceFileConfig.MINIMUM_AGE_MONTHS + ||cgBL||pLevel||'IS_WORKFLOW_SUCCESS_REQUIRED = '||pSourceFileConfig.IS_WORKFLOW_SUCCESS_REQUIRED + + ||cgBL||pLevel||''||'--------------------------------' + ; + RETURN vMsg; + END FORMAT_CONFIG; + + BEGIN + vMsgTmp := ''; + -- Get Main Config + BEGIN + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE A_SOURCE_FILE_CONFIG_KEY = pSourceFileConfigKey; + + vMsgTmp := FORMAT_CONFIG(pSourceFileConfig => vSourceFileConfig, pTypeInfo => 'File Configuration'); + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := 'There is no config entry in A_SOURCE_FILE_CONFIG where A_SOURCE_FILE_CONFIG_KEY = '||pSourceFileConfigKey; + RETURN vMsgTmp; + END; + + -- Get Container Config + IF pIncludeContainerInfo > 0 THEN + BEGIN + SELECT * + INTO vContainerFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG + WHERE A_SOURCE_FILE_CONFIG_KEY = vSourceFileConfig.CONTAINER_FILE_KEY; + + vMsgTmp := vMsgTmp || cgBL || FORMAT_CONFIG(pSourceFileConfig => vContainerFileConfig, pTypeInfo => 'related Container Config', pLevel => ' '); + + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := vMsgTmp|| cgBL || 'There is no CONTAINER config entry in A_SOURCE_FILE_CONFIG.'; + -- RETURN vMsgTmp; + END; + END IF; + + -- Get Column Date Format Config + IF pIncludeColumnFormatInfo > 0 THEN + BEGIN + OPEN cColumnFormat(vTableName => vSourceFileConfig.TEMPLATE_TABLE_NAME); + LOOP + FETCH cColumnFormat INTO vColumnDateFormat; + IF cColumnFormat%FOUND AND addColHeader THEN + vCount := 1; + vMsgTmp := vMsgTmp||cgBL|| cgBL || ' Column Date Format config entries:'; + vMsgTmp := vMsgTmp||cgBL||''||' --------------------------------'; + addColHeader := FALSE; + END IF; + EXIT WHEN cColumnFormat%NOTFOUND; + vMsgTmp := vMsgTmp + ||cgBL||' TEMPLATE_TABLE_NAME = '||vColumnDateFormat.TEMPLATE_TABLE_NAME + ||cgBL||' COLUMN_NAME = '||vColumnDateFormat.COLUMN_NAME + ||cgBL||' DATE_FORMAT = '||vColumnDateFormat.DATE_FORMAT + ||cgBL||''||' --------------------------------'; + END LOOP; + If vCount=0 THEN + vMsgTmp := vMsgTmp || cgBL || 'There is no Column Date Format config entries in A_COLUMN_DATE_FORMAT where TEMPLATE_TABLE_NAME = '||NVL(vSourceFileConfig.TEMPLATE_TABLE_NAME,'NULL'); + END IF; + CLOSE cColumnFormat; + END; + END IF; + + RETURN vMsgTmp; + EXCEPTION + + WHEN OTHERS THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END GET_DET_SOURCE_FILE_CONFIG_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DET_SOURCE_FILE_RECEIVED_INFO ( + pSourceFileReceivedKey IN CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE + ,pIncludeConfigInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2 + ---- + -- Function to get info about File Received entry + IS + vSourceFileReceived CT_MRDS.A_SOURCE_FILE_RECEIVED%ROWTYPE; + vMsgTmp VARCHAR2(32000):= ''; + BEGIN + + BEGIN + SELECT * + INTO vSourceFileReceived + FROM CT_MRDS.A_SOURCE_FILE_RECEIVED + WHERE A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + vMsgTmp := '' + ||cgBL||''||'Details about received file:' + ||cgBL||''||'--------------------------------' + ||cgBL||'A_SOURCE_FILE_RECEIVED_KEY = '||vSourceFileReceived.A_SOURCE_FILE_RECEIVED_KEY + ||cgBL||'A_SOURCE_FILE_CONFIG_KEY = '||vSourceFileReceived.A_SOURCE_FILE_CONFIG_KEY + ||cgBL||'SOURCE_FILE_NAME = '||vSourceFileReceived.SOURCE_FILE_NAME + ||cgBL||'CHECKSUM = '||vSourceFileReceived.CHECKSUM + ||cgBL||'CREATED = '||vSourceFileReceived.CREATED + ||cgBL||'BYTES = '||vSourceFileReceived.BYTES + ||cgBL||'RECEPTION_DATE = '||vSourceFileReceived.RECEPTION_DATE + ||cgBL||'PROCESSING_STATUS = '||vSourceFileReceived.PROCESSING_STATUS + ||cgBL||'EXTERNAL_TABLE_NAME = '||vSourceFileReceived.EXTERNAL_TABLE_NAME + ||cgBL||'PARTITION_YEAR = '||vSourceFileReceived.PARTITION_YEAR + ||cgBL||'PARTITION_MONTH = '||vSourceFileReceived.PARTITION_MONTH + ||cgBL||''||'--------------------------------' + ; + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := 'There is no data in A_SOURCE_FILE_RECEIVED where A_SOURCE_FILE_RECEIVED_KEY = '||pSourceFileReceivedKey; + RETURN vMsgTmp; + END; + + IF pIncludeConfigInfo>0 THEN + vMsgTmp := vMsgTmp || cgBL || CT_MRDS.FILE_MANAGER.GET_DET_SOURCE_FILE_CONFIG_INFO(vSourceFileReceived.A_SOURCE_FILE_CONFIG_KEY,pIncludeContainerInfo,pIncludeColumnFormatInfo); + END IF; + RETURN vMsgTmp; + EXCEPTION + + WHEN OTHERS THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END GET_DET_SOURCE_FILE_RECEIVED_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_DET_USER_LOAD_OPERATIONS ( + pOperationId PLS_INTEGER + ) RETURN VARCHAR2 + ---- + -- Function to get info about File Received entry + IS + vUserLoadOperations USER_LOAD_OPERATIONS%ROWTYPE; + vMsgTmp VARCHAR2(32000):= ''; + BEGIN + + BEGIN + SELECT * + INTO vUserLoadOperations + FROM USER_LOAD_OPERATIONS + WHERE id = pOperationId; + + vMsgTmp := '' + ||''||'Details about USER_LOAD_OPERATIONS where ID = '||pOperationId + ||cgBL||''||'--------------------------------' + ||cgBL||'ID = '||vUserLoadOperations.ID + ||cgBL||'TYPE = '||vUserLoadOperations.TYPE + ||cgBL||'SID = '||vUserLoadOperations.SID + ||cgBL||'SERIAL# = '||vUserLoadOperations.SERIAL# + ||cgBL||'START_TIME = '||vUserLoadOperations.START_TIME + ||cgBL||'UPDATE_TIME = '||vUserLoadOperations.UPDATE_TIME + ||cgBL||'STATUS = '||vUserLoadOperations.STATUS + ||cgBL||'OWNER_NAME = '||vUserLoadOperations.OWNER_NAME + ||cgBL||'TABLE_NAME = '||vUserLoadOperations.TABLE_NAME + ||cgBL||'PARTITION_NAME = '||vUserLoadOperations.PARTITION_NAME + ||cgBL||'SUBPARTITION_NAME = '||vUserLoadOperations.SUBPARTITION_NAME + ||cgBL||'FILE_URI_LIST = '||vUserLoadOperations.FILE_URI_LIST + ||cgBL||'ROWS_LOADED = '||vUserLoadOperations.ROWS_LOADED + ||cgBL||'LOGFILE_TABLE = '||vUserLoadOperations.LOGFILE_TABLE + ||cgBL||'BADFILE_TABLE = '||vUserLoadOperations.BADFILE_TABLE + ||cgBL||'STATUS_TABLE = '||vUserLoadOperations.STATUS_TABLE + ||cgBL||'TEMPEXT_TABLE = '||vUserLoadOperations.TEMPEXT_TABLE + ||cgBL||'CREDENTIAL_NAME = '||vUserLoadOperations.CREDENTIAL_NAME + ||cgBL||'EXPIRATION_TIME = '||vUserLoadOperations.EXPIRATION_TIME + ||cgBL||''||'--------------------------------' + ; + EXCEPTION + WHEN NO_DATA_FOUND THEN + vMsgTmp := 'There is no data in USER_LOAD_OPERATIONS where ID = '||pOperationId; + RETURN vMsgTmp; + END; + + RETURN vMsgTmp; + EXCEPTION + + WHEN OTHERS THEN + RAISE_APPLICATION_ERROR(ENV_MANAGER.CODE_UNKNOWN, ENV_MANAGER.GET_ERROR_STACK(pFormat => 'OUTPUT', pCode=> SQLCODE)); + + END GET_DET_USER_LOAD_OPERATIONS; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION ANALYZE_VALIDATION_ERRORS( + pSourceFileReceivedKey IN NUMBER + ) RETURN VARCHAR2 + IS + vSourceFileReceived CT_MRDS.A_SOURCE_FILE_RECEIVED%ROWTYPE; + vSourceFileConfig CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + vValidationLogTable VARCHAR2(128); + vTemplateSchema VARCHAR2(128); + vTemplateTable VARCHAR2(128); + vCsvFileUri VARCHAR2(4000); + vParameters VARCHAR2(4000); + vResult VARCHAR2(32000); + BEGIN + vParameters := ENV_MANAGER.FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST( + 'pSourceFileReceivedKey => ' || pSourceFileReceivedKey + )); + ENV_MANAGER.LOG_PROCESS_EVENT('Start', 'DEBUG', vParameters); + + -- Get file and config information + BEGIN + -- Get source file received data first + SELECT * + INTO vSourceFileReceived + FROM CT_MRDS.A_SOURCE_FILE_RECEIVED sfr + WHERE sfr.A_SOURCE_FILE_RECEIVED_KEY = pSourceFileReceivedKey; + + -- Get source file config data + SELECT * + INTO vSourceFileConfig + FROM CT_MRDS.A_SOURCE_FILE_CONFIG sfc + WHERE sfc.A_SOURCE_FILE_CONFIG_KEY = vSourceFileReceived.A_SOURCE_FILE_CONFIG_KEY; + EXCEPTION + WHEN NO_DATA_FOUND THEN + ENV_MANAGER.LOG_PROCESS_ERROR('Source file or config not found for key: ' || pSourceFileReceivedKey, vParameters); + RETURN 'Error: Source file with key ' || pSourceFileReceivedKey || ' not found in A_SOURCE_FILE_RECEIVED table'; + END; + + -- Extract template schema and table from template table name + vTemplateSchema := REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME, '^([^.]+)'); + vTemplateTable := REGEXP_SUBSTR(vSourceFileConfig.TEMPLATE_TABLE_NAME, '\.(.+)$', 1, 1, NULL, 1); + + -- Build CSV file URI + vCsvFileUri := ENV_MANAGER.gvInboxBucketUri || + 'INBOX/' || vSourceFileConfig.A_SOURCE_KEY || '/' || + vSourceFileConfig.SOURCE_FILE_ID || '/' || + vSourceFileConfig.TABLE_ID || '/' || + vSourceFileReceived.SOURCE_FILE_NAME; + + -- Find validation log table (most recent one with errors) + -- Look for validation log tables related to this external table + BEGIN + SELECT table_name + INTO vValidationLogTable + FROM ( + SELECT table_name, + REGEXP_SUBSTR(table_name, '\$([0-9]+)_', 1, 1, NULL, 1) as log_number + FROM USER_TABLES + WHERE table_name LIKE 'VALIDATE$%_LOG' + ORDER BY TO_NUMBER(log_number) DESC + ) + WHERE ROWNUM = 1; + EXCEPTION + WHEN NO_DATA_FOUND THEN + -- If no validation log tables found, use a default name + vValidationLogTable := 'VALIDATE$999_LOG'; + END; + + ENV_MANAGER.LOG_PROCESS_EVENT('Calling ENV_MANAGER.ANALYZE_VALIDATION_ERRORS with parameters: ' || + 'LogTable=' || vValidationLogTable || + ', Schema=' || vTemplateSchema || + ', Table=' || vTemplateTable || + ', URI=' || SUBSTR(vCsvFileUri, 1, 100) || '...', 'DEBUG', vParameters); + + -- Call the main function with derived parameters + vResult := ENV_MANAGER.ANALYZE_VALIDATION_ERRORS( + pValidationLogTable => vValidationLogTable, + pTemplateSchema => vTemplateSchema, + pTemplateTable => vTemplateTable, + pCsvFileUri => vCsvFileUri + ); + + ENV_MANAGER.LOG_PROCESS_EVENT('End', 'DEBUG', vParameters); + RETURN vResult; + + EXCEPTION + WHEN OTHERS THEN + ENV_MANAGER.LOG_PROCESS_ERROR('Error in ANALYZE_VALIDATION_ERRORS: ' || SQLERRM, vParameters); + RETURN 'Error analyzing validation errors: ' || SQLERRM || + cgBL || 'Source File Key: ' || pSourceFileReceivedKey || + cgBL || 'Check A_SOURCE_FILE_RECEIVED and A_SOURCE_FILE_CONFIG tables for data integrity.'; + END ANALYZE_VALIDATION_ERRORS; + + ---------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS IMPLEMENTATION + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_VERSION; + END GET_VERSION; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUILD_INFO + RETURN VARCHAR2 + IS + BEGIN + RETURN ENV_MANAGER.GET_PACKAGE_VERSION_INFO( + pPackageName => 'FILE_MANAGER', + pVersion => PACKAGE_VERSION, + pBuildDate => PACKAGE_BUILD_DATE, + pAuthor => PACKAGE_AUTHOR + ); + END GET_BUILD_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION_HISTORY + RETURN VARCHAR2 + IS + BEGIN + RETURN ENV_MANAGER.FORMAT_VERSION_HISTORY( + pPackageName => 'FILE_MANAGER', + pVersionHistory => VERSION_HISTORY + ); + END GET_VERSION_HISTORY; + + ---------------------------------------------------------------------------------------------------- + +END; + +/ + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER.pkg b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER.pkg new file mode 100644 index 0000000..157b618 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER.pkg @@ -0,0 +1,658 @@ +create or replace PACKAGE CT_MRDS.FILE_MANAGER +AUTHID CURRENT_USER +AS + /** + * General comment for package: Please put comments for functions and procedures as shown in below example. + * It is a standard. + * The structure of comment is used by GET_PACKAGE_DOCUMENTATION function + * which returns documentation text for confluence page (to Copy-Paste it). + **/ + + -- Example comment: + /** + * @name EX_PROCEDURE_NAME + * @desc Procedure description + * @example select FILE_MANAGER.EX_PROCEDURE_NAME(pParameter => 129) from dual; + * @ex_rslt Example Result + **/ + + -- Package Version Information (Semantic Versioning: MAJOR.MINOR.PATCH) + PACKAGE_VERSION CONSTANT VARCHAR2(10) := '3.6.3'; + PACKAGE_BUILD_DATE CONSTANT VARCHAR2(20) := '2026-03-17 12:30:00'; + PACKAGE_AUTHOR CONSTANT VARCHAR2(100) := 'Grzegorz Michalski'; + + -- Version History (Latest changes first) + VERSION_HISTORY CONSTANT VARCHAR2(4000) := + '3.6.3 (2026-03-17): MARS-828 - Added pIsArchiveEnabled, pIsKeptInTrash, pArchivalStrategy, pMinimumAgeMonths to ADD_SOURCE_FILE_CONFIG; FORMAT_CONFIG now shows all A_SOURCE_FILE_CONFIG columns' || CHR(13)||CHR(10) || + '3.6.2 (2026-03-17): MARS-1409 - Added pIsWorkflowSuccessRequired parameter to ADD_SOURCE_FILE_CONFIG; IS_WORKFLOW_SUCCESS_REQUIRED shown in GET_DET_SOURCE_FILE_CONFIG_INFO output' || CHR(13)||CHR(10) || + '3.6.1 (2026-03-13): MARS-1468 - Fixed CHAR/NCHAR/NVARCHAR2 column definitions in GENERATE_EXTERNAL_TABLE_PARAMS: CHAR now uses char_used/char_length semantics; NCHAR/NVARCHAR2 use char_length (data_length stores bytes in AL16UTF16)' || CHR(13)||CHR(10) || + '3.6.0 (2026-02-27): MARS-1409 - Added A_WORKFLOW_HISTORY_KEY tracking in A_SOURCE_FILE_RECEIVED. Each file now stores its workflow execution key extracted during VALIDATE_SOURCE_FILE_RECEIVED' || CHR(13)||CHR(10) || + '3.5.1 (2026-02-24): Fixed TIMESTAMP field syntax in GENERATE_EXTERNAL_TABLE_PARAMS for SQL*Loader compatibility (CHAR(35) DATE_FORMAT TIMESTAMP MASK format)' || CHR(13)||CHR(10) || + '3.3.2 (2026-02-20): MARS-828 - Fixed threshold column names in GET_DET_SOURCE_FILE_CONFIG_INFO for MARS-828 compatibility' || CHR(13)||CHR(10) || + '3.3.1 (2025-11-27): MARS-1046 - Fixed ISO 8601 datetime format parsing with milliseconds and timezone (e.g., 2012-03-02T14:16:23.798+01:00)' || CHR(13)||CHR(10) || + '3.3.0 (2025-11-26): MARS-1056 - Fixed VARCHAR2 definitions in GENERATE_EXTERNAL_TABLE_PARAMS to preserve CHAR/BYTE semantics from template tables' || CHR(13)||CHR(10) || + '3.2.1 (2025-11-24): MARS-1049 - Added pEncoding parameter support for CSV character set specification' || CHR(13)||CHR(10) || + '3.2.0 (2025-10-22): Added package versioning system using centralized ENV_MANAGER functions' || CHR(13)||CHR(10) || + '3.1.0 (2025-10-20): Enhanced PROCESS_SOURCE_FILE with 6-step validation workflow' || CHR(13)||CHR(10) || + '3.0.0 (2025-10-15): Separated export procedures into dedicated DATA_EXPORTER package' || CHR(13)||CHR(10) || + '2.5.0 (2025-10-10): Added DELETE_SOURCE_CASCADE for safe configuration removal' || CHR(13)||CHR(10) || + '2.0.0 (2025-09-25): Added official path patterns support (INBOX 3-level, ODS 2-level, ARCHIVE 2-level)' || CHR(13)||CHR(10) || + '1.0.0 (2025-09-01): Initial release with file processing and validation capabilities'; + + TYPE tSourceFileReceived IS RECORD + ( + A_SOURCE_FILE_RECEIVED_KEY CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE, + A_SOURCE_FILE_CONFIG_KEY CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_CONFIG_KEY%TYPE, + SOURCE_FILE_PREFIX_INBOX VARCHAR2(430), + SOURCE_FILE_PREFIX_ODS VARCHAR2(430), + SOURCE_FILE_PREFIX_QUARANTINE VARCHAR2(430), + SOURCE_FILE_PREFIX_ARCHIVE VARCHAR2(430), + SOURCE_FILE_NAME CT_MRDS.A_SOURCE_FILE_RECEIVED.SOURCE_FILE_NAME%TYPE, + RECEPTION_DATE CT_MRDS.A_SOURCE_FILE_RECEIVED.RECEPTION_DATE%TYPE, + PROCESSING_STATUS CT_MRDS.A_SOURCE_FILE_RECEIVED.PROCESSING_STATUS%TYPE, + EXTERNAL_TABLE_NAME CT_MRDS.A_SOURCE_FILE_RECEIVED.EXTERNAL_TABLE_NAME%TYPE + ); + + cgBL CONSTANT VARCHAR2(2) := CHR(13)||CHR(10); + vgSourceFileConfigKey PLS_INTEGER; + vgMsgTmp VARCHAR2(32000); + + + + --------------------------------------------------------------------------------------------------------------------------- + --------------------------------------------------------------------------------------------------------------------------- + + /** + * @name GET_SOURCE_FILE_CONFIG + * @desc Get source file type by matching the source file name against source file type naming patterns + * or by specifying the id of a received source file. + * @example ... + * @ex_rslt "CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE" + **/ + FUNCTION GET_SOURCE_FILE_CONFIG(pFileUri IN VARCHAR2 DEFAULT NULL + , pSourceFileReceivedKey IN NUMBER DEFAULT NULL + , pSourceFileConfigKey IN NUMBER DEFAULT NULL) + RETURN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE; + + + + /** + * @name REGISTER_SOURCE_FILE_RECEIVED + * @desc Register a newly received source file in A_SOURCE_FILE_RECEIVED table. + * This overload automatically determines source file type from the file name. + * It returns the value of A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY column for newly added record. + * @example vSourceFileReceivedKey := FILE_MANAGER.REGISTER_SOURCE_FILE_RECEIVED(pSourceFileReceivedName => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv'); + * @ex_rslt 3245 + **/ + FUNCTION REGISTER_SOURCE_FILE_RECEIVED ( + pSourceFileReceivedName IN VARCHAR2 + ) + RETURN PLS_INTEGER; + + + + /** + * @name REGISTER_SOURCE_FILE_RECEIVED + * @desc Register a new new source file in A_SOURCE_FILE_RECEIVED table based on pSourceFileReceivedName and pSourceFileConfig. + * Then it returns the value of A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY column for newly added record. + * @example vSourceFileReceivedKey := FILE_MANAGER.REGISTER_SOURCE_FILE_RECEIVED( + * pSourceFileReceivedName => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv' + * ,pSourceFileConfig => ...A_SOURCE_FILE_CONFIG%ROWTYPE... ); + * @ex_rslt 3245 + **/ + FUNCTION REGISTER_SOURCE_FILE_RECEIVED ( + pSourceFileReceivedName IN VARCHAR2, + pSourceFileConfig IN CT_MRDS.A_SOURCE_FILE_CONFIG%ROWTYPE + ) + RETURN PLS_INTEGER; + + + + /** + * @name SET_SOURCE_FILE_RECEIVED_STATUS + * @desc Set status of file in A_SOURCE_FILE_RECEIVED table - PROCESSING_STATUS column + * based on A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY + * and provided value of pStatus parameter + * @example exec FILE_MANAGER.SET_SOURCE_FILE_RECEIVED_STATUS(pSourceFileReceivedKey => 377, pStatus => 'READY_FOR_INGESTION'); + **/ + PROCEDURE SET_SOURCE_FILE_RECEIVED_STATUS( + pSourceFileReceivedKey IN PLS_INTEGER, + pStatus IN VARCHAR2 + ); + + + + /** + * @name GET_EXTERNAL_TABLE_COLUMNS + * @desc Function used to get string with all table columns definitions based on pTargetTableTemplate "TEMPLATE TABLE" name. + * It used for creating "EXTERNAL TABLE" using CREATE_EXTERNAL_TABLE procedure. + * @example select FILE_MANAGER.GET_EXTERNAL_TABLE_COLUMNS(pTargetTableTemplate => 'CT_ET_TEMPLATES.LM_STANDING_FACILITIES_HEADER') from dual; + * @ex_rslt "A_KEY" NUMBER(38,0) NOT NULL ENABLE, + * "A_WORKFLOW_HISTORY_KEY" NUMBER(38,0) NOT NULL ENABLE, + * "REV_NUMBER" NUMBER(28,0), + * "REF_DATE" DATE, + * "FREE_TEXT" VARCHAR2(1000 CHAR), + * "MLF_BS_TOTAL" NUMBER(28,10), + * "DF_BS_TOTAL" NUMBER(28,10), + * "MLF_SF_TOTAL" NUMBER(28,10), + * "DF_SF_TOTAL" NUMBER(28,10) + **/ + FUNCTION GET_EXTERNAL_TABLE_COLUMNS ( + pTargetTableTemplate IN VARCHAR2 + ) + RETURN CLOB; + + + + /** + * @name CREATE_EXTERNAL_TABLE + * @desc A wrapper procedure for DBMS_CLOUD.CREATE_EXTERNAL_TABLE which creates External Table + * MARS-1049: Added pEncoding parameter for CSV character set specification + * @param pEncoding - Character set encoding for CSV files (e.g., 'UTF8', 'WE8MSWIN1252') + * If provided, adds CHARACTERSET clause to external table definition + * @example + * begin + * FILE_MANAGER.CREATE_EXTERNAL_TABLE( + * pTableName => 'STANDING_FACILITIES_HEADER', + * pTemplateTableName => 'CT_ET_TEMPLATES.LM_STANDING_FACILITIES_HEADER', + * pPrefix => 'ODS/LM/STANDING_FACILITIES_HEADER/', + * pBucketUri => 'https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/frcnomajoc7v/b/mrds_data_tst/o/', + * pFileName => NULL, + * pDelimiter => ',', + * pEncoding => 'UTF8' + * ); + * end; + **/ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL -- MARS-1049: NOWY PARAMETR + ); + + + + /** + * @name CREATE_EXTERNAL_TABLE + * @desc Creates External Table for single file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * @example exec FILE_MANAGER.CREATE_EXTERNAL_TABLE(pSourceFileReceivedKey => 377);; + **/ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pSourceFileReceivedKey IN NUMBER + ); + + + + /** + * @name VALIDATE_SOURCE_FILE_RECEIVED + * @desc A wrapper procedure for DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE + * It validate External table build upon single file + * provided by pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * @example exec FILE_MANAGER.VALIDATE_SOURCE_FILE_RECEIVED(pSourceFileReceivedKey => 377); + **/ + PROCEDURE VALIDATE_SOURCE_FILE_RECEIVED + ( + pSourceFileReceivedKey IN NUMBER + ); + + + /** + * @name VALIDATE_EXTERNAL_TABLE + * @desc A wrapper function for DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE. + * It validates External Table provided by parameter pTableName. + * It returns: PASSED or FAILED. + * @example + * declare + * vStatus VARCHAR2(100); + * begin + * vStatus := FILE_MANAGER.VALIDATE_EXTERNAL_TABLE(pTableName => 'STANDING_FACILITIES_HEADER'); + * DBMS_OUTPUT.PUT_LINE('vStatus = '||vStatus); + * end; + * + * @ex_rslt FAILED + **/ + FUNCTION VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2; + + + /** + * @name S_VALIDATE_EXTERNAL_TABLE + * @desc A function which checks if SELECT query reterns any rows. + * It trys to selects External Table provided by parameter pTableName. + * It returns: PASSED or FAILED. + * @example + * declare + * vStatus VARCHAR2(100); + * begin + * vStatus := FILE_MANAGER.S_VALIDATE_EXTERNAL_TABLE(pTableName => 'STANDING_FACILITIES_HEADER'); + * DBMS_OUTPUT.PUT_LINE('vStatus = '||vStatus); + * end; + * + * @ex_rslt PASSED + **/ + FUNCTION S_VALIDATE_EXTERNAL_TABLE(pTableName IN VARCHAR2) + RETURN VARCHAR2; + + + + /** + * @name DROP_EXTERNAL_TABLE + * @desc It drops External Table for single file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * @example exec FILE_MANAGER.DROP_EXTERNAL_TABLE(pSourceFileReceivedKey => 377); + **/ + PROCEDURE DROP_EXTERNAL_TABLE ( + pSourceFileReceivedKey IN NUMBER + ); + + + + /** + * @name COPY_FILE + * @desc It copies file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * into destination provided by pDestination parameter. + * pDestination parameter allowed values are: 'ODS' + * @example exec FILE_MANAGER.COPY_FILE(pSourceFileReceivedKey => 377, pDestination => 'ODS'); + **/ + PROCEDURE COPY_FILE( + pSourceFileReceivedKey IN NUMBER, + pDestination IN VARCHAR2 + ); + + + + + /** + * @name MOVE_FILE + * @desc It moves file provided by + * pSourceFileReceivedKey parameter (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY) + * into destination provided by pDestination parameter. + * pDestination parameter allowed values are: 'ODS', 'QUARANTINE' + * @example exec FILE_MANAGER.MOVE_FILE(pSourceFileReceivedKey => 377, pDestination => 'ODS'); + **/ + PROCEDURE MOVE_FILE( + pSourceFileReceivedKey IN NUMBER, + pDestination IN VARCHAR2 + ); + + + + /** + * @name DELETE_FOLDER_CONTENTS + * @desc It deletes all files from specified folder in the cloud storage. + * The procedure lists all objects in the specified folder prefix and deletes them one by one. + * pBucketArea parameter specifies which bucket to use: 'INBOX', 'DATA', 'ARCHIVE' + * pFolderPrefix parameter specifies the folder path within the bucket (e.g., 'C2D/UC_DISSEM/UC_NMA_DISSEM/') + * @example exec FILE_MANAGER.DELETE_FOLDER_CONTENTS(pBucketArea => 'INBOX', pFolderPrefix => 'C2D/UC_DISSEM/UC_NMA_DISSEM/'); + **/ + PROCEDURE DELETE_FOLDER_CONTENTS( + pBucketArea IN VARCHAR2, + pFolderPrefix IN VARCHAR2 + ); + + + + + /** + * @name PROCESS_SOURCE_FILE + * @desc It process file provided by pSourceFileReceivedName parameter. + * Ubmrella procedure that calls: + * - REGISTER_SOURCE_FILE_RECEIVED; + * - CREATE_EXTERNAL_TABLE; + * - VALIDATE_SOURCE_FILE_RECEIVED; + * - DROP_EXTERNAL_TABLE; + * - MOVE_FILE; + * @example exec FILE_MANAGER.PROCESS_SOURCE_FILE(pSourceFileReceivedName => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv'); + **/ + PROCEDURE PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + ; + + + + /** + * @name PROCESS_SOURCE_FILE + * @desc It process file provided by pSourceFileReceivedName parameter and return processing result value. + * It returns (success/failure) => 0 / -(value). + * Ubmrella function that calls PROCESS_SOURCE_FILE procedure. + * @example + * declare + * vResult PLS_INTEGER; + * begin + * vResult := CT_MRDS.FILE_MANAGER.PROCESS_SOURCE_FILE(PSOURCEFILERECEIVEDNAME => 'INBOX/C2D/UC_DISSEM/UC_NMA_DISSEM/UC_NMA_DISSEM-277740.csv'); + * DBMS_OUTPUT.PUT_LINE('vResult = ' || vResult); + * end; + * @ex_rslt 0 + * -20021 + **/ + FUNCTION PROCESS_SOURCE_FILE(pSourceFileReceivedName IN VARCHAR2) + RETURN PLS_INTEGER; + + + + /** + * @name GET_DATE_FORMAT + * @desc Returns date format for specified template table name and column name. + * Date is taken from configuration A_COLUMN_DATE_FORMAT table. + * @example select FILE_MANAGER.GET_DATE_FORMAT( + * pTemplateTableName => 'STANDING_FACILITIES_HEADER', + * pColumnName => 'SNAPSHOT_DATE') + * from dual; + * @ex_rslt DD/MM/YYYY HH24:MI:SS + **/ + FUNCTION GET_DATE_FORMAT( + pTemplateTableName IN VARCHAR2, + pColumnName IN VARCHAR2 + ) RETURN VARCHAR2; + + + + /** + * @name GENERATE_EXTERNAL_TABLE_PARAMS + * @desc It builds two strings: pColumnList and pFieldList for specified Template Table name, by parameter: pTemplateTableName. + * @example + * declare + * vColumnList CLOB; + * vFieldList CLOB; + * begin + * FILE_MANAGER.GENERATE_EXTERNAL_TABLE_PARAMS ( + * pTemplateTableName => 'CT_ET_TEMPLATES.LM_STANDING_FACILITIES_HEADER' + * ,pColumnList => vColumnList + * ,pFieldList => vFieldList + * ); + * DBMS_OUTPUT.PUT_LINE('vColumnList = '||vColumnList); + * DBMS_OUTPUT.PUT_LINE('vFieldList = '||vFieldList); + * end; + * / + **/ + PROCEDURE GENERATE_EXTERNAL_TABLE_PARAMS ( + pTemplateTableName IN VARCHAR2, + pColumnList OUT CLOB, + pFieldList OUT CLOB + ); + + + + + + /** + * @name ADD_SOURCE + * @desc Insert a new record to A_SOURCE table. + * pSourceKey is a PRIMARY KEY value. + **/ + PROCEDURE ADD_SOURCE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE, + pSourceName IN CT_MRDS.A_SOURCE.SOURCE_NAME%TYPE + ); + + + + /** + * @name DELETE_SOURCE_CASCADE + * @desc Safely deletes a SOURCE specified by pSourceKey parameter from A_SOURCE table and all dependent tables: + * - A_SOURCE_FILE_CONFIG + * - A_SOURCE_FILE_RECEIVED + * - A_COLUMN_DATE_FORMAT (only if template table is not shared with other source systems) + * The procedure checks if template tables are shared before deleting date format configurations. + * If a template table is used by multiple source systems, date formats are preserved. + * @example CALL CT_MRDS.FILE_MANAGER.DELETE_SOURCE_CASCADE(pSourceKey => 'TEST_SYS'); + **/ + PROCEDURE DELETE_SOURCE_CASCADE ( + pSourceKey IN CT_MRDS.A_SOURCE.A_SOURCE_KEY%TYPE + ); + + + + /** + * @name GET_CONTAINER_SOURCE_FILE_CONFIG_KEY + * @desc For specified parameter pSourceFileId (A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID) + * it returns A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY for related CONTAINER record. + * @example select FILE_MANAGER.GET_CONTAINER_SOURCE_FILE_CONFIG_KEY( + * pSourceFileId => 'UC_DISSEM') + * from dual; + * @ex_rslt 126 + **/ + FUNCTION GET_CONTAINER_SOURCE_FILE_CONFIG_KEY ( + pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ) RETURN PLS_INTEGER; + + + + /** + * @name GET_SOURCE_FILE_CONFIG_KEY + * @desc For specified input parameters, + * it returns A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY. + * @example select FILE_MANAGER.GET_SOURCE_FILE_CONFIG_KEY ( + * pSourceFileType => 'INPUT' + * ,pSourceFileId => 'UC_DISSEM' + * ,pTableId => 'UC_NMA_DISSEM') + * from dual; + * @ex_rslt 126 + **/ + FUNCTION GET_SOURCE_FILE_CONFIG_KEY ( + pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE DEFAULT 'INPUT' + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE + ) RETURN PLS_INTEGER; + + + + /** + * @name ADD_SOURCE_FILE_CONFIG + * @desc Insert a new record to A_SOURCE_FILE_CONFIG table. + * MARS-1049: Added pEncoding parameter for CSV character set specification. + * MARS-1409: Added pIsWorkflowSuccessRequired parameter. + * MARS-828: Added pIsArchiveEnabled, pIsKeptInTrash, pArchivalStrategy, pMinimumAgeMonths. + * @param pEncoding - Character set encoding for CSV files (e.g., 'UTF8', 'WE8MSWIN1252', 'EE8ISO8859P2') + * If NULL, no CHARACTERSET clause is added to external table definitions + * @param pIsWorkflowSuccessRequired - 'Y' (default) = archivization requires WORKFLOW_SUCCESSFUL='Y' (standard DBT flow) + * 'N' = archive regardless of workflow status (bypass for manual/non-DBT sources) + * @param pIsArchiveEnabled - 'Y' = enable automatic archivization for this config; 'N' (default) = disabled + * @param pIsKeptInTrash - 'Y' = move files to trash before purge; 'N' (default) = purge directly + * @param pArchivalStrategy - Archival strategy: 'MINIMUM_AGE_MONTHS' or NULL + * @param pMinimumAgeMonths - Minimum age in months before file eligible for archivization (used with MINIMUM_AGE_MONTHS strategy) + * @example CALL CT_MRDS.FILE_MANAGER.ADD_SOURCE_FILE_CONFIG( + * pSourceKey => 'C2D', pSourceFileType => 'INPUT', + * pSourceFileId => 'UC_DISSEM', pTableId => 'METADATA_LOADS', + * pTemplateTableName => 'CT_ET_TEMPLATES.C2D_A_UC_DISSEM_METADATA_LOADS', + * pEncoding => 'UTF8', pIsWorkflowSuccessRequired => 'Y', + * pIsArchiveEnabled => 'Y', pIsKeptInTrash => 'N', + * pArchivalStrategy => 'MINIMUM_AGE_MONTHS', pMinimumAgeMonths => 3 + * ); + **/ + PROCEDURE ADD_SOURCE_FILE_CONFIG ( + pSourceKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_KEY%TYPE + ,pSourceFileType IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_TYPE%TYPE + ,pSourceFileId IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_ID%TYPE + ,pSourceFileDesc IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_DESC%TYPE + ,pSourceFileNamePattern IN CT_MRDS.A_SOURCE_FILE_CONFIG.SOURCE_FILE_NAME_PATTERN%TYPE + ,pTableId IN CT_MRDS.A_SOURCE_FILE_CONFIG.TABLE_ID%TYPE DEFAULT NULL + ,pTemplateTableName IN CT_MRDS.A_SOURCE_FILE_CONFIG.TEMPLATE_TABLE_NAME%TYPE DEFAULT NULL + ,pContainerFileKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.CONTAINER_FILE_KEY%TYPE DEFAULT NULL + ,pEncoding IN CT_MRDS.A_SOURCE_FILE_CONFIG.ENCODING%TYPE DEFAULT NULL -- MARS-1049 + ,pIsWorkflowSuccessRequired IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_WORKFLOW_SUCCESS_REQUIRED%TYPE DEFAULT 'Y' -- MARS-1409 + ,pIsArchiveEnabled IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_ARCHIVE_ENABLED%TYPE DEFAULT 'N' -- MARS-828 + ,pIsKeptInTrash IN CT_MRDS.A_SOURCE_FILE_CONFIG.IS_KEPT_IN_TRASH%TYPE DEFAULT 'Y' -- MARS-828 + ,pArchivalStrategy IN CT_MRDS.A_SOURCE_FILE_CONFIG.ARCHIVAL_STRATEGY%TYPE DEFAULT 'THRESHOLD_BASED' -- MARS-828 + ,pMinimumAgeMonths IN CT_MRDS.A_SOURCE_FILE_CONFIG.MINIMUM_AGE_MONTHS%TYPE DEFAULT 0 -- MARS-828 + ); + + + + /** + * @name ADD_COLUMN_DATE_FORMAT + * @desc Insert a new record to A_COLUMN_DATE_FORMAT table. + **/ + PROCEDURE ADD_COLUMN_DATE_FORMAT ( + pTemplateTableName IN CT_MRDS.A_COLUMN_DATE_FORMAT.TEMPLATE_TABLE_NAME%TYPE + ,pColumnName IN CT_MRDS.A_COLUMN_DATE_FORMAT.COLUMN_NAME%TYPE + ,pDateFormat IN CT_MRDS.A_COLUMN_DATE_FORMAT.DATE_FORMAT%TYPE + ); + + + + /** + * @name GET_BUCKET_URI + * @desc Function used to get string with bucket http url. + * Possible input values for pBucketArea are: 'INBOX', 'ODS', 'DATA', 'ARCHIVE' + * @example select FILE_MANAGER.GET_BUCKET_URI(pBucketArea => 'ODS') from dual; + * @ex_rslt https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/frcnomajoc7v/b/mrds_data_tst/o/ + **/ + FUNCTION GET_BUCKET_URI(pBucketArea VARCHAR2) + RETURN VARCHAR2; + + + + /** + * @name GET_DET_SOURCE_FILE_CONFIG_INFO + * @desc Function returns details about A_SOURCE_FILE_CONFIG record + * for specified pSourceFileConfigKey (A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY). + * If pIncludeContainerInfo is <> 0 it returns additional info about related Container config record (A_SOURCE_FILE_CONFIG) + * If pIncludeColumnFormatInfo is <> 0 it returns additional info about related ColumnFormat config record (A_COLUMN_DATE_FORMAT) + * @example select FILE_MANAGER.GET_DET_SOURCE_FILE_CONFIG_INFO ( + * pSourceFileConfigKey => 128 + * ,pIncludeContainerInfo => 1 + * ,pIncludeColumnFormatInfo => 1 + * ) from dual; + * @ex_rslt + * Details about File Configuration: + * -------------------------------- + * A_SOURCE_FILE_CONFIG_KEY = 128 + * A_SOURCE_KEY = C2D + * ... + * -------------------------------- + * + * Details about related Container Config: + * -------------------------------- + * A_SOURCE_FILE_CONFIG_KEY = 126 + * A_SOURCE_KEY = C2D + * ... + * -------------------------------- + * + * Column Date Format config entries: + * -------------------------------- + * TEMPLATE_TABLE_NAME = CT_ET_TEMPLATES.C2D_UC_MA_DISSEM + * ... + * -------------------------------- + **/ + FUNCTION GET_DET_SOURCE_FILE_CONFIG_INFO ( + pSourceFileConfigKey IN CT_MRDS.A_SOURCE_FILE_CONFIG.A_SOURCE_FILE_CONFIG_KEY%TYPE + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2; + + + + /** + * @name GET_DET_SOURCE_FILE_RECEIVED_INFO + * @desc Function returns details about A_SOURCE_FILE_RECEIVED record + * for specified pSourceFileReceivedKey (A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY). + * If pIncludeConfigInfo is <> 0 it returns additional info about related Container config record (A_SOURCE_FILE_CONFIG) + * If pIncludeContainerInfo is <> 0 it returns additional info about related Container config record (A_SOURCE_FILE_CONFIG) + * If pIncludeColumnFormatInfo is <> 0 it returns additional info about related ColumnFormat config record (A_COLUMN_DATE_FORMAT) + * @example select FILE_MANAGER.GET_DET_SOURCE_FILE_RECEIVED_INFO ( + * pSourceFileReceivedKey => 377 + * ,pIncludeConfigInfo => 1 + * ,pIncludeContainerInfo => 1 + * ,pIncludeColumnFormatInfo => 1 + * ) from dual; + * + **/ + FUNCTION GET_DET_SOURCE_FILE_RECEIVED_INFO ( + pSourceFileReceivedKey IN CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE + ,pIncludeConfigInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeContainerInfo IN PLS_INTEGER DEFAULT 1 + ,pIncludeColumnFormatInfo IN PLS_INTEGER DEFAULT 1 + ) RETURN VARCHAR2; + + + + /** + * @name GET_DET_USER_LOAD_OPERATIONS + * @desc Function returns details from USER_LOAD_OPERATIONS table + * for specified pOperationId. + * @example select FILE_MANAGER.GET_DET_USER_LOAD_OPERATIONS (pOperationId => 3608) from dual; + * @ex_rslt + * Details about USER_LOAD_OPERATIONS where ID = 3608 + * -------------------------------- + * ID = 3608 + * TYPE = VALIDATE + * SID = 31260 + * SERIAL# = 52915 + * START_TIME = 2025-05-20 10.08.24.436983 EUROPE/BELGRADE + * UPDATE_TIME = 2025-05-20 10.08.24.458643 EUROPE/BELGRADE + * STATUS = FAILED + * OWNER_NAME = CT_MRDS + * TABLE_NAME = STANDING_FACILITIES_HEADER + * PARTITION_NAME = + * SUBPARTITION_NAME = + * FILE_URI_LIST = + * ROWS_LOADED = + * LOGFILE_TABLE = VALIDATE$3608_LOG + * BADFILE_TABLE = VALIDATE$3608_BAD + * STATUS_TABLE = + * TEMPEXT_TABLE = + * CREDENTIAL_NAME = + * EXPIRATION_TIME = 2025-05-22 10.08.24.436983000 EUROPE/BELGRADE + * -------------------------------- + **/ + FUNCTION GET_DET_USER_LOAD_OPERATIONS ( + pOperationId PLS_INTEGER + ) RETURN VARCHAR2; + + /** + * @name ANALYZE_VALIDATION_ERRORS + * @desc Wrapper function that analyzes validation errors for a source file using its received key. + * Automatically derives template schema, table name, CSV URI and validation log table + * from file metadata and calls ENV_MANAGER.ANALYZE_VALIDATION_ERRORS. + * @example SELECT FILE_MANAGER.ANALYZE_VALIDATION_ERRORS(63) FROM DUAL; + * @ex_rslt Detailed validation analysis report with column mismatches and solutions + **/ + FUNCTION ANALYZE_VALIDATION_ERRORS( + pSourceFileReceivedKey IN NUMBER + ) RETURN VARCHAR2; + + --------------------------------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS + --------------------------------------------------------------------------------------------------------------------------- + + /** + * @name GET_VERSION + * @desc Returns the current version number of the FILE_MANAGER package. + * Uses semantic versioning format (MAJOR.MINOR.PATCH). + * @example SELECT FILE_MANAGER.GET_VERSION() FROM DUAL; + * @ex_rslt 3.2.0 + **/ + FUNCTION GET_VERSION RETURN VARCHAR2; + + /** + * @name GET_BUILD_INFO + * @desc Returns comprehensive build information including version, build date, and author. + * Uses centralized ENV_MANAGER.GET_PACKAGE_VERSION_INFO function. + * @example SELECT FILE_MANAGER.GET_BUILD_INFO() FROM DUAL; + * @ex_rslt Package: FILE_MANAGER + * Version: 3.2.0 + * Build Date: 2025-10-22 16:30:00 + * Author: Grzegorz Michalski + **/ + FUNCTION GET_BUILD_INFO RETURN VARCHAR2; + + /** + * @name GET_VERSION_HISTORY + * @desc Returns complete version history with all releases and changes. + * Uses centralized ENV_MANAGER.FORMAT_VERSION_HISTORY function. + * @example SELECT FILE_MANAGER.GET_VERSION_HISTORY() FROM DUAL; + * @ex_rslt FILE_MANAGER Version History: + * 3.2.0 (2025-10-22): Added package versioning system... + **/ + FUNCTION GET_VERSION_HISTORY RETURN VARCHAR2; + +END; + +/ + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER_ODS.pkb b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER_ODS.pkb new file mode 100644 index 0000000..532eec2 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER_ODS.pkb @@ -0,0 +1,118 @@ +create or replace PACKAGE BODY ODS.FILE_MANAGER_ODS +AS + + /** + * CREATE_EXTERNAL_TABLE - Wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE + */ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT CT_MRDS.ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL + ) + IS + vParameters CT_MRDS.A_PROCESS_LOG.PROCEDURE_PARAMETERS%TYPE; + BEGIN + -- Log the start of the procedure + vParameters := CT_MRDS.ENV_MANAGER.FORMAT_PARAMETERS( + SYS.ODCIVARCHAR2LIST( + 'pTableName => ''' || pTableName || '''', + 'pTemplateTableName => ''' || pTemplateTableName || '''', + 'pPrefix => ''' || pPrefix || '''', + 'pBucketUri => ''' || pBucketUri || '''', + 'pFileName => ''' || NVL(pFileName, 'NULL') || '''', + 'pDelimiter => ''' || pDelimiter || '''', + 'pEncoding => ''' || NVL(pEncoding, 'NULL') || '''' + ) + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('Start FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE', 'INFO', vParameters); + + -- Call the original CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE procedure + -- This ensures all logic remains centralized in the original package + -- and ODS wrapper simply delegates execution with DEFINER rights + CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE( + pTableName => pTableName, + pTemplateTableName => pTemplateTableName, + pPrefix => pPrefix, + pBucketUri => pBucketUri, + pFileName => pFileName, + pDelimiter => pDelimiter, + pEncoding => pEncoding + ); + + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT('End FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE', 'INFO', vParameters); + + EXCEPTION + WHEN OTHERS THEN + CT_MRDS.ENV_MANAGER.LOG_PROCESS_EVENT( + 'Error in ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE: ' || SQLERRM, + 'ERROR', + vParameters + ); + RAISE; + END CREATE_EXTERNAL_TABLE; + + ---------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS IMPLEMENTATION + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_VERSION; + END GET_VERSION; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUILD_DATE + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_BUILD_DATE; + END GET_BUILD_DATE; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_AUTHOR + RETURN VARCHAR2 + IS + BEGIN + RETURN PACKAGE_AUTHOR; + END GET_AUTHOR; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_BUILD_INFO + RETURN VARCHAR2 + IS + BEGIN + RETURN CT_MRDS.ENV_MANAGER.GET_PACKAGE_VERSION_INFO( + pPackageName => 'FILE_MANAGER_ODS', + pVersion => PACKAGE_VERSION, + pBuildDate => PACKAGE_BUILD_DATE, + pAuthor => PACKAGE_AUTHOR + ); + END GET_BUILD_INFO; + + ---------------------------------------------------------------------------------------------------- + + FUNCTION GET_VERSION_HISTORY + RETURN VARCHAR2 + IS + BEGIN + RETURN CT_MRDS.ENV_MANAGER.FORMAT_VERSION_HISTORY( + pPackageName => 'FILE_MANAGER_ODS', + pVersionHistory => VERSION_HISTORY + ); + END GET_VERSION_HISTORY; + + ---------------------------------------------------------------------------------------------------- + +END FILE_MANAGER_ODS; + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER_ODS.pkg b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER_ODS.pkg new file mode 100644 index 0000000..0010af6 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/rollback_version/FILE_MANAGER_ODS.pkg @@ -0,0 +1,116 @@ +create or replace PACKAGE ODS.FILE_MANAGER_ODS +AUTHID DEFINER +AS + /** + * FILE_MANAGER_ODS - Wrapper package for CT_MRDS.FILE_MANAGER + * + * This package serves as a wrapper for CT_MRDS.FILE_MANAGER procedures. + * The key difference is that this package uses AUTHID DEFINER instead of + * AUTHID CURRENT_USER, which means all objects will be created in the ODS + * schema regardless of which user executes the procedures. + * + * This solves the execution context issue where users need to connect as + * ODS user to use CT_MRDS.FILE_MANAGER procedures. + * + * MARS-1049: Added pEncoding parameter support for CSV character set specification + */ + + -- Package Version Information (Semantic Versioning: MAJOR.MINOR.PATCH) + PACKAGE_VERSION CONSTANT VARCHAR2(10) := '2.1.0'; + PACKAGE_BUILD_DATE CONSTANT VARCHAR2(20) := '2025-11-24 11:50:00'; + PACKAGE_AUTHOR CONSTANT VARCHAR2(100) := 'Grzegorz Michalski'; + + -- Version History (Latest changes first) + VERSION_HISTORY CONSTANT VARCHAR2(4000) := + '2.1.0 (2025-11-24): MARS-1049 - Added pEncoding parameter support for CSV character set specification' || CHR(13)||CHR(10) || + '2.0.0 (2025-10-22): Added package versioning system using centralized ENV_MANAGER functions' || CHR(13)||CHR(10) || + '1.5.0 (2025-10-12): Enhanced external table creation with official path patterns support' || CHR(13)||CHR(10) || + '1.0.0 (2025-09-20): Initial release with AUTHID DEFINER wrapper for external table creation'; + + /** + * @name CREATE_EXTERNAL_TABLE + * @desc Creates external tables that can read data from Oracle Cloud Storage. + * This is a wrapper for CT_MRDS.FILE_MANAGER.CREATE_EXTERNAL_TABLE + * but uses DEFINER rights to ensure objects are created in ODS schema. + * + * MARS-1049: Added support for CSV character set encoding specification + * @param pTableName - Name of the external table to create + * @param pTemplateTableName - Template table defining the structure + * @param pPrefix - Storage path prefix in Oracle Cloud Storage + * @param pBucketUri - URI of the target bucket (default: ENV_MANAGER.gvInboxBucketUri) + * @param pFileName - Specific file name (optional) + * @param pDelimiter - Field delimiter (default: ',') + * @param pEncoding - Character set encoding for CSV files (e.g., 'UTF-8', 'WE8MSWIN1252', 'EE8ISO8859P2') + * If NULL, no CHARACTERSET clause is added to external table definition + * @example EXEC ODS.FILE_MANAGER_ODS.CREATE_EXTERNAL_TABLE( + * 'C2D_A_UC_DISSEM_METADATA_LOADS_INBOX', + * 'CT_ET_TEMPLATES.C2D_A_UC_DISSEM_METADATA_LOADS', + * 'INBOX/C2D/UC_DISSEM/A_UC_DISSEM_METADATA_LOADS', + * CT_MRDS.ENV_MANAGER.gvInboxBucketUri, + * NULL, ',', 'UTF-8' + * ); + */ + PROCEDURE CREATE_EXTERNAL_TABLE ( + pTableName IN VARCHAR2, + pTemplateTableName IN VARCHAR2, + pPrefix IN VARCHAR2, + pBucketUri IN VARCHAR2 DEFAULT CT_MRDS.ENV_MANAGER.gvInboxBucketUri, + pFileName IN VARCHAR2 DEFAULT NULL, + pDelimiter IN VARCHAR2 DEFAULT ',', + pEncoding IN VARCHAR2 DEFAULT NULL + ); + + --------------------------------------------------------------------------------------------------------------------------- + -- PACKAGE VERSION MANAGEMENT FUNCTIONS + --------------------------------------------------------------------------------------------------------------------------- + + /** + * @name GET_VERSION + * @desc Returns the current version number of the FILE_MANAGER_ODS package. + * Uses semantic versioning format (MAJOR.MINOR.PATCH). + * @example SELECT FILE_MANAGER_ODS.GET_VERSION() FROM DUAL; + * @ex_rslt 2.1.0 + **/ + FUNCTION GET_VERSION RETURN VARCHAR2; + + /** + * @name GET_BUILD_DATE + * @desc Returns the build date of the FILE_MANAGER_ODS package. + * @return VARCHAR2 - Build date in format 'YYYY-MM-DD HH24:MI:SS' + * @example SELECT ODS.FILE_MANAGER_ODS.GET_BUILD_DATE() FROM DUAL; + */ + FUNCTION GET_BUILD_DATE RETURN VARCHAR2; + + /** + * @name GET_AUTHOR + * @desc Returns the author of the FILE_MANAGER_ODS package. + * @return VARCHAR2 - Package author name + * @example SELECT ODS.FILE_MANAGER_ODS.GET_AUTHOR() FROM DUAL; + */ + FUNCTION GET_AUTHOR RETURN VARCHAR2; + + /** + * @name GET_BUILD_INFO + * @desc Returns comprehensive build information including version, build date, and author. + * Uses centralized ENV_MANAGER.GET_PACKAGE_VERSION_INFO function. + * @example SELECT FILE_MANAGER_ODS.GET_BUILD_INFO() FROM DUAL; + * @ex_rslt Package: FILE_MANAGER_ODS + * Version: 2.1.0 + * Build Date: 2025-11-24 11:50:00 + * Author: Grzegorz Michalski + **/ + FUNCTION GET_BUILD_INFO RETURN VARCHAR2; + + /** + * @name GET_VERSION_HISTORY + * @desc Returns complete version history with all releases and changes. + * Uses centralized ENV_MANAGER.FORMAT_VERSION_HISTORY function. + * @example SELECT FILE_MANAGER_ODS.GET_VERSION_HISTORY() FROM DUAL; + * @ex_rslt FILE_MANAGER_ODS Version History: + * 2.0.0 (2025-10-22): Added package versioning system... + **/ + FUNCTION GET_VERSION_HISTORY RETURN VARCHAR2; + +END FILE_MANAGER_ODS; + +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/track_package_versions.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/track_package_versions.sql new file mode 100644 index 0000000..4a20ec0 --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/track_package_versions.sql @@ -0,0 +1,96 @@ +-- =================================================================== +-- Simple Package Version Tracking Script +-- =================================================================== +-- Purpose: Track specified Oracle package versions +-- Author: Grzegorz Michalski +-- Date: 2025-11-27 +-- Version: 3.1.0 - List-Based Edition +-- +-- USAGE: +-- 1. Edit package list below (add/remove packages as needed) +-- 2. Include in your install/rollback script: @@track_package_versions.sql +-- =================================================================== + +SET SERVEROUTPUT ON; + +DECLARE + TYPE t_package_rec IS RECORD ( + owner VARCHAR2(50), + package_name VARCHAR2(50), + version VARCHAR2(50) + ); + TYPE t_packages IS TABLE OF t_package_rec; + TYPE t_string_array IS TABLE OF VARCHAR2(100); + + -- =================================================================== + -- PACKAGE LIST - Edit this array to specify packages to track + -- =================================================================== + -- Add or remove entries as needed for your MARS issue + -- Format: 'SCHEMA.PACKAGE_NAME' + -- =================================================================== + vPackageList t_string_array := t_string_array( + 'CT_MRDS.FILE_MANAGER', + 'ODS.FILE_MANAGER_ODS' + ); + -- =================================================================== + + vPackages t_packages := t_packages(); + vVersion VARCHAR2(50); + vCount NUMBER := 0; + vOwner VARCHAR2(50); + vPackageName VARCHAR2(50); + vDotPos NUMBER; +BEGIN + DBMS_OUTPUT.PUT_LINE('========================================'); + DBMS_OUTPUT.PUT_LINE('Package Version Tracking'); + DBMS_OUTPUT.PUT_LINE('========================================'); + + -- Process each package in the list + FOR i IN 1..vPackageList.COUNT LOOP + vDotPos := INSTR(vPackageList(i), '.'); + IF vDotPos > 0 THEN + vOwner := SUBSTR(vPackageList(i), 1, vDotPos - 1); + vPackageName := SUBSTR(vPackageList(i), vDotPos + 1); + + -- Get package version + BEGIN + EXECUTE IMMEDIATE 'SELECT ' || vOwner || '.' || vPackageName || '.GET_VERSION() FROM DUAL' INTO vVersion; + vPackages.EXTEND; + vPackages(vPackages.COUNT).owner := vOwner; + vPackages(vPackages.COUNT).package_name := vPackageName; + vPackages(vPackages.COUNT).version := vVersion; + + -- Track in ENV_MANAGER + BEGIN + CT_MRDS.ENV_MANAGER.TRACK_PACKAGE_VERSION( + pPackageOwner => vOwner, + pPackageName => vPackageName, + pPackageVersion => vVersion, + pPackageBuildDate => TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), + pPackageAuthor => 'Grzegorz Michalski' + ); + vCount := vCount + 1; + EXCEPTION + WHEN OTHERS THEN NULL; -- Continue even if tracking fails + END; + EXCEPTION + WHEN OTHERS THEN NULL; -- Skip packages that fail + END; + END IF; + END LOOP; + + -- Display results + IF vPackages.COUNT > 0 THEN + DBMS_OUTPUT.PUT_LINE('Packages tracked: ' || vCount || ' of ' || vPackages.COUNT); + DBMS_OUTPUT.PUT_LINE(''); + + FOR i IN 1..vPackages.COUNT LOOP + DBMS_OUTPUT.PUT_LINE(vPackages(i).owner || '.' || vPackages(i).package_name || ' = ' || vPackages(i).version); + END LOOP; + ELSE + DBMS_OUTPUT.PUT_LINE('No packages found in list'); + END IF; + + DBMS_OUTPUT.PUT_LINE('========================================'); +END; +/ diff --git a/MARS_Packages/REL04/MARS-1443-PREHOOK/verify_packages_version.sql b/MARS_Packages/REL04/MARS-1443-PREHOOK/verify_packages_version.sql new file mode 100644 index 0000000..97b6afd --- /dev/null +++ b/MARS_Packages/REL04/MARS-1443-PREHOOK/verify_packages_version.sql @@ -0,0 +1,62 @@ +-- =================================================================== +-- Universal Package Version Verification Script +-- =================================================================== +-- Purpose: Verify all tracked Oracle packages for code changes +-- Author: Grzegorz Michalski +-- Date: 2025-11-27 +-- Version: 1.0.0 +-- +-- USAGE: +-- Include at the end of install/rollback scripts: @@verify_packages_version.sql +-- +-- OUTPUT: +-- - List of all tracked packages with their current status +-- - OK: Package has not changed since last tracking +-- - WARNING: Package code changed without version update +-- =================================================================== + +SET LINESIZE 200 +SET PAGESIZE 1000 +SET FEEDBACK OFF + +PROMPT +PROMPT ======================================== +PROMPT Package Version Verification +PROMPT ======================================== +PROMPT + +COLUMN PACKAGE_OWNER FORMAT A15 +COLUMN PACKAGE_NAME FORMAT A20 +COLUMN VERSION FORMAT A10 +COLUMN STATUS FORMAT A80 + +SELECT + PACKAGE_OWNER, + PACKAGE_NAME, + PACKAGE_VERSION AS VERSION, + CT_MRDS.ENV_MANAGER.CHECK_PACKAGE_CHANGES(PACKAGE_OWNER, PACKAGE_NAME) AS STATUS +FROM ( + SELECT + PACKAGE_OWNER, + PACKAGE_NAME, + PACKAGE_VERSION, + ROW_NUMBER() OVER (PARTITION BY PACKAGE_OWNER, PACKAGE_NAME ORDER BY TRACKING_DATE DESC) AS RN + FROM CT_MRDS.A_PACKAGE_VERSION_TRACKING +) +WHERE RN = 1 +ORDER BY PACKAGE_OWNER, PACKAGE_NAME; + +PROMPT +PROMPT ======================================== +PROMPT Verification Complete +PROMPT ======================================== +PROMPT +PROMPT Legend: +PROMPT OK - Package has not changed since last tracking +PROMPT WARNING - Package code changed without version update +PROMPT +PROMPT For detailed hash information, use: +PROMPT SELECT ENV_MANAGER.GET_PACKAGE_HASH_INFO('OWNER', 'PACKAGE') FROM DUAL; +PROMPT ======================================== + +SET FEEDBACK ON