1174 lines
54 KiB
Plaintext
1174 lines
54 KiB
Plaintext
create or replace PACKAGE BODY CT_MRDS.ENV_MANAGER
|
|
AS
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
PROCEDURE INIT_ERRORS IS
|
|
BEGIN
|
|
Errors(CODE_EMPTY_FILEURI_AND_RECKEY) := Error_Record(CODE_EMPTY_FILEURI_AND_RECKEY, MSG_EMPTY_FILEURI_AND_RECKEY); -- -20001
|
|
Errors(CODE_NO_CONFIG_MATCH_FOR_FILEURI) := Error_Record(CODE_NO_CONFIG_MATCH_FOR_FILEURI, MSG_NO_CONFIG_MATCH_FOR_FILEURI); -- -20002
|
|
Errors(CODE_MULTIPLE_MATCH_FOR_SRCFILE) := Error_Record(CODE_MULTIPLE_MATCH_FOR_SRCFILE, MSG_MULTIPLE_MATCH_FOR_SRCFILE); -- -20003
|
|
Errors(CODE_MISSING_COLUMN_DATE_FORMAT) := Error_Record(CODE_MISSING_COLUMN_DATE_FORMAT, MSG_MISSING_COLUMN_DATE_FORMAT); -- -20004
|
|
Errors(CODE_MULTIPLE_COLUMN_DATE_FORMAT) := Error_Record(CODE_MULTIPLE_COLUMN_DATE_FORMAT, MSG_MULTIPLE_COLUMN_DATE_FORMAT); -- -20005
|
|
Errors(CODE_DIDNT_GET_LOAD_OPERATION_ID) := Error_Record(CODE_DIDNT_GET_LOAD_OPERATION_ID, MSG_DIDNT_GET_LOAD_OPERATION_ID); -- -20006
|
|
Errors(CODE_NO_CONFIG_FOR_RECEIVED_FILE) := Error_Record(CODE_NO_CONFIG_FOR_RECEIVED_FILE, MSG_NO_CONFIG_FOR_RECEIVED_FILE); -- -20007
|
|
Errors(CODE_MULTI_CONFIG_FOR_RECEIVED_FILE) := Error_Record(CODE_MULTI_CONFIG_FOR_RECEIVED_FILE, MSG_MULTI_CONFIG_FOR_RECEIVED_FILE); -- -20008
|
|
Errors(CODE_FILE_NOT_FOUND_ON_CLOUD) := Error_Record(CODE_FILE_NOT_FOUND_ON_CLOUD, MSG_FILE_NOT_FOUND_ON_CLOUD); -- -20009
|
|
Errors(CODE_FILE_VALIDATION_FAILED) := Error_Record(CODE_FILE_VALIDATION_FAILED, MSG_FILE_VALIDATION_FAILED); -- -20010
|
|
Errors(CODE_EXCESS_COLUMNS_DETECTED) := Error_Record(CODE_EXCESS_COLUMNS_DETECTED, MSG_EXCESS_COLUMNS_DETECTED); -- -20011
|
|
Errors(CODE_NO_CONFIG_MATCH) := Error_Record(CODE_NO_CONFIG_MATCH, MSG_NO_CONFIG_MATCH); -- -20012
|
|
Errors(CODE_UNKNOWN_PREFIX) := Error_Record(CODE_UNKNOWN_PREFIX, MSG_UNKNOWN_PREFIX); -- -20013
|
|
Errors(CODE_TABLE_NOT_EXISTS) := Error_Record(CODE_TABLE_NOT_EXISTS, MSG_TABLE_NOT_EXISTS); -- -20014
|
|
Errors(CODE_COLUMN_NOT_EXISTS) := Error_Record(CODE_COLUMN_NOT_EXISTS, MSG_COLUMN_NOT_EXISTS); -- -20015
|
|
Errors(CODE_UNSUPPORTED_DATA_TYPE) := Error_Record(CODE_UNSUPPORTED_DATA_TYPE, MSG_UNSUPPORTED_DATA_TYPE); -- -20016
|
|
Errors(CODE_MISSING_SOURCE_KEY) := Error_Record(CODE_MISSING_SOURCE_KEY, MSG_MISSING_SOURCE_KEY); -- -20017
|
|
Errors(CODE_NULL_SOURCE_FILE_CONFIG_KEY) := Error_Record(CODE_NULL_SOURCE_FILE_CONFIG_KEY, MSG_NULL_SOURCE_FILE_CONFIG_KEY); -- -20018
|
|
Errors(CODE_DUPLICATED_SOURCE_KEY) := Error_Record(CODE_DUPLICATED_SOURCE_KEY, MSG_DUPLICATED_SOURCE_KEY); -- -20019
|
|
Errors(CODE_MISSING_CONTAINER_CONFIG) := Error_Record(CODE_MISSING_CONTAINER_CONFIG, MSG_MISSING_CONTAINER_CONFIG); -- -20020
|
|
Errors(CODE_MULTIPLE_CONTAINER_ENTRIES) := Error_Record(CODE_MULTIPLE_CONTAINER_ENTRIES, MSG_MULTIPLE_CONTAINER_ENTRIES); -- -20021
|
|
Errors(CODE_WRONG_DESTINATION_PARAM) := Error_Record(CODE_WRONG_DESTINATION_PARAM, MSG_WRONG_DESTINATION_PARAM); -- -20022
|
|
Errors(CODE_FILE_NOT_EXISTS_ON_CLOUD) := Error_Record(CODE_FILE_NOT_EXISTS_ON_CLOUD, MSG_FILE_NOT_EXISTS_ON_CLOUD); -- -20023
|
|
Errors(CODE_FILE_ALREADY_REGISTERED) := Error_Record(CODE_FILE_ALREADY_REGISTERED, MSG_FILE_ALREADY_REGISTERED); -- -20024
|
|
Errors(CODE_WRONG_DATE_TIMESTAMP_FORMAT) := Error_Record(CODE_WRONG_DATE_TIMESTAMP_FORMAT, MSG_WRONG_DATE_TIMESTAMP_FORMAT); -- -20025
|
|
Errors(CODE_ENVIRONMENT_NOT_SET) := Error_Record(CODE_ENVIRONMENT_NOT_SET, MSG_ENVIRONMENT_NOT_SET); -- -20026
|
|
Errors(CODE_CONFIG_VARIABLE_NOT_SET) := Error_Record(CODE_CONFIG_VARIABLE_NOT_SET, MSG_CONFIG_VARIABLE_NOT_SET); -- -20027
|
|
Errors(CODE_NOT_INPUT_SOURCE_FILE_TYPE) := Error_Record(CODE_NOT_INPUT_SOURCE_FILE_TYPE, MSG_NOT_INPUT_SOURCE_FILE_TYPE); -- -20028
|
|
Errors(CODE_EXP_DATA_FOR_ARCH_FAILED) := Error_Record(CODE_EXP_DATA_FOR_ARCH_FAILED, MSG_EXP_DATA_FOR_ARCH_FAILED); -- -20029
|
|
Errors(CODE_RESTORE_FILE_FROM_TRASH) := Error_Record(CODE_RESTORE_FILE_FROM_TRASH, MSG_RESTORE_FILE_FROM_TRASH); -- -20030
|
|
Errors(CODE_CHANGE_STAT_TO_ARCHIVED_FAILED):= Error_Record(CODE_CHANGE_STAT_TO_ARCHIVED_FAILED, MSG_CHANGE_STAT_TO_ARCHIVED_FAILED); -- -20031
|
|
Errors(CODE_MOVE_FILE_TO_TRASH_FAILED) := Error_Record(CODE_MOVE_FILE_TO_TRASH_FAILED, MSG_MOVE_FILE_TO_TRASH_FAILED); -- -20032
|
|
Errors(CODE_DROP_EXPORTED_FILES_FAILED) := Error_Record(CODE_DROP_EXPORTED_FILES_FAILED, MSG_DROP_EXPORTED_FILES_FAILED); -- -20033
|
|
Errors(CODE_INVALID_BUCKET_AREA) := Error_Record(CODE_INVALID_BUCKET_AREA, MSG_INVALID_BUCKET_AREA); -- -20034
|
|
Errors(CODE_WORKFLOW_KEY_NULL) := Error_Record(CODE_WORKFLOW_KEY_NULL, MSG_WORKFLOW_KEY_NULL); -- -20035
|
|
Errors(CODE_MULTIPLE_WORKFLOW_KEYS) := Error_Record(CODE_MULTIPLE_WORKFLOW_KEYS, MSG_MULTIPLE_WORKFLOW_KEYS); -- -20036
|
|
Errors(CODE_INVALID_PARALLEL_DEGREE) := Error_Record(CODE_INVALID_PARALLEL_DEGREE, MSG_INVALID_PARALLEL_DEGREE); -- -20110
|
|
Errors(CODE_PARALLEL_EXECUTION_FAILED) := Error_Record(CODE_PARALLEL_EXECUTION_FAILED, MSG_PARALLEL_EXECUTION_FAILED); -- -20111
|
|
|
|
Errors(CODE_UNKNOWN) := Error_Record(CODE_UNKNOWN, MSG_UNKNOWN); -- -20999
|
|
|
|
END INIT_ERRORS;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION GET_DEFAULT_ENV
|
|
RETURN VARCHAR2
|
|
IS
|
|
vDefaultEnv CT_MRDS.a_file_manager_config.config_variable_value%TYPE;
|
|
BEGIN
|
|
select config_variable_value
|
|
into vDefaultEnv
|
|
from CT_MRDS.a_file_manager_config
|
|
where lower(environment_id)='default'
|
|
and lower(config_variable)='environmentid';
|
|
RETURN vDefaultEnv;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
RETURN NULL;
|
|
END;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
PROCEDURE INIT_VARIABLES(
|
|
pEnv VARCHAR2
|
|
) IS
|
|
BEGIN
|
|
for rec in (
|
|
select
|
|
ENVIRONMENT_ID
|
|
,REGION
|
|
,NAMESPACE
|
|
,INBOXBUCKETNAME
|
|
,DATABUCKETNAME
|
|
,ARCHIVEBUCKETNAME
|
|
,CREDENTIALNAME
|
|
,LOGGINGENABLED
|
|
,MINLOGLEVEL
|
|
,DEFAULTDATEFORMAT
|
|
,CONSOLELOGGINGENABLED
|
|
from (
|
|
select environment_id, config_variable, config_variable_value from CT_MRDS.A_FILE_MANAGER_CONFIG
|
|
where environment_id=pEnv
|
|
)
|
|
pivot (
|
|
min(config_variable_value)
|
|
for config_variable in (
|
|
'Region' as Region
|
|
,'NameSpace' as NameSpace
|
|
,'InboxBucketName' as InboxBucketName
|
|
,'DataBucketName' as DataBucketName
|
|
,'ArchiveBucketName' as ArchiveBucketName
|
|
,'CredentialName' as CredentialName
|
|
,'LoggingEnabled' as LoggingEnabled
|
|
,'MinLogLevel' as MinLogLevel
|
|
,'DefaultDateFormat' as DefaultDateFormat
|
|
,'ConsoleLoggingEnabled' as ConsoleLoggingEnabled)
|
|
)
|
|
) loop
|
|
if (rec.NAMESPACE is NULL
|
|
or rec.REGION is NULL
|
|
or rec.NAMESPACE is NULL
|
|
or rec.INBOXBUCKETNAME is NULL
|
|
or rec.DATABUCKETNAME is NULL
|
|
or rec.ARCHIVEBUCKETNAME is NULL
|
|
or rec.CREDENTIALNAME is NULL
|
|
) THEN
|
|
vgMsgTmp := MSG_CONFIG_VARIABLE_NOT_SET
|
|
||cgBL||' '||'Details about existing Configuration Variables where environment_id='||pEnv||': '
|
|
||cgBL||' '||'-------------------------'
|
|
||cgBL||' '||'Region = '||rec.Region
|
|
||cgBL||' '||'NameSpace = '||rec.NameSpace
|
|
||cgBL||' '||'InboxBucketName = '||rec.InboxBucketName
|
|
||cgBL||' '||'DataBucketName = '||rec.DataBucketName
|
|
||cgBL||' '||'ArchiveBucketName = '||rec.ArchiveBucketName
|
|
||cgBL||' '||'CredentialName = '||rec.CredentialName
|
|
;
|
|
LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR');
|
|
RAISE_APPLICATION_ERROR(CODE_CONFIG_VARIABLE_NOT_SET, vgMsgTmp);
|
|
|
|
elsif (rec.LOGGINGENABLED is NULL
|
|
or rec.MINLOGLEVEL is NULL
|
|
or rec.DEFAULTDATEFORMAT is NULL
|
|
) THEN
|
|
vgMsgTmp := 'Missing configuration variables'
|
|
||cgBL||' '||'Details about existing Configuration Variables where environment_id='||pEnv||': '
|
|
||cgBL||' '||'-------------------------'
|
|
||cgBL||' '||'LoggingEnabled = '||rec.LoggingEnabled
|
|
||cgBL||' '||'MinLogLevel = '||rec.MinLogLevel
|
|
||cgBL||' '||'DefaultDateFormat = '||rec.DefaultDateFormat
|
|
;
|
|
LOG_PROCESS_EVENT(vgMsgTmp, 'WARNING');
|
|
|
|
else
|
|
gvNameSpace := rec.NAMESPACE;
|
|
gvRegion := rec.REGION;
|
|
gvInboxBucketName := rec.INBOXBUCKETNAME;
|
|
gvDataBucketName := rec.DATABUCKETNAME;
|
|
gvArchiveBucketName := rec.ARCHIVEBUCKETNAME;
|
|
gvCredentialName := rec.CREDENTIALNAME;
|
|
gvInboxBucketUri := 'https://objectstorage.'||rec.REGION||'.oraclecloud.com/n/'||rec.NAMESPACE||'/b/'||rec.INBOXBUCKETNAME||'/o/';
|
|
gvDataBucketUri := 'https://objectstorage.'||rec.REGION||'.oraclecloud.com/n/'||rec.NAMESPACE||'/b/'||rec.DATABUCKETNAME||'/o/';
|
|
gvArchiveBucketUri := 'https://objectstorage.'||rec.REGION||'.oraclecloud.com/n/'||rec.NAMESPACE||'/b/'||rec.ARCHIVEBUCKETNAME||'/o/';
|
|
gvLoggingEnabled := rec.LOGGINGENABLED;
|
|
gvMinLogLevel := rec.MINLOGLEVEL;
|
|
gvDefaultDateFormat := rec.DEFAULTDATEFORMAT;
|
|
gvConsoleLoggingEnabled := NVL(rec.CONSOLELOGGINGENABLED, 'ON');
|
|
end if;
|
|
end loop;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
vgMsgTmp := MSG_CONFIG_VARIABLE_NOT_SET
|
|
||cgBL||' '||'No configuration found for environment_id='||pEnv||' in A_FILE_MANAGER_CONFIG table';
|
|
LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', 'pEnv='||pEnv);
|
|
RAISE_APPLICATION_ERROR(CODE_CONFIG_VARIABLE_NOT_SET, vgMsgTmp);
|
|
WHEN OTHERS THEN
|
|
vgMsgTmp := 'Unexpected error while initializing variables for environment: '||pEnv
|
|
||cgBL||' '||'SQLERRM: '||SQLERRM;
|
|
LOG_PROCESS_EVENT(vgMsgTmp, 'ERROR', 'pEnv='||pEnv);
|
|
RAISE;
|
|
END INIT_VARIABLES;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION GET_ERROR_MESSAGE(
|
|
pCode PLS_INTEGER
|
|
) RETURN VARCHAR2
|
|
IS
|
|
BEGIN
|
|
RETURN Errors(pCode).message;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
LOG_PROCESS_EVENT('No error message found for pCode='||pCode , 'WARNING', 'pCode='||pCode);
|
|
LOG_PROCESS_EVENT('Update ENV_MANAGER package header with new code.' , 'WARNING', 'pCode='||pCode);
|
|
RETURN NULL;
|
|
WHEN OTHERS THEN
|
|
LOG_PROCESS_EVENT(MSG_UNKNOWN , 'ERROR', 'pCode='||pCode);
|
|
RAISE_APPLICATION_ERROR(CODE_UNKNOWN, MSG_UNKNOWN);
|
|
END GET_ERROR_MESSAGE;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION GET_ERROR_STACK(
|
|
pFormat VARCHAR2
|
|
,pCode PLS_INTEGER
|
|
,pSourceFileReceivedKey CT_MRDS.A_SOURCE_FILE_RECEIVED.A_SOURCE_FILE_RECEIVED_KEY%TYPE DEFAULT NULL
|
|
) RETURN VARCHAR2
|
|
IS
|
|
vFullErrorCore VARCHAR2(32000);
|
|
vFullErrorMsg VARCHAR2(32000);
|
|
BEGIN
|
|
-- vgErrorMessage := SQLERRM|| cgBL;
|
|
-- vgErrorStack := DBMS_UTILITY.FORMAT_ERROR_STACK;
|
|
-- vgErrorBacktrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
|
|
vFullErrorCore :='Error Message:'
|
|
||cgBL|| SQLERRM|| cgBL
|
|
||'-------------------------------------------------------'
|
|
||cgBL||'Error Stack:'
|
|
||cgBL|| DBMS_UTILITY.FORMAT_ERROR_STACK
|
|
||'-------------------------------------------------------'
|
|
||cgBL||'Error Backtrace:'
|
|
||cgBL|| DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
|
|
-- vFullErrorCore := REGEXP_REPLACE (vFullErrorCore, pCode||': ', pCode||': '||GET_ERROR_MESSAGE(pCode) , 1, 1);
|
|
IF (pFormat = 'TABLE') THEN
|
|
vFullErrorMsg := vFullErrorCore;
|
|
ELSE
|
|
vFullErrorMsg := cgBL||'------------------------------------------------------+'
|
|
||cgBL||vFullErrorCore
|
|
||'------------------------------------------------------+';
|
|
END IF;
|
|
-- IF pSourceFileReceivedKey is not null THEN
|
|
-- vFullErrorMsg := vFullErrorMsg ||cgBL||GET_DET_SOURCE_FILE_RECEIVED_INFO(pSourceFileReceivedKey,1,1,1);
|
|
-- END IF;
|
|
|
|
RETURN vFullErrorMsg;
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
LOG_PROCESS_EVENT(MSG_UNKNOWN , 'ERROR', 'pFormat='||pFormat);
|
|
RETURN NULL;
|
|
END GET_ERROR_STACK;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION FORMAT_PARAMETERS(
|
|
pParameterList SYS.ODCIVARCHAR2LIST
|
|
) RETURN VARCHAR2 IS
|
|
vResult VARCHAR2(10000);
|
|
BEGIN
|
|
FOR i IN 1 .. pParameterList.COUNT LOOP
|
|
-- dbms_output.put_line('pParameterList(i): '||pParameterList(i));
|
|
if i < pParameterList.COUNT then vResult := vResult || replace(pParameterList(i), '''NULL''', 'NULL') ||' ,'|| cgBL;
|
|
else vResult := vResult || replace(pParameterList(i), '''NULL''', 'NULL');
|
|
end if;
|
|
END LOOP;
|
|
RETURN vResult;
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
LOG_PROCESS_EVENT('Error while formating parameters.' , 'WARNING');
|
|
RETURN NULL;
|
|
END FORMAT_PARAMETERS;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
PROCEDURE LOG_PROCESS_EVENT (
|
|
pLogMessage VARCHAR2
|
|
,pLogLevel VARCHAR2 DEFAULT 'ERROR'
|
|
,pParameters VARCHAR2 DEFAULT NULL
|
|
,pProcessName VARCHAR2 DEFAULT 'FILE_MANAGER'
|
|
) IS
|
|
PRAGMA AUTONOMOUS_TRANSACTION;
|
|
|
|
vLoggingEnabled VARCHAR2(10);
|
|
vMinLogLevel VARCHAR2(10);
|
|
vCallStack VARCHAR2(10000);
|
|
vProcedureName VARCHAR2(100);
|
|
vProcedureLevel PLS_INTEGER;
|
|
vTotalLines PLS_INTEGER;
|
|
vCurrentLine PLS_INTEGER;
|
|
|
|
-- Map of priority level
|
|
TYPE logLevelMap IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
|
|
vLogLevels logLevelMap;
|
|
|
|
BEGIN
|
|
-- Prority logging level (higher -> more important)
|
|
vLogLevels('DEBUG') := 1;
|
|
vLogLevels('INFO') := 2;
|
|
vLogLevels('WARNING') := 3;
|
|
vLogLevels('ERROR') := 4;
|
|
|
|
-- Check id logging is TURN-OFF
|
|
IF gvLoggingEnabled = 'OFF' THEN
|
|
RETURN;
|
|
END IF;
|
|
-- Check logging level
|
|
IF vLogLevels(pLogLevel) < vLogLevels(gvMinLogLevel) THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
vCallStack := DBMS_UTILITY.FORMAT_CALL_STACK;
|
|
vProcedureName := REGEXP_SUBSTR(vCallStack, 'package body\s+\w+\.(\w+\.\w+)', 1, 2, NULL, 1);
|
|
vTotalLines := REGEXP_COUNT(vCallStack, CHR(10)) + 1;
|
|
vCurrentLine := REGEXP_COUNT(SUBSTR(vCallStack, 1, INSTR(vCallStack, vProcedureName) - 1), CHR(10)) + 1;
|
|
vProcedureLevel := (vTotalLines - vCurrentLine + 1) - 3;
|
|
vProcedureName := LPAD(vProcedureName, LENGTH(vProcedureName) + 2*vProcedureLevel, ' ');
|
|
|
|
INSERT INTO CT_MRDS.A_PROCESS_LOG (guid, username, osuser, machine, module, process_name, procedure_name, procedure_parameters, log_level, log_message)
|
|
VALUES (guid, gvUsername, gvOsuser, gvMachine, gvModule, pProcessName, vProcedureName, pParameters, pLogLevel, pLogMessage);
|
|
|
|
COMMIT;
|
|
|
|
-- Also output to console for immediate visibility (if enabled)
|
|
IF gvConsoleLoggingEnabled = 'ON' THEN
|
|
DBMS_OUTPUT.PUT_LINE('[' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') || '] [' || pLogLevel || '] ' || vProcedureName || ': ' || pLogMessage);
|
|
END IF;
|
|
|
|
END LOG_PROCESS_EVENT;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
PROCEDURE LOG_PROCESS_ERROR(
|
|
pLogMessage IN VARCHAR2,
|
|
pParameters IN VARCHAR2 DEFAULT NULL,
|
|
pProcessName IN VARCHAR2 DEFAULT 'FILE_MANAGER'
|
|
) IS
|
|
PRAGMA AUTONOMOUS_TRANSACTION;
|
|
|
|
vCallStack VARCHAR2(32767);
|
|
vErrorStack VARCHAR2(32767);
|
|
vErrorBacktrace VARCHAR2(32767);
|
|
vAdjustedBacktrace VARCHAR2(32767);
|
|
vErrorContext VARCHAR2(4000);
|
|
vProcName VARCHAR2(100);
|
|
vProcedureLevel PLS_INTEGER;
|
|
vTotalLines PLS_INTEGER;
|
|
vCurrentLine PLS_INTEGER;
|
|
vFullErrorMessage CLOB;
|
|
vTimestamp VARCHAR2(30);
|
|
vSessionInfo VARCHAR2(1000);
|
|
|
|
BEGIN
|
|
-- Check if logging is disabled
|
|
IF gvLoggingEnabled = 'OFF' THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- Capture all available error information
|
|
vErrorStack := DBMS_UTILITY.FORMAT_ERROR_STACK;
|
|
vErrorBacktrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
|
|
vCallStack := DBMS_UTILITY.FORMAT_CALL_STACK;
|
|
vTimestamp := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
|
|
|
|
-- Capture session information for better context
|
|
vSessionInfo := 'Session ID: ' || SYS_CONTEXT('USERENV', 'SID') ||
|
|
', User: ' || SYS_CONTEXT('USERENV', 'SESSION_USER') ||
|
|
', Module: ' || SYS_CONTEXT('USERENV', 'MODULE') ||
|
|
', Client Info: ' || NVL(SYS_CONTEXT('USERENV', 'CLIENT_INFO'), 'N/A') ||
|
|
', Action: ' || NVL(SYS_CONTEXT('USERENV', 'ACTION'), 'N/A');
|
|
|
|
-- Build error context information
|
|
vErrorContext := 'Environment: ' || gvEnv ||
|
|
', Process: ' || NVL(pProcessName, 'UNKNOWN') ||
|
|
', Timestamp: ' || vTimestamp ||
|
|
', SQLCODE: ' || SQLCODE ||
|
|
', Transaction Active: ' || CASE WHEN DBMS_TRANSACTION.STEP_ID IS NOT NULL THEN 'YES' ELSE 'NO' END;
|
|
|
|
-- Extract procedure name and nesting level from call stack
|
|
-- Always extract actual procedure name from call stack for precise error location
|
|
vProcName := REGEXP_SUBSTR(vCallStack, 'package body\s+\w+\.(\w+\.\w+)', 1, 2, NULL, 1);
|
|
|
|
-- If we couldn't extract procedure name from call stack, use provided process name
|
|
IF vProcName IS NULL THEN
|
|
vProcName := NVL(pProcessName, 'UNKNOWN');
|
|
END IF;
|
|
|
|
vTotalLines := REGEXP_COUNT(vCallStack, CHR(10)) + 1;
|
|
vCurrentLine := REGEXP_COUNT(SUBSTR(vCallStack, 1, INSTR(vCallStack, vProcName) - 1), CHR(10)) + 1;
|
|
vProcedureLevel := (vTotalLines - vCurrentLine + 1) - 3;
|
|
vProcName := LPAD(vProcName, LENGTH(vProcName) + 2*vProcedureLevel, ' ');
|
|
|
|
-- Enhance line number display to show direct _BODY.sql file line numbers
|
|
-- Since packages are now split into separate _SPEC and _BODY files, line numbers map directly
|
|
vAdjustedBacktrace := REGEXP_REPLACE(vErrorBacktrace,
|
|
'at "CT_MRDS\.FILE_MANAGER", line ([0-9]+)',
|
|
'at "CT_MRDS.FILE_MANAGER", line \1 (-> FILE_MANAGER_BODY.sql:line \1)', 1, 0, 'i');
|
|
|
|
vAdjustedBacktrace := REGEXP_REPLACE(vAdjustedBacktrace,
|
|
'at "CT_MRDS\.ENV_MANAGER", line ([0-9]+)',
|
|
'at "CT_MRDS.ENV_MANAGER", line \1 (-> ENV_MANAGER_BODY.sql:line \1)', 1, 0, 'i');
|
|
|
|
-- Build comprehensive error message with professional formatting
|
|
vFullErrorMessage := 'ERROR REPORT' || cgBL ||
|
|
'-------------------------------------------------------' || cgBL ||
|
|
'ERROR SUMMARY' || cgBL ||
|
|
' Message: ' || pLogMessage || cgBL ||
|
|
' Context: ' || vErrorContext || cgBL ||
|
|
'-------------------------------------------------------' || cgBL ||
|
|
'SESSION INFORMATION' || cgBL ||
|
|
' ' || vSessionInfo || cgBL ||
|
|
'-------------------------------------------------------' || cgBL ||
|
|
'ERROR STACK (Oracle Internal)' || cgBL ||
|
|
vErrorStack ||
|
|
'-------------------------------------------------------' || cgBL ||
|
|
'BACKTRACE INFORMATION (Oracle Internal)' || cgBL ||
|
|
vErrorBacktrace ||
|
|
'-------------------------------------------------------' || cgBL ||
|
|
'CALL STACK (Execution Path)' || cgBL ||
|
|
vCallStack ||
|
|
'-------------------------------------------------------' || cgBL ||
|
|
'QUICK REFERENCE' || cgBL ||
|
|
' SQLCODE: ' || SQLCODE || cgBL ||
|
|
' SQLERRM: ' || SQLERRM || cgBL ||
|
|
' Timestamp: ' || vTimestamp || cgBL ||
|
|
' Parameters: ' || NVL(pParameters, 'None provided') || cgBL ||
|
|
'-------------------------------------------------------';
|
|
|
|
-- Insert comprehensive error record into log table
|
|
-- Note: LOG_MESSAGE is VARCHAR2(4000), so we'll truncate if needed but include key info
|
|
INSERT INTO CT_MRDS.A_PROCESS_LOG (guid, username, osuser, machine, module, process_name, procedure_name, procedure_parameters, log_level, log_message)
|
|
VALUES (guid, gvUsername, gvOsuser, gvMachine, gvModule, NVL(pProcessName, 'FILE_MANAGER'), vProcName, pParameters, 'ERROR',
|
|
CASE
|
|
WHEN LENGTH(vFullErrorMessage) <= 4000 THEN vFullErrorMessage
|
|
ELSE SUBSTR(vFullErrorMessage, 1, 3950) || '... [TRUNCATED]'
|
|
END);
|
|
|
|
COMMIT;
|
|
|
|
-- Enhanced console output for immediate visibility (if enabled)
|
|
IF gvConsoleLoggingEnabled = 'ON' THEN
|
|
DBMS_OUTPUT.PUT_LINE('=======================================================');
|
|
DBMS_OUTPUT.PUT_LINE('ERROR DETECTED AT: ' || vTimestamp);
|
|
DBMS_OUTPUT.PUT_LINE('PROCEDURE: ' || NVL(vProcName, 'UNKNOWN'));
|
|
DBMS_OUTPUT.PUT_LINE('MESSAGE: ' || pLogMessage);
|
|
DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE || ' | ENVIRONMENT: ' || gvEnv);
|
|
-- Extract and show the most relevant file and line number
|
|
IF INSTR(vAdjustedBacktrace, '-> ') > 0 THEN
|
|
DBMS_OUTPUT.PUT_LINE('SOURCE FILE LOCATION: ' || REGEXP_SUBSTR(vAdjustedBacktrace, '-> [^)]+'));
|
|
END IF;
|
|
DBMS_OUTPUT.PUT_LINE('FULL DETAILS: Query A_PROCESS_LOG table for complete diagnostic info');
|
|
DBMS_OUTPUT.PUT_LINE('QUERY (This Error): SELECT * FROM CT_MRDS.A_PROCESS_LOG WHERE GUID = ''' || guid || ''' ORDER BY LOG_TIMESTAMP DESC;');
|
|
DBMS_OUTPUT.PUT_LINE('QUERY (Recent All): SELECT * FROM CT_MRDS.A_PROCESS_LOG WHERE LOG_TIMESTAMP >= SYSDATE - 1/1440 ORDER BY LOG_TIMESTAMP DESC;');
|
|
DBMS_OUTPUT.PUT_LINE('=======================================================');
|
|
END IF;
|
|
|
|
END LOG_PROCESS_ERROR;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION ANALYZE_VALIDATION_ERRORS(
|
|
pValidationLogTable VARCHAR2,
|
|
pTemplateSchema VARCHAR2,
|
|
pTemplateTable VARCHAR2,
|
|
pCsvFileUri VARCHAR2
|
|
) RETURN VARCHAR2
|
|
IS
|
|
vAnalysisReport CLOB := '';
|
|
vCsvHeader VARCHAR2(4000);
|
|
vExpectedOrder VARCHAR2(4000);
|
|
vCsvOrder VARCHAR2(4000);
|
|
vErrorDetails VARCHAR2(32000) := '';
|
|
vSolutions VARCHAR2(4000);
|
|
vColumnMismatch VARCHAR2(1000);
|
|
vErrorCount NUMBER := 0;
|
|
vFirstDataError VARCHAR2(1000);
|
|
vErrorColumn VARCHAR2(100);
|
|
vErrorValue VARCHAR2(500);
|
|
vExpectedType VARCHAR2(100);
|
|
vTemplateColCount NUMBER := 0;
|
|
vCsvColCount NUMBER := 0;
|
|
vExcessColumns VARCHAR2(2000);
|
|
vCsvFirstLine VARCHAR2(4000);
|
|
|
|
-- Cursor for template table columns
|
|
CURSOR c_template_columns IS
|
|
SELECT COLUMN_NAME, DATA_TYPE, COLUMN_ID
|
|
FROM ALL_TAB_COLUMNS
|
|
WHERE OWNER = UPPER(REGEXP_SUBSTR(pTemplateSchema || '.' || pTemplateTable, '^([^.]+)'))
|
|
AND TABLE_NAME = UPPER(REGEXP_SUBSTR(pTemplateSchema || '.' || pTemplateTable, '\.(.+)$', 1, 1, NULL, 1))
|
|
ORDER BY COLUMN_ID;
|
|
|
|
BEGIN
|
|
-- Build expected column order from template table and count columns
|
|
FOR rec IN c_template_columns LOOP
|
|
IF vExpectedOrder IS NOT NULL THEN
|
|
vExpectedOrder := vExpectedOrder || ', ';
|
|
END IF;
|
|
vExpectedOrder := vExpectedOrder || rec.COLUMN_NAME;
|
|
vTemplateColCount := vTemplateColCount + 1;
|
|
END LOOP;
|
|
|
|
-- Parse validation log table for errors and CSV structure
|
|
BEGIN
|
|
-- Try to extract error information from the validation log table
|
|
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || pValidationLogTable ||
|
|
' WHERE record LIKE ''error processing column%'''
|
|
INTO vErrorCount;
|
|
|
|
-- Get first error details
|
|
IF vErrorCount > 0 THEN
|
|
EXECUTE IMMEDIATE 'SELECT record FROM ' || pValidationLogTable ||
|
|
' WHERE record LIKE ''error processing column%'' AND ROWNUM = 1'
|
|
INTO vFirstDataError;
|
|
|
|
-- Parse error to extract column name and error type
|
|
vErrorColumn := REGEXP_SUBSTR(vFirstDataError, 'error processing column ([A-Z_]+)', 1, 1, NULL, 1);
|
|
|
|
-- Try to get the actual error value from ORA-01722 message
|
|
BEGIN
|
|
EXECUTE IMMEDIATE 'SELECT record FROM ' || pValidationLogTable ||
|
|
' WHERE record LIKE ''ORA-01722%'' AND ROWNUM = 1'
|
|
INTO vFirstDataError;
|
|
vErrorValue := REGEXP_SUBSTR(vFirstDataError, 'string value containing ''([^'']+)''', 1, 1, NULL, 1);
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
vErrorValue := 'unknown value';
|
|
WHEN OTHERS THEN
|
|
vErrorValue := 'parsing error';
|
|
END;
|
|
END IF;
|
|
|
|
-- Try to extract CSV structure from validation log field definitions
|
|
BEGIN
|
|
EXECUTE IMMEDIATE '
|
|
SELECT LISTAGG(
|
|
REGEXP_SUBSTR(record, ''^\s+([A-Z_]+)\s+'', 1, 1, NULL, 1),
|
|
'', ''
|
|
) WITHIN GROUP (ORDER BY ROWNUM)
|
|
FROM ' || pValidationLogTable || '
|
|
WHERE record LIKE '' %CHAR%''
|
|
AND record NOT LIKE ''%Fields in Data Source%''
|
|
AND REGEXP_SUBSTR(record, ''^\s+([A-Z_]+)\s+'') IS NOT NULL'
|
|
INTO vCsvOrder;
|
|
|
|
-- Count CSV columns from parsed structure
|
|
IF vCsvOrder IS NOT NULL THEN
|
|
vCsvColCount := REGEXP_COUNT(vCsvOrder, ',') + 1;
|
|
END IF;
|
|
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
vCsvOrder := 'Unable to determine CSV column order from validation log';
|
|
END;
|
|
|
|
-- Alternative method: Try to read first line of CSV directly for column count
|
|
IF vCsvColCount = 0 THEN
|
|
BEGIN
|
|
-- This is a fallback - try to get CSV header from external source if possible
|
|
-- Note: This would require DBMS_CLOUD.GET_OBJECT or similar approach
|
|
-- For now, we'll rely on the validation log parsing
|
|
NULL;
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
NULL;
|
|
END;
|
|
END IF;
|
|
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
vErrorDetails := 'Error analyzing validation log: ' || SQLERRM;
|
|
END;
|
|
|
|
-- Detect column order mismatch and excess columns
|
|
IF vCsvOrder IS NOT NULL AND vExpectedOrder IS NOT NULL THEN
|
|
IF UPPER(REPLACE(vCsvOrder, ' ', '')) != UPPER(REPLACE(vExpectedOrder, ' ', '')) THEN
|
|
vColumnMismatch := 'YES';
|
|
ELSE
|
|
vColumnMismatch := 'NO';
|
|
END IF;
|
|
END IF;
|
|
|
|
-- Check for excess columns
|
|
IF vCsvColCount > vTemplateColCount THEN
|
|
-- Try to identify which columns are excess
|
|
IF vCsvOrder IS NOT NULL THEN
|
|
-- Parse CSV columns and compare with template
|
|
DECLARE
|
|
vCsvCols SYS.ODCIVARCHAR2LIST;
|
|
vTemplateCols SYS.ODCIVARCHAR2LIST;
|
|
vExcessFound VARCHAR2(1) := 'N';
|
|
i NUMBER;
|
|
BEGIN
|
|
-- Split CSV columns
|
|
SELECT TRIM(REGEXP_SUBSTR(vCsvOrder, '[^,]+', 1, LEVEL))
|
|
BULK COLLECT INTO vCsvCols
|
|
FROM DUAL
|
|
CONNECT BY REGEXP_SUBSTR(vCsvOrder, '[^,]+', 1, LEVEL) IS NOT NULL;
|
|
|
|
-- Split template columns
|
|
SELECT TRIM(REGEXP_SUBSTR(vExpectedOrder, '[^,]+', 1, LEVEL))
|
|
BULK COLLECT INTO vTemplateCols
|
|
FROM DUAL
|
|
CONNECT BY REGEXP_SUBSTR(vExpectedOrder, '[^,]+', 1, LEVEL) IS NOT NULL;
|
|
|
|
-- Find excess columns (those in CSV but not in template)
|
|
FOR i IN 1..vCsvCols.COUNT LOOP
|
|
DECLARE
|
|
vFoundInTemplate BOOLEAN := FALSE;
|
|
j NUMBER;
|
|
BEGIN
|
|
-- Check if CSV column exists in template
|
|
FOR j IN 1..vTemplateCols.COUNT LOOP
|
|
IF UPPER(TRIM(vCsvCols(i))) = UPPER(TRIM(vTemplateCols(j))) THEN
|
|
vFoundInTemplate := TRUE;
|
|
EXIT;
|
|
END IF;
|
|
END LOOP;
|
|
|
|
-- If not found in template, it's an excess column
|
|
IF NOT vFoundInTemplate THEN
|
|
IF vExcessFound = 'Y' THEN
|
|
vExcessColumns := vExcessColumns || ', ';
|
|
END IF;
|
|
vExcessColumns := vExcessColumns || vCsvCols(i);
|
|
vExcessFound := 'Y';
|
|
END IF;
|
|
END;
|
|
END LOOP;
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
vExcessColumns := 'Unable to determine specific excess columns';
|
|
END;
|
|
END IF;
|
|
END IF;
|
|
|
|
-- Build comprehensive analysis report
|
|
vAnalysisReport := 'FILE VALIDATION FAILED - DETAILED ANALYSIS' || cgBL ||
|
|
'=================================================' || cgBL || cgBL;
|
|
|
|
-- Column structure analysis
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'COLUMN STRUCTURE ANALYSIS:' || cgBL ||
|
|
'---------------------------------------------------' || cgBL ||
|
|
'Template Expected Order: ' || vExpectedOrder || cgBL ||
|
|
'Template Column Count: ' || vTemplateColCount || cgBL ||
|
|
'CSV Detected Order: ' || NVL(vCsvOrder, 'Unknown') || cgBL ||
|
|
'CSV Column Count: ' || vCsvColCount || cgBL || cgBL;
|
|
|
|
-- Report column count issues
|
|
IF vCsvColCount > vTemplateColCount THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'EXCESS COLUMNS DETECTED!' || cgBL ||
|
|
'CSV file has ' || (vCsvColCount - vTemplateColCount) || ' more columns than template allows.' || cgBL;
|
|
IF vExcessColumns IS NOT NULL THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'Excess columns found: ' || vExcessColumns || cgBL;
|
|
END IF;
|
|
vAnalysisReport := vAnalysisReport || cgBL;
|
|
END IF;
|
|
|
|
-- Report column order issues
|
|
IF vColumnMismatch = 'YES' THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'COLUMN ORDER MISMATCH DETECTED!' || cgBL ||
|
|
'CSV columns are in different order than template expects.' || cgBL || cgBL;
|
|
END IF;
|
|
|
|
-- Specific error analysis
|
|
IF vErrorCount > 0 THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'SPECIFIC ERRORS FOUND:' || cgBL ||
|
|
'---------------------------------------------------' || cgBL;
|
|
|
|
IF vErrorColumn IS NOT NULL THEN
|
|
-- Get expected data type for error column
|
|
FOR rec IN c_template_columns LOOP
|
|
IF rec.COLUMN_NAME = vErrorColumn THEN
|
|
vExpectedType := rec.DATA_TYPE;
|
|
EXIT;
|
|
END IF;
|
|
END LOOP;
|
|
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'1. Column ' || vErrorColumn || ': Expected ' || vExpectedType ||
|
|
', received "' || NVL(vErrorValue, 'unknown value') || '" (TEXT)' || cgBL ||
|
|
' → CSV position contains different data type than expected' || cgBL;
|
|
END IF;
|
|
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'Total validation errors found: ' || vErrorCount || cgBL || cgBL;
|
|
END IF;
|
|
|
|
-- Solutions section
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'SUGGESTED SOLUTIONS:' || cgBL ||
|
|
'---------------------------------------------------' || cgBL;
|
|
|
|
-- Solutions for excess columns
|
|
IF vCsvColCount > vTemplateColCount THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'FOR EXCESS COLUMNS:' || cgBL ||
|
|
'• Remove extra columns from CSV file' || cgBL ||
|
|
'• Keep only these columns in this order: ' || vExpectedOrder || cgBL;
|
|
IF vExcessColumns IS NOT NULL THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'• Specifically remove: ' || vExcessColumns || cgBL;
|
|
END IF;
|
|
vAnalysisReport := vAnalysisReport || cgBL;
|
|
END IF;
|
|
|
|
-- Solutions for column order
|
|
IF vColumnMismatch = 'YES' THEN
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'FOR COLUMN ORDER:' || cgBL ||
|
|
'• Reorder CSV columns to match template: ' || vExpectedOrder || cgBL ||
|
|
'• Or update template table column order to match CSV file' || cgBL || cgBL;
|
|
END IF;
|
|
|
|
-- General solutions
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'GENERAL RECOMMENDATIONS:' || cgBL ||
|
|
'• Ensure CSV has exactly ' || vTemplateColCount || ' columns' || cgBL ||
|
|
'• Verify column names match template table exactly' || cgBL ||
|
|
'• Check data types in each column match expectations' || cgBL || cgBL;
|
|
|
|
-- Validation log reference
|
|
vAnalysisReport := vAnalysisReport ||
|
|
'TECHNICAL DETAILS:' || cgBL ||
|
|
'---------------------------------------------------' || cgBL ||
|
|
'Validation Log Table: ' || pValidationLogTable || cgBL ||
|
|
'Template Table: ' || pTemplateSchema || '.' || pTemplateTable || cgBL ||
|
|
'CSV File: ' || pCsvFileUri || cgBL ||
|
|
'Query validation details: SELECT * FROM ' || pValidationLogTable || ';' || cgBL;
|
|
|
|
RETURN vAnalysisReport;
|
|
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
RETURN 'Error generating validation analysis: ' || SQLERRM || cgBL ||
|
|
'Validation Log Table: ' || pValidationLogTable || cgBL ||
|
|
'Check table manually: SELECT * FROM ' || pValidationLogTable || ';';
|
|
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 GET_PACKAGE_VERSION_INFO(
|
|
pPackageName => 'ENV_MANAGER',
|
|
pVersion => PACKAGE_VERSION,
|
|
pBuildDate => PACKAGE_BUILD_DATE,
|
|
pAuthor => PACKAGE_AUTHOR
|
|
);
|
|
END GET_BUILD_INFO;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION GET_VERSION_HISTORY
|
|
RETURN VARCHAR2
|
|
IS
|
|
BEGIN
|
|
RETURN FORMAT_VERSION_HISTORY(
|
|
pPackageName => 'ENV_MANAGER',
|
|
pVersionHistory => VERSION_HISTORY
|
|
);
|
|
END GET_VERSION_HISTORY;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION GET_PACKAGE_VERSION_INFO(
|
|
pPackageName VARCHAR2,
|
|
pVersion VARCHAR2,
|
|
pBuildDate VARCHAR2,
|
|
pAuthor VARCHAR2
|
|
) RETURN VARCHAR2
|
|
IS
|
|
BEGIN
|
|
RETURN 'Package: ' || pPackageName || cgBL ||
|
|
'Version: ' || pVersion || cgBL ||
|
|
'Build Date: ' || pBuildDate || cgBL ||
|
|
'Author: ' || pAuthor;
|
|
END GET_PACKAGE_VERSION_INFO;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION FORMAT_VERSION_HISTORY(
|
|
pPackageName VARCHAR2,
|
|
pVersionHistory VARCHAR2
|
|
) RETURN VARCHAR2
|
|
IS
|
|
BEGIN
|
|
RETURN pPackageName || ' Version History:' || cgBL || pVersionHistory;
|
|
END FORMAT_VERSION_HISTORY;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- PACKAGE HASH + CHANGE DETECTION FUNCTIONS IMPLEMENTATION
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION CALCULATE_PACKAGE_HASH(
|
|
pPackageOwner VARCHAR2,
|
|
pPackageName VARCHAR2,
|
|
pPackageType VARCHAR2
|
|
) RETURN VARCHAR2
|
|
IS
|
|
vSourceCode CLOB;
|
|
vHash VARCHAR2(64);
|
|
vRawHash RAW(32);
|
|
BEGIN
|
|
-- Build complete source code from ALL_SOURCE using XMLAGG (no 4000 char limit)
|
|
-- CRITICAL: Cannot use LISTAGG due to VARCHAR2 limit
|
|
SELECT XMLAGG(XMLELEMENT(E, TEXT) ORDER BY LINE).GETCLOBVAL()
|
|
INTO vSourceCode
|
|
FROM ALL_SOURCE
|
|
WHERE OWNER = UPPER(pPackageOwner)
|
|
AND NAME = UPPER(pPackageName)
|
|
AND TYPE = UPPER(pPackageType);
|
|
|
|
-- If empty, return NULL
|
|
IF vSourceCode IS NULL OR DBMS_LOB.GETLENGTH(vSourceCode) = 0 THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
-- Calculate SHA256 hash directly from CLOB
|
|
-- DBMS_CRYPTO.HASH has overload for CLOB in Oracle 19c+
|
|
vRawHash := DBMS_CRYPTO.HASH(
|
|
src => vSourceCode,
|
|
typ => DBMS_CRYPTO.HASH_SH256
|
|
);
|
|
|
|
-- Convert to hex string
|
|
vHash := LOWER(RAWTOHEX(vRawHash));
|
|
|
|
RETURN vHash;
|
|
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
RETURN NULL;
|
|
WHEN OTHERS THEN
|
|
LOG_PROCESS_ERROR('Error calculating package hash: ' || SQLERRM,
|
|
'pPackageOwner=' || pPackageOwner || ', pPackageName=' || pPackageName);
|
|
RETURN NULL;
|
|
END CALCULATE_PACKAGE_HASH;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
PROCEDURE TRACK_PACKAGE_VERSION(
|
|
pPackageOwner VARCHAR2,
|
|
pPackageName VARCHAR2,
|
|
pPackageVersion VARCHAR2,
|
|
pPackageBuildDate VARCHAR2,
|
|
pPackageAuthor VARCHAR2
|
|
)
|
|
IS
|
|
vHashSpec VARCHAR2(64);
|
|
vHashBody VARCHAR2(64);
|
|
vLastHashSpec VARCHAR2(64);
|
|
vLastHashBody VARCHAR2(64);
|
|
vLastVersion VARCHAR2(10);
|
|
vLineCountSpec NUMBER;
|
|
vLineCountBody NUMBER;
|
|
vChangeDetected CHAR(1) := 'N';
|
|
vChangeMessage VARCHAR2(4000);
|
|
vParameters VARCHAR2(4000);
|
|
BEGIN
|
|
vParameters := FORMAT_PARAMETERS(SYS.ODCIVARCHAR2LIST(
|
|
'pPackageOwner => ''' || pPackageOwner || '''',
|
|
'pPackageName => ''' || pPackageName || '''',
|
|
'pPackageVersion => ''' || pPackageVersion || ''''
|
|
));
|
|
|
|
LOG_PROCESS_EVENT('Start TRACK_PACKAGE_VERSION', 'INFO', vParameters);
|
|
|
|
-- Calculate current hashes
|
|
vHashSpec := CALCULATE_PACKAGE_HASH(pPackageOwner, pPackageName, 'PACKAGE');
|
|
vHashBody := CALCULATE_PACKAGE_HASH(pPackageOwner, pPackageName, 'PACKAGE BODY');
|
|
|
|
-- Get line counts
|
|
BEGIN
|
|
SELECT COUNT(*)
|
|
INTO vLineCountSpec
|
|
FROM ALL_SOURCE
|
|
WHERE OWNER = UPPER(pPackageOwner)
|
|
AND NAME = UPPER(pPackageName)
|
|
AND TYPE = 'PACKAGE';
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
vLineCountSpec := 0;
|
|
END;
|
|
|
|
BEGIN
|
|
SELECT COUNT(*)
|
|
INTO vLineCountBody
|
|
FROM ALL_SOURCE
|
|
WHERE OWNER = UPPER(pPackageOwner)
|
|
AND NAME = UPPER(pPackageName)
|
|
AND TYPE = 'PACKAGE BODY';
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
vLineCountBody := 0;
|
|
END;
|
|
|
|
-- Get last tracked version and hashes
|
|
BEGIN
|
|
SELECT PACKAGE_VERSION, SOURCE_CODE_HASH_SPEC, SOURCE_CODE_HASH_BODY
|
|
INTO vLastVersion, vLastHashSpec, vLastHashBody
|
|
FROM CT_MRDS.A_PACKAGE_VERSION_TRACKING
|
|
WHERE PACKAGE_OWNER = UPPER(pPackageOwner)
|
|
AND PACKAGE_NAME = UPPER(pPackageName)
|
|
ORDER BY TRACKING_DATE DESC
|
|
FETCH FIRST 1 ROW ONLY;
|
|
|
|
-- Check if hash changed but version didn't
|
|
IF (vHashSpec != vLastHashSpec OR NVL(vHashBody,'X') != NVL(vLastHashBody,'X'))
|
|
AND pPackageVersion = vLastVersion THEN
|
|
|
|
vChangeDetected := 'Y';
|
|
vChangeMessage := 'WARNING: Source code changed without version update!' || cgBL ||
|
|
'Last Version: ' || vLastVersion || cgBL ||
|
|
'Current Version: ' || pPackageVersion || cgBL;
|
|
|
|
IF vHashSpec != vLastHashSpec THEN
|
|
vChangeMessage := vChangeMessage ||
|
|
'SPEC Changed - Hash: ' || SUBSTR(vHashSpec, 1, 16) || '... (was: ' ||
|
|
SUBSTR(vLastHashSpec, 1, 16) || '...)' || cgBL;
|
|
END IF;
|
|
|
|
IF NVL(vHashBody,'X') != NVL(vLastHashBody,'X') THEN
|
|
vChangeMessage := vChangeMessage ||
|
|
'BODY Changed - Hash: ' || SUBSTR(vHashBody, 1, 16) || '... (was: ' ||
|
|
SUBSTR(NVL(vLastHashBody,'NULL'), 1, 16) || '...)' || cgBL;
|
|
END IF;
|
|
|
|
vChangeMessage := vChangeMessage ||
|
|
'RECOMMENDATION: Update PACKAGE_VERSION constant and PACKAGE_BUILD_DATE';
|
|
|
|
LOG_PROCESS_EVENT(vChangeMessage, 'WARNING', vParameters);
|
|
END IF;
|
|
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
-- First time tracking this package
|
|
vChangeDetected := 'N';
|
|
vChangeMessage := 'First tracking record for this package';
|
|
LOG_PROCESS_EVENT(vChangeMessage, 'INFO', vParameters);
|
|
END;
|
|
|
|
-- Insert tracking record
|
|
INSERT INTO CT_MRDS.A_PACKAGE_VERSION_TRACKING (
|
|
PACKAGE_OWNER,
|
|
PACKAGE_NAME,
|
|
PACKAGE_TYPE,
|
|
PACKAGE_VERSION,
|
|
PACKAGE_BUILD_DATE,
|
|
PACKAGE_AUTHOR,
|
|
SOURCE_CODE_HASH_SPEC,
|
|
SOURCE_CODE_HASH_BODY,
|
|
LINE_COUNT_SPEC,
|
|
LINE_COUNT_BODY,
|
|
DETECTED_CHANGE_WITHOUT_VERSION,
|
|
CHANGE_DETECTION_MESSAGE
|
|
) VALUES (
|
|
UPPER(pPackageOwner),
|
|
UPPER(pPackageName),
|
|
'BOTH',
|
|
pPackageVersion,
|
|
pPackageBuildDate,
|
|
pPackageAuthor,
|
|
vHashSpec,
|
|
vHashBody,
|
|
vLineCountSpec,
|
|
vLineCountBody,
|
|
vChangeDetected,
|
|
vChangeMessage
|
|
);
|
|
|
|
COMMIT;
|
|
|
|
LOG_PROCESS_EVENT('End TRACK_PACKAGE_VERSION - Record inserted', 'INFO', vParameters);
|
|
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
LOG_PROCESS_ERROR('Error in TRACK_PACKAGE_VERSION: ' || SQLERRM, vParameters);
|
|
RAISE;
|
|
END TRACK_PACKAGE_VERSION;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION CHECK_PACKAGE_CHANGES(
|
|
pPackageOwner VARCHAR2,
|
|
pPackageName VARCHAR2
|
|
) RETURN VARCHAR2
|
|
IS
|
|
vCurrentHashSpec VARCHAR2(64);
|
|
vCurrentHashBody VARCHAR2(64);
|
|
vLastHashSpec VARCHAR2(64);
|
|
vLastHashBody VARCHAR2(64);
|
|
vLastVersion VARCHAR2(10);
|
|
vLastTrackingDate TIMESTAMP;
|
|
vChangeReport VARCHAR2(4000);
|
|
vSpecChanged BOOLEAN := FALSE;
|
|
vBodyChanged BOOLEAN := FALSE;
|
|
BEGIN
|
|
-- Get current hashes
|
|
vCurrentHashSpec := CALCULATE_PACKAGE_HASH(pPackageOwner, pPackageName, 'PACKAGE');
|
|
vCurrentHashBody := CALCULATE_PACKAGE_HASH(pPackageOwner, pPackageName, 'PACKAGE BODY');
|
|
|
|
-- Get last tracked hashes
|
|
BEGIN
|
|
SELECT PACKAGE_VERSION, SOURCE_CODE_HASH_SPEC, SOURCE_CODE_HASH_BODY, TRACKING_DATE
|
|
INTO vLastVersion, vLastHashSpec, vLastHashBody, vLastTrackingDate
|
|
FROM CT_MRDS.A_PACKAGE_VERSION_TRACKING
|
|
WHERE PACKAGE_OWNER = UPPER(pPackageOwner)
|
|
AND PACKAGE_NAME = UPPER(pPackageName)
|
|
ORDER BY TRACKING_DATE DESC
|
|
FETCH FIRST 1 ROW ONLY;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
RETURN 'Package ' || pPackageOwner || '.' || pPackageName || ' has never been tracked.' || cgBL ||
|
|
'Run TRACK_PACKAGE_VERSION to establish baseline.';
|
|
END;
|
|
|
|
-- Check for changes
|
|
IF vCurrentHashSpec != vLastHashSpec THEN
|
|
vSpecChanged := TRUE;
|
|
END IF;
|
|
|
|
IF NVL(vCurrentHashBody, 'X') != NVL(vLastHashBody, 'X') THEN
|
|
vBodyChanged := TRUE;
|
|
END IF;
|
|
|
|
-- Build report
|
|
IF vSpecChanged OR vBodyChanged THEN
|
|
vChangeReport := 'WARNING: Package ' || pPackageOwner || '.' || pPackageName || ' has changed!' || cgBL ||
|
|
'========================================' || cgBL ||
|
|
'Last Tracked Version: ' || vLastVersion || cgBL ||
|
|
'Last Tracked Date: ' || TO_CHAR(vLastTrackingDate, 'YYYY-MM-DD HH24:MI:SS') || cgBL ||
|
|
cgBL;
|
|
|
|
IF vSpecChanged THEN
|
|
vChangeReport := vChangeReport ||
|
|
'SPECIFICATION Changed:' || cgBL ||
|
|
' Current Hash: ' || SUBSTR(vCurrentHashSpec, 1, 16) || '...' || cgBL ||
|
|
' Last Hash: ' || SUBSTR(vLastHashSpec, 1, 16) || '...' || cgBL ||
|
|
cgBL;
|
|
END IF;
|
|
|
|
IF vBodyChanged THEN
|
|
vChangeReport := vChangeReport ||
|
|
'BODY Changed:' || cgBL ||
|
|
' Current Hash: ' || SUBSTR(NVL(vCurrentHashBody, 'NULL'), 1, 16) || '...' || cgBL ||
|
|
' Last Hash: ' || SUBSTR(NVL(vLastHashBody, 'NULL'), 1, 16) || '...' || cgBL ||
|
|
cgBL;
|
|
END IF;
|
|
|
|
vChangeReport := vChangeReport ||
|
|
'RECOMMENDATION:' || cgBL ||
|
|
'1. Update PACKAGE_VERSION constant' || cgBL ||
|
|
'2. Update PACKAGE_BUILD_DATE constant' || cgBL ||
|
|
'3. Add entry to VERSION_HISTORY' || cgBL ||
|
|
'4. Call TRACK_PACKAGE_VERSION to update tracking';
|
|
ELSE
|
|
vChangeReport := 'OK: Package ' || pPackageOwner || '.' || pPackageName || ' has not changed.' || cgBL ||
|
|
'Last Tracked: ' || TO_CHAR(vLastTrackingDate, 'YYYY-MM-DD HH24:MI:SS') || cgBL ||
|
|
'Version: ' || vLastVersion;
|
|
END IF;
|
|
|
|
RETURN vChangeReport;
|
|
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
RETURN 'Error checking package changes: ' || SQLERRM;
|
|
END CHECK_PACKAGE_CHANGES;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
FUNCTION GET_PACKAGE_HASH_INFO(
|
|
pPackageOwner VARCHAR2,
|
|
pPackageName VARCHAR2
|
|
) RETURN VARCHAR2
|
|
IS
|
|
vCurrentHashSpec VARCHAR2(64);
|
|
vCurrentHashBody VARCHAR2(64);
|
|
vLastHashSpec VARCHAR2(64);
|
|
vLastHashBody VARCHAR2(64);
|
|
vLastVersion VARCHAR2(10);
|
|
vLastTrackingDate TIMESTAMP;
|
|
vLastChangeDetected CHAR(1);
|
|
vInfo VARCHAR2(4000);
|
|
BEGIN
|
|
-- Get current hashes
|
|
vCurrentHashSpec := CALCULATE_PACKAGE_HASH(pPackageOwner, pPackageName, 'PACKAGE');
|
|
vCurrentHashBody := CALCULATE_PACKAGE_HASH(pPackageOwner, pPackageName, 'PACKAGE BODY');
|
|
|
|
-- Get last tracking info
|
|
BEGIN
|
|
SELECT PACKAGE_VERSION,
|
|
SOURCE_CODE_HASH_SPEC,
|
|
SOURCE_CODE_HASH_BODY,
|
|
TRACKING_DATE,
|
|
DETECTED_CHANGE_WITHOUT_VERSION
|
|
INTO vLastVersion, vLastHashSpec, vLastHashBody, vLastTrackingDate, vLastChangeDetected
|
|
FROM CT_MRDS.A_PACKAGE_VERSION_TRACKING
|
|
WHERE PACKAGE_OWNER = UPPER(pPackageOwner)
|
|
AND PACKAGE_NAME = UPPER(pPackageName)
|
|
ORDER BY TRACKING_DATE DESC
|
|
FETCH FIRST 1 ROW ONLY;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
RETURN 'Package: ' || pPackageOwner || '.' || pPackageName || cgBL ||
|
|
'Status: Never tracked' || cgBL ||
|
|
'Current Hash (SPEC): ' || SUBSTR(vCurrentHashSpec, 1, 16) || '...' || cgBL ||
|
|
'Current Hash (BODY): ' || SUBSTR(NVL(vCurrentHashBody, 'NULL'), 1, 16) || '...';
|
|
END;
|
|
|
|
-- Build info report
|
|
vInfo := 'Package: ' || pPackageOwner || '.' || pPackageName || cgBL ||
|
|
'Current Version: ' || vLastVersion || cgBL ||
|
|
'Last Tracked: ' || TO_CHAR(vLastTrackingDate, 'YYYY-MM-DD HH24:MI:SS') || cgBL ||
|
|
cgBL ||
|
|
'Current Hash (SPEC): ' || SUBSTR(vCurrentHashSpec, 1, 32) || '...' || cgBL ||
|
|
'Last Hash (SPEC): ' || SUBSTR(vLastHashSpec, 1, 32) || '...' || cgBL;
|
|
|
|
IF vCurrentHashBody IS NOT NULL OR vLastHashBody IS NOT NULL THEN
|
|
vInfo := vInfo ||
|
|
'Current Hash (BODY): ' || SUBSTR(NVL(vCurrentHashBody, 'NULL'), 1, 32) || '...' || cgBL ||
|
|
'Last Hash (BODY): ' || SUBSTR(NVL(vLastHashBody, 'NULL'), 1, 32) || '...' || cgBL;
|
|
END IF;
|
|
|
|
vInfo := vInfo || cgBL;
|
|
|
|
-- Status
|
|
IF vCurrentHashSpec = vLastHashSpec AND NVL(vCurrentHashBody, 'X') = NVL(vLastHashBody, 'X') THEN
|
|
vInfo := vInfo || 'Status: OK - No changes detected';
|
|
ELSE
|
|
vInfo := vInfo || 'Status: CHANGED - Source code modified since last tracking';
|
|
END IF;
|
|
|
|
IF vLastChangeDetected = 'Y' THEN
|
|
vInfo := vInfo || cgBL || 'Last Tracking Warning: Change detected without version update';
|
|
END IF;
|
|
|
|
RETURN vInfo;
|
|
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
RETURN 'Error getting package hash info: ' || SQLERRM;
|
|
END GET_PACKAGE_HASH_INFO;
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
BEGIN
|
|
INIT_ERRORS;
|
|
guid := sys_guid();
|
|
gvUsername := SYS_CONTEXT('USERENV', 'SESSION_USER');
|
|
gvOsuser := SYS_CONTEXT('USERENV', 'OS_USER');
|
|
gvMachine := SYS_CONTEXT('USERENV', 'HOST');
|
|
gvModule := SYS_CONTEXT('USERENV', 'MODULE');
|
|
|
|
-- Get info about EnvironmentID. Without it package cannot proceed further.
|
|
-- Information about environment is needed to get proper configuration values
|
|
-- It can be set up in two different ways :
|
|
-- 1. Set it on session level: execute DBMS_SESSION.SET_IDENTIFIER (client_id => 'dev');
|
|
-- 2. Set it on configuration level: Insert into CT_MRDS.A_FILE_MANAGER_CONFIG (ENVIRONMENT_ID,CONFIG_VARIABLE,CONFIG_VARIABLE_VALUE) values ('default','environment_id','dev');
|
|
-- Session level setup (1.) takes precedence over configuration level one (2.)
|
|
|
|
gvEnv := nvl(SYS_CONTEXT ('USERENV', 'CLIENT_IDENTIFIER'), GET_DEFAULT_ENV());
|
|
if gvEnv is null then
|
|
dbms_output.put_line(MSG_ENVIRONMENT_NOT_SET);
|
|
LOG_PROCESS_EVENT(MSG_ENVIRONMENT_NOT_SET, 'ERROR');
|
|
RAISE_APPLICATION_ERROR(CODE_ENVIRONMENT_NOT_SET, MSG_ENVIRONMENT_NOT_SET);
|
|
else
|
|
dbms_output.put_line('EnvironmentID set to: '||gvEnv);
|
|
end if;
|
|
|
|
INIT_VARIABLES(pEnv => gvEnv);
|
|
END ENV_MANAGER;
|
|
|
|
/
|
|
|
|
/
|