--
-- Copyright 2024 Google LLC
--
-- Licensed under the Apache License, Version 2.0 (the "License").
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     https://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
This script access Automatic Repository Workload (AWR) views in the database dictionary.
Please ensure you have proper licensing. For more information consult Oracle Support Doc ID 1490798.1

*/
prompt Param1 = &1

define version = '&1'
define dtrange = &v_statsWindow
define colspr = '|'

-- Set the environment to a known state, overriding any custom configuration.
@@op_set_sql_env.sql
set headsep off
set trimspool on
set lines 32000
set pagesize 0 embedded on
set feed off
set underline off
set verify off
set head off
set scan on
set pause off
set wrap on
set echo off
set appinfo 'DB MIGRATION ASSESSMENT'
set colsep '|'
set timing off
set time off
alter session set nls_numeric_characters='.,';
alter session set nls_date_format='YYYY-MM-DD HH24:MI:SS';

set termout on
whenever sqlerror exit failure
whenever oserror continue

variable v_pkey VARCHAR2(100);

-- For the min and max snaps to collect
variable minsnap NUMBER;
variable minsnaptime VARCHAR2(20);
variable maxsnap NUMBER;
variable maxsnaptime VARCHAR2(20);

-- To handle collection from within a Dataguard standby
variable umfflag VARCHAR2(100);

-- To handle column 'LOGGING' in 'DBA_PDBS'.  Depends on Oracle version
variable pdb_logging_flag VARCHAR2(1);

-- To handle column 'DEFAULT_VALUE' in 'V_$SYSTEM_PARAMETER'
variable dflt_value_flag  VARCHAR2(1);

-- to handle column 'COMPRESS_FOR' in 'DBA_TABLES'. Depends on Oracle version
variable b_compress_col VARCHAR2(20);

variable b_lob_compression_col         VARCHAR2(30);
variable b_lob_part_compression_col    VARCHAR2(30);
variable b_lob_subpart_compression_col VARCHAR2(30);
variable b_lob_dedup_col               VARCHAR2(30);
variable b_lob_part_dedup_col          VARCHAR2(30);
variable b_lob_subpart_dedup_col       VARCHAR2(30);

variable b_index_visibility            VARCHAR2(30);

variable b_io_function_sql             VARCHAR2(20);

variable v_dma_source_id               VARCHAR2(100);
variable v_manual_unique_id            VARCHAR2(100);

-- Session settings to support creating substitution variables for the scripts.
column instnc new_value v_inst noprint
column hostnc new_value v_host noprint
column horanc new_value v_hora noprint
column dbname new_value v_dbname noprint
column dbversion new_value v_dbversion noprint
column min_snapid new_value v_min_snapid noprint
column min_snaptime new_value v_min_snaptime noprint
column max_snapid new_value v_max_snapid noprint
column max_snaptime new_value v_max_snaptime noprint
column umf_test new_value v_umf_test noprint
--column p_dma_source_id new_value v_dma_source_id noprint
column p_dbid new_value v_dbid noprint
column p_tblprefix new_value v_tblprefix noprint
column p_is_container new_value v_is_container noprint
column p_dbparam_dflt_col new_value v_dbparam_dflt_col noprint
column p_editionable_col new_value v_editionable_col noprint
column p_dopluggable new_value v_dopluggable noprint
column p_db_container_col new_value v_db_container_col noprint
column p_pluggablelogging new_value v_pluggablelogging noprint
column p_sqlcmd new_value v_sqlcmd noprint
column p_compress_col new_value v_compress_col noprint
column p_lob_compression_col new_value v_lob_compression_col noprint
column p_lob_part_compression_col new_value v_lob_part_compression_col noprint
column p_lob_subpart_compression_col new_value v_lob_subpart_compression_col noprint
column p_lob_dedup_col new_value v_lob_dedup_col noprint
column p_lob_part_dedup_col new_value v_lob_part_dedup_col noprint
column p_lob_subpart_dedup_col new_value v_lob_subpart_dedup_col noprint
column p_index_visibility new_value v_index_visibility noprint
column p_io_function_sql new_value v_io_function_sql noprint

-- Define some session info for the extraction -- BEGIN
SELECT host_name     hostnc,
       instance_name instnc
FROM   v$instance
/

SELECT name dbname
FROM   v$database
/


SELECT RTRIM(SUBSTR('&v_tag',INSTR('&v_tag','_',1,5)+1), '.csv') horanc from dual;

SELECT substr(replace(version,'.',''),0,3) dbversion
FROM v$instance
/
BEGIN
  :v_pkey := '&&v_host' || '_' || '&&v_dbname' || '_' || '&&v_hora';
END;
/

-- Define some session info for the extraction -- END


-- Determine how we will transform the data_type column based on database version. --BEGIN
COLUMN p_data_type_exp NEW_VALUE v_data_type_exp noprint
COLUMN p_ora9ind NEW_VALUE v_ora9ind noprint
SELECT CASE WHEN  '&v_dbversion' LIKE '9%' THEN 'data_type_col_9i.sql'
            ELSE 'data_type_col_regex.sql'
       END as p_data_type_exp,
       CASE WHEN  '&v_dbversion' LIKE '9%' THEN '9i_'
            ELSE ''
       END AS p_ora9ind
FROM dual;
-- Determine how we will transform the data_type column based on database version. --END


-- Determine how we will get certain databasae and Dat Guard parameters based on databasee version. --BEGIN
COLUMN p_dg_valid_role         new_value v_dg_valid_role         noprint
COLUMN p_dg_verify             new_value v_dg_verify             noprint
COLUMN p_db_unique_name        new_value v_db_unique_name        noprint
COLUMN p_platform_name         new_value v_platform_name         noprint
SELECT
        '''N/A''' AS p_dg_valid_role,
        '''N/A''' AS p_dg_verify,
        'name'    AS p_db_unique_name,
        '''N/A''' AS p_platform_name
FROM DUAL
WHERE '&v_dbversion' LIKE '9%'
UNION
SELECT
        REPLACE('REPLACE(valid_role ,"|", " ")', chr(34), chr(39)) ,
        'verify'  ,
        'db_unique_name' AS p_db_unique_name,
        'platform_name'  AS p_platform_name
FROM DUAL
WHERE '&v_dbversion' NOT LIKE '9%';
-- Determine how we will get certain databasae and Dat Guard parameters based on databasee version. --END


-- Define a source id that will be consistent regardless of which RAC instance we are connected to.  --BEGIN
column vname new_value v_name noprint
SELECT min(object_name) AS vname
FROM dba_objects
WHERE object_name IN ('V$INSTANCE', 'GV$INSTANCE');

BEGIN
SELECT lower(i.host_name||'_'||&v_db_unique_name||'_'||d.dbid) INTO :v_dma_source_id
FROM (
	SELECT version, host_name
	FROM &&v_name
	WHERE instance_number = (SELECT min(instance_number) FROM &&v_name) ) i, v$database d;
END;
/
-- Define a source id that will be consistent regardless of which RAC instance we are connected to.  --END


-- Determine if we are in a container database, and if it supports editioning.  --BEGIN
var lv_tblprefix VARCHAR2(3);
var lv_is_container NUMBER;
var lv_editionable_col  VARCHAR2(20);
var lv_do_pluggable     VARCHAR2(40);
var lv_db_container_col VARCHAR2(30);

DECLARE
  cnt NUMBER;
BEGIN
  :lv_tblprefix := 'dba';
  :lv_is_container := 0;
  :lv_editionable_col := '''N/A''';
  :lv_do_pluggable := 'op_collect_nopluggable_info.sql';
  :lv_db_container_col := '''N/A''';

  SELECT count(1) INTO cnt FROM dba_tab_columns WHERE owner ='SYS' AND table_name = 'V_$DATABASE' AND column_name = 'CDB';
  IF cnt > 0 THEN
    EXECUTE IMMEDIATE 'SELECT count(1) FROM v$database WHERE cdb = ''YES'' ' INTO cnt;
    IF cnt > 0 THEN
      :lv_tblprefix := 'cdb' ;
      :lv_is_container := 1;
      :lv_do_pluggable := 'op_collect_pluggable_info.sql';
      :lv_db_container_col := 'cdb';
    END IF;
  END IF;

  SELECT count(1) INTO cnt  FROM dba_tab_columns WHERE owner ='SYS' AND table_name = 'DBA_OBJECTS' AND column_name ='EDITIONABLE';
  IF cnt > 0 THEN :lv_editionable_col := 'EDITIONABLE';
  END IF;
END;
/

SELECT :lv_tblprefix AS p_tblprefix,
       :lv_is_container AS p_is_container,
       :lv_editionable_col AS p_editionable_col,
       :lv_do_pluggable AS p_dopluggable,
       :lv_db_container_col as p_db_container_col
FROM DUAL;
/
-- Determine if we are in a container database, and if it supports editioning.  --END

-- Determine if the database version supports the 'default_value' column in v$parameter --BEGIN
-- and iff it supports the 'logging' column in dba_pbs.
DECLARE
  cnt NUMBER;
BEGIN
  IF '&v_dbversion'  = '121' THEN
    SELECT count(1) INTO cnt FROM dba_tab_columns WHERE owner ='SYS' AND table_name ='V_$SYSTEM_PARAMETER' AND column_name ='DEFAULT_VALUE';
    IF cnt = 0 THEN
      :dflt_value_flag := 'N';
    ELSE
      :dflt_value_flag := 'Y';
    END IF;

    SELECT count(1) INTO cnt FROM dba_tab_columns WHERE owner = 'SYS' AND table_name ='DBA_PDBS' AND column_name ='LOGGING';
    IF cnt = 0 THEN
      :pdb_logging_flag := 'N';
    ELSE
      :pdb_logging_flag := 'Y';
    END IF;
  ELSE IF  '&v_dbversion'  LIKE '11%' OR  '&v_dbversion'  LIKE '10%'  OR  '&v_dbversion'  LIKE '9%'  THEN
          :dflt_value_flag := 'N';
          :pdb_logging_flag := 'N';
       END IF;
  END IF;
END;
/

SELECT CASE WHEN :dflt_value_flag = 'N' THEN '''N/A''' ELSE 'DEFAULT_VALUE' END as p_dbparam_dflt_col ,
       CASE WHEN :pdb_logging_flag = 'N' THEN  '''N/A''' ELSE 'LOGGING' END AS p_pluggablelogging,
       CASE WHEN '&v_dbversion' LIKE '10%' OR  '&v_dbversion' = '111' THEN 'sqlcmd10g.sql' ELSE 'sqlcmd.sql' END AS p_sqlcmd
FROM DUAL;
-- Determine if the database version supports the 'default_value' column in v$parameter --END


-- Determine if we can check for table compression.  BEGIN
DECLARE
  cnt NUMBER;
BEGIN
  SELECT count(1) INTO cnt FROM dba_tab_columns WHERE table_name = 'DBA_TABLES' AND column_name ='COMPRESS_FOR';
  IF cnt = 1 THEN :b_compress_col := 'COMPRESS_FOR';
  ELSE
    SELECT count(1) INTO cnt FROM dba_tab_columns WHERE table_name = 'DBA_TABLES' AND column_name = 'COMPRESSION';
    IF cnt = 1 THEN :b_compress_col := 'COMPRESSION';
    END IF;
  END IF;
END;
/

SELECT :b_compress_col AS p_compress_col FROM dual;
-- Determine if we can check for table compression.  END


-- Determine if we can collect IO stats based on requested performance stats source.   BEGIN
DECLARE
cnt NUMBER;
BEGIN
  SELECT SUM(cnt) INTO cnt FROM (
    SELECT count(1) FROM dba_views WHERE (view_name = 'DBA_HIST_IOSTAT_FUNCTION' AND '&v_dodiagnostics' = 'usediagnostics')
    UNION
    SELECT count(1) FROM dba_tables WHERE (table_name ='STATS$IOSTAT_FUNCTION_NAME' AND '&v_dodiagnostics' = 'nodiagnostics' AND OWNER ='PERFSTAT')
  );
  IF (cnt > 0 ) THEN :b_io_function_sql := 'iofunction.sql';
  ELSE
    :b_io_function_sql := 'noop.sql';
  END IF;
END;
/

SELECT :b_io_function_sql AS p_io_function_sql FROM dual;
-- Determine if we can collect IO stats based on requested performance stats source.   END


-- Get the DBID - BEGIN
-- Determine if we are running on a standby database and if so, check if it is recording AWR stats via DBMS_UMF.
-- Fail if running on a standby without DBMS_UMF configured.
-- Defines from where we will get the dbid for collection.
set serveroutput on
DECLARE cnt NUMBER;
BEGIN
  SELECT count(1) INTO cnt FROM v$database WHERE database_role = 'PHYSICAL STANDBY';
  IF (cnt != 0) THEN
    SELECT count(1) INTO cnt FROM all_objects WHERE object_name = 'DBMS_UMF';
    IF (cnt > 0) THEN :umfflag := 'dbms_umf.get_node_id_local';
    ELSE raise_application_error(-20002, 'This physical standby database is not configured to store historical performance data or this user does not have correct privileges.');
    END IF;
  ELSE :umfflag := 'dbid';
  END IF;
END;
/

-- Use the results from above to set the variable we will use to get the dbid.
SELECT
:umfflag umf_test
FROM dual
/

-- Finally get the dbid from the determined source.
SELECT &v_umf_test p_dbid
FROM   v$database
/
-- Get the DBID - END


-- Determine if this version of the database supports LOB compression and set the substitution variables -- BEGIN
DECLARE
  cnt NUMBER;
BEGIN
  SELECT count(1) INTO cnt FROM dba_tab_columns WHERE table_name = 'DBA_LOBS' AND column_name = 'COMPRESSION';
  IF cnt = 1 THEN
    :b_lob_compression_col         := 'l.compression';
    :b_lob_part_compression_col    := 'lp.compression';
    :b_lob_subpart_compression_col := 'lsp.compression';
    :b_lob_dedup_col               := 'l.deduplication';
    :b_lob_part_dedup_col          := 'lp.deduplication';
    :b_lob_subpart_dedup_col       := 'lsp.deduplication';
  ELSE
    :b_lob_compression_col         := '''N/A''';
    :b_lob_part_compression_col    := '''N/A''';
    :b_lob_subpart_compression_col := '''N/A''';
    :b_lob_dedup_col               := '''N/A''';
    :b_lob_part_dedup_col          := '''N/A''';
    :b_lob_subpart_dedup_col       := '''N/A''';
  END IF;
END;
/

SELECT
:b_lob_compression_col         AS p_lob_compression_col ,
:b_lob_part_compression_col    AS p_lob_part_compression_col ,
:b_lob_subpart_compression_col AS p_lob_subpart_compression_col ,
:b_lob_dedup_col               AS p_lob_dedup_col ,
:b_lob_part_dedup_col          AS p_lob_part_dedup_col ,
:b_lob_subpart_dedup_col       AS p_lob_subpart_dedup_col
FROM DUAL;

-- Determine if this version of the database supports LOB compression and set the substitution variables -- END


-- Determine if this version of the database supports invisible indexes -- BEGIN
DECLARE
  cnt NUMBER;
BEGIN
  SELECT count(1) INTO cnt FROM dba_tab_columns WHERE table_name = 'DBA_INDEXES' AND column_name = 'VISIBILITY';
  IF cnt = 1 THEN
    :b_index_visibility := 'VISIBILITY';
  ELSE
    :b_index_visibility := '''N/A''';
  END IF;
END;
/

SELECT :b_index_visibility AS p_index_visibility FROM DUAL;

-- Determine if this version of the database supports invisible indexes -- END


-- This is where we determine which source (AWR, STATSPACK or NONE) we will use for performance metrics -- BEGiN
-- and which snaps we will collect.
variable sp VARCHAR2(100);
variable v_info_prompt VARCHAR2(200);
column sp_script new_value p_sp_script noprint
column info_prompt new_value p_info_prompt noprint

set termout on
set serveroutput on
DECLARE
  cnt NUMBER;
  l_tab_name VARCHAR2(100) := '---';
  l_col_name VARCHAR2(100);
  the_sql VARCHAR2(1000) := '---';
  table_does_not_exist EXCEPTION;
  PRAGMA EXCEPTION_INIT (table_does_not_exist, -00942);
BEGIN
  -- Set default performance metrics to NONE.
  :sp  := 'prompt_nostatspack.sql';

  -- Use AWR repository if requested.
  IF '&v_dodiagnostics' = 'usediagnostics' THEN
     l_tab_name := 'DBA_HIST_SNAPSHOT';
     l_col_name := 'begin_interval_time';

  -- If STATSPACK has been requested, check that it is installed and permissions granted.
  ELSE IF '&v_dodiagnostics' = 'nodiagnostics' THEN
         SELECT count(1) INTO cnt FROM all_tables WHERE owner ='PERFSTAT' AND table_name IN ('STATS$OSSTAT', 'STATS$OSSTATNAME', 'STATS$SNAPSHOT', 'STATS$SQL_SUMMARY', 'STATS$SYSSTAT', 'STATS$SYSTEM_EVENT', 'STATS$SYS_TIME_MODEL', 'STATS$TIME_MODEL_STATNAME');

         -- If we have access to STATSPACK, use STATSPACK as the source of performance metrics
 	 IF cnt = 8 THEN
           :sp := 'op_collect_statspack.sql';
           l_tab_name := 'STATS$SNAPSHOT';
           l_col_name := 'snap_time';
         END IF;
       -- If instructed to not collect performance metrics, do not collect stats.
       ELSE IF  '&v_dodiagnostics' = 'nostatspack' THEN
              :sp  := 'prompt_nostatspack.sql';
            -- If we get here, then there was a problem.
            ELSE l_tab_name :=  'ERROR - Unexpected parameter: &v_dodiagnostics';
            END IF;
       END IF;
  END IF;

  BEGIN
    IF l_tab_name = '---' THEN
        dbms_output.put_line('No performance data will be collected.');
    ELSE
      -- Verify there are metrics to collect.
      BEGIN
        EXECUTE IMMEDIATE 'SELECT count(1) FROM ' || upper(l_tab_name) || ' WHERE rownum < 2' INTO cnt ;
        IF cnt = 0 THEN
            dbms_output.put_line('No data found in ' ||  upper(l_tab_name) || '.  No performance data will be collected.');
        END IF;
        EXCEPTION WHEN table_does_not_exist THEN
          RAISE_APPLICATION_ERROR(-20002, 'This user does not have SELECT privileges on ' || upper(l_tab_name) || '.  Please ensure the grants_wrapper.sql script has been executed for this user.');
      END;
    END IF;
  END;
  IF (l_tab_name != '---' AND l_tab_name NOT LIKE 'ERROR%') THEN
     -- Get the snapshot range for AWR stats.
     IF l_tab_name = 'DBA_HIST_SNAPSHOT' THEN
       THE_SQL := 'SELECT min(snap_id) , max(snap_id) FROM ' || l_tab_name || ' WHERE ' || l_col_name || ' >= (sysdate- &&dtrange ) AND dbid = :1 ';
       EXECUTE IMMEDIATE the_sql INTO  :minsnap, :maxsnap USING '&&v_dbid' ;
       IF :minsnap IS NULL THEN
          dbms_output.put_line('Warning: No snapshots found within the last &&dtrange days.  No performance data will be extracted.');
          :minsnap := -1;
          :maxsnap := -1;
          :v_info_prompt := 'without performance data';
       ELSE
          :v_info_prompt := 'between snaps ' || :minsnap || ' and ' || :maxsnap;
       END IF;
     ELSE
       -- Get the snapshot range for STATSPACE stats.
       THE_SQL := 'SELECT min(snap_time) , max(snap_time) FROM ' || l_tab_name || ' WHERE ' || l_col_name || ' >= (sysdate- &&dtrange ) AND dbid = :1 ';
       EXECUTE IMMEDIATE the_sql INTO  :minsnaptime, :maxsnaptime USING '&&v_dbid' ;
       IF :minsnaptime IS NULL THEN
          dbms_output.put_line('Warning: No snapshots found within the last &&dtrange days.  No performance data will be extracted.');
          :minsnaptime := sysdate;
          :maxsnaptime := sysdate;
          :v_info_prompt := 'without performance data';
       ELSE
          :v_info_prompt := 'between  ' || :minsnaptime || ' and ' || :maxsnaptime;
       END IF;
     END IF;
  ELSE
     :v_info_prompt := 'without performance data';
  END IF;
END;
/
set termout off
SELECT NVL(:minsnap, -1) min_snapid, NVL(:maxsnap, -1) max_snapid, NVL(:minsnaptime, SYSDATE) min_snaptime, NVL(:maxsnaptime, SYSDATE) max_snaptime,  :sp sp_script, :v_info_prompt info_prompt FROM dual;

set termout on
PROMPT Collecting data for database &v_dbname '&&v_dbid' &p_info_prompt
PROMPT

set termout &TERMOUTOFF

COLUMN min_snapid clear
COLUMN max_snapid clear
COLUMN min_snaptime clear
COLUMN max_snaptime clear

-- This is where we determine which source (AWR, STATSPACK or NONE) we will use for performance metrics -- END



-- This is where we set the substitution variables for working within container databases. -- BEGIN
column a_con_id new_value v_a_con_id noprint
column b_con_id new_value v_b_con_id noprint
column c_con_id new_value v_c_con_id noprint
column d_con_id new_value v_d_con_id noprint
column p_con_id new_value v_p_con_id noprint
column h_con_id new_value v_h_con_id noprint
SELECT CASE WHEN &v_is_container != 0 THEN 'a.con_id' ELSE '''N/A''' END as a_con_id,
       CASE WHEN &v_is_container != 0 THEN 'b.con_id' ELSE '''N/A''' END as b_con_id,
       CASE WHEN &v_is_container != 0 THEN 'c.con_id' ELSE '''N/A''' END as c_con_id,
       CASE WHEN &v_is_container != 0 THEN 'd.con_id' ELSE '''N/A''' END as d_con_id,
       CASE WHEN &v_is_container != 0 THEN 'p.con_id' ELSE '''N/A''' END as p_con_id,
       CASE WHEN &v_is_container != 0 THEN 'FORMAT 999999' ELSE 'FORMAT A6' END as h_con_id
FROM DUAL;

column CON_ID &v_h_con_id
-- This is where we set the substitution variables for working within container databases. -- END

-- Set manual_unique_id
BEGIN
  :v_manual_unique_id :=  chr(39) || '&v_manualUniqueId' || chr(39);
END;
/


-- Session settings for output
set numwidth 48
column v_dma_source_id format a100
column v_dma_manual_id format a100
column dma_source_id format a100
column dma_manual_id format a100
column pkey format a100
