MINI MINI MANI MO
Rem
Rem $Header: rdbms/admin/utltzuv2.sql /main/14 2017/05/28 22:46:13 stanaya Exp $
Rem
Rem utltzuv2.sql
Rem
Rem Copyright (c) 2003, 2017, Oracle and/or its affiliates.
Rem All rights reserved.
Rem
Rem NAME
Rem utltzuv2.sql - time zone file upgrade to a new version script
Rem
Rem DESCRIPTION
Rem The contents of the files timezone.dat and timezlrg.dat are
Rem usually updated to a new version to reflect the transition rule
Rem changes for some time zone region names. The transition rule
Rem changes of some time zones might affect the column data of
Rem TIMESTAMP WITH TIME ZONE data type. For example, if users
Rem enter TIMESTAMP '2003-02-17 09:00:00 America/Sao_Paulo',
Rem we convert the data to UTC based on the transition rules in the
Rem time zone file and store them on the disk. So '2003-02-17 11:00:00'
Rem along with the time zone id for 'America/Sao_Paulo' is stored
Rem because the offset for this particular time is '-02:00' . Now the
Rem transition rules are modified and the offset for this particular
Rem time is changed to '-03:00'. when users retrieve the data,
Rem they will get '2003-02-17 08:00:00 America/Sao_Paulo'. There is
Rem one hour difference compared to the original value.
Rem
Rem Refer to $ORACLE_HOME/oracore/zoneinfo/readme.txt for detailed
Rem information about time zone file updates.
Rem
Rem This script should be run before you update your database's
Rem time zone file to the latest version. This is a pre-update script.
Rem
Rem This script first determines the time zone version currently in use
Rem before the upgrade. It then queries an external table to get all the
Rem affected timezone regions between the current version (version before
Rem the update) and the latest one. This external table points to the
Rem file of $ORACLE_HOME/oracore/zoneinfo/timezdif.csv, which contains
Rem all the affected time zone names in each version. Please make sure
Rem that you have the latest version of the timezdif.csv (the one
Rem corresponding to the latest time zone data files) before you run this
Rem check script.
Rem
Rem Then, this script scans the database to find out all columns
Rem of TIMESTAMP WITH TIME ZONE data type. If the column is defined
Rem directly on TIMESTAMP WITH TIME ZONE data type or ADT data type
Rem defined on TIMESTAMP WITH TIME ZONE excluding VARRAY, the script
Rem also finds out how many rows might be affected by checking whether
Rem the column data contain the values for the affected time zone names.
Rem The results are stored in table sys.sys_tzuv2_temptab. If the column
Rem is defined on VARRAY with TIMESTAMP WITH TIME ZONE embedded, we do NOT
Rem scan the data to find out how many rows are affected but we still
Rem report the table and column information, which are stroed in table
Rem sys.sys_tzuv2_va_tmptab;
Rem
Rem Before running this script, make sure that the following tempoary
Rem table names do not confict with any existing table objects in
Rem your database.
Rem
Rem sys.sys_tzuv2_temptab
Rem sys.sys_tzuv2_temptab1
Rem sys.sys_tzuv2_va_temptab
Rem sys.sys_tzuv2_va_temptab1
Rem
Rem If they do, change the the above table names in the script to other
Rem names.
Rem
Rem If your database has column data that will be affected by the
Rem time zone file update, dump the data before you upgrade to the
Rem new version. After the upgrade, you need update the data
Rem to make sure the data is stored based on the new rules in the
Rem new version of time zone files.
Rem
Rem For example, user scott has a table tztab:
Rem
Rem CREATE TABLE tztab(
Rem x NUMBER PRIMARY KEY,
Rem y TIMESTAMP WITH TIME ZONE);
Rem
Rem INSERT INTO tztab VALUES(1, TIMESTAMP '...');
Rem
Rem Before upgrade, you can create a table tztab_back, note
Rem column y here is defined as VARCHAR2 to preserve the original
Rem value.
Rem
Rem CREATE TABLE tztab_back(
Rem x NUMBER PRIMARY KEY,
Rem y VARCHAR2(256));
Rem
Rem INSERT INTO tztab_back
Rem SELECt x, to_char(y, 'YYYY-MM-DD HH24.MI.SSXFF TZR')
Rem FROM tztab;
Rem
Rem After upgrade, you need update the data in the table tztab using
Rem the value in tztab_back.
Rem
Rem UPDATE tztab t SET
Rem t.y = (SELECT TO_TIMESTAMP_TZ(t1.y, 'YYYY-MM-DD HH24.MI.SSXFF TZR')
Rem FROM tztab_back t1
Rem WHERE t.x=t1.x);
Rem
Rem Once you are done with the time zone files upgrade and patch of
Rem TIMESTAMP WITH TIME ZONE data. Please drop the following temporary
Rem tables:
Rem
Rem drop table sys.sys_tzuv2_temptab;
Rem drop table sys.sys_tzuv2_temptab1;
Rem drop table sys.sys_tzuv2_va_temptab;
Rem drop table sys.sys_tzuv2_va_temptab1
Rem
Rem NOTES
Rem 1. This script needs to be run before upgrading to a new version time
Rem zone file. Also, before running this script, make sure that you
Rem get the latest version of timezdif.csv file.
Rem
Rem 2. This script must be run using SQL*PLUS.
Rem
Rem 3. You must be connected AS SYSDBA to run this script.
Rem
Rem 4. This script is created only for Oracle 10.1 or higer. A separate
Rem script is provided for Oracle 9i.
Rem
Rem 5. tzuv2ext_*.log and tzuv2ext_*.bad will be created in the directory
Rem of $ORACLE_HOME/oracore/zoneinfo when using the external table
Rem for $ORACLE_HOME/oracore/zoneinfo/timezdif.csv file to get all the
Rem affected time zone names.
Rem
Rem After running the script, please refer to these two files to see
Rem if there are any rows in timezdif.csv, which are not loaded in
Rem table sys.sys_tzuv2_affected_regions. If so, it might affect the
Rem correct selection of affected TIMESTAMP WITH TIME ZONE tables in
Rem the database. You can always delete tzuv2ext*.log & tzuv2ext*.bad.
Rem
Rem Path separator is obtained by querying v$database for platform_id.
Rem If it is windows platform, path separator uses '\'. If it is Unix
Rem platform, path separator uses '/'.
Rem
Rem
Rem BEGIN SQL_FILE_METADATA
Rem SQL_SOURCE_FILE: rdbms/admin/utltzuv2.sql
Rem SQL_SHIPPED_FILE: rdbms/admin/utltzuv2.sql
Rem SQL_PHASE: UTILITY
Rem SQL_STARTUP_MODE: NORMAL
Rem SQL_IGNORABLE_ERRORS: NONE
Rem END SQL_FILE_METADATA
Rem
Rem MODIFIED (MM/DD/YY)
Rem traney 04/05/11 - 35209: long identifiers dictionary upgrade
Rem huagli 04/09/10 - 9559503: fix possible SQL injection issue
Rem huagli 01/07/10 - 7256209: enhancements and code clean up
Rem yifeng 03/06/07 - 5923970: added functionalities to detect and
Rem count the rows affected by TZ ver changes in
Rem typed table and nested tables. Added
Rem functionalities to detect tables and columns of
Rem affected varrays.
Rem huagli 01/24/07 - 5838646:new line marker changed to '\n' when
Rem reading from the external table
Rem 5844057:HP OpenVMS has a specific logic to
Rem provide the UNIX path equivalent to ORACLE_HOME,
Rem which is ORACLE_HOME_UNIX.
Rem huagli 10/11/06 - code hygiene
Rem huagli 07/31/06 - time zone update
Rem srsubram 12/27/05 - convert in-list into a join query
Rem srsubram 11/06/05 - 4616517:execute count query in parallel
Rem srsubram 05/12/05 - 4331865:Modify script to work with prior
Rem releases
Rem lkumar 05/11/04 - Fix lrg 1691434.
Rem rchennoj 12/02/03 - Fix query
Rem qyu 11/22/03 - qyu_bug-3236585
Rem qyu 11/17/03 - Created
Rem
SET SERVEROUTPUT ON
DECLARE
dbv VARCHAR2(10);
dbtzv VARCHAR2(5);
numrows NUMBER;
TYPE cursor_t IS REF CURSOR;
cursor_tstz cursor_t;
cursor_nt_tstz cursor_t;
tstz_owner VARCHAR2(30);
tstz_tname VARCHAR2(30);
tstz_qcname VARCHAR2(4000);
eqtstz_qcname VARCHAR2(4000);
tz_version NUMBER;
oracle_home VARCHAR(4000);
tz_count INTEGER;
tz_numrows INTEGER;
plsql_block VARCHAR2(200);
file_separator VARCHAR2(3);
pfid NUMBER;
current_user VARCHAR2(30);
insert_stmt VARCHAR2(4000);
-- constant for double quote
DBLQT CONSTANT VARCHAR2(2) := '"';
-- constants defined for platform ID
PLATFORM_WINDOWS32 CONSTANT BINARY_INTEGER := 7;
PLATFORM_WINDOWS64 CONSTANT BINARY_INTEGER := 8;
PLATFORM_WINDOWS64AMD CONSTANT BINARY_INTEGER := 12;
PLATFORM_OPENVMS CONSTANT BINARY_INTEGER := 15;
BEGIN
--========================================================================
-- First make sure that the check script is running by SYS
--========================================================================
EXECUTE IMMEDIATE
'SELECT SYS_CONTEXT(''userenv'', ''current_user'') FROM dual'
INTO current_user;
IF current_user != 'SYS' THEN
DBMS_OUTPUT.PUT_LINE('This check script must be run through user "SYS".');
RETURN;
END IF;
--========================================================================
-- Make sure that only version 10 or higher uses this script
--========================================================================
EXECUTE IMMEDIATE 'SELECT substr(version,1,6) FROM v$instance' INTO dbv;
IF dbv = '8.1.7.' THEN
DBMS_OUTPUT.PUT_LINE('TIMESTAMP WITH TIME ZONE data type was not ' ||
'supported in Release 8.1.7.');
DBMS_OUTPUT.PUT_LINE('No need to validate TIMESTAMP WITH TIME ZONE data.');
RETURN;
END IF;
IF dbv in ('9.0.1.','9.2.0.') THEN
DBMS_OUTPUT.PUT_LINE('There are no time zone version changes ' ||
'for Release 9.0.1 or 9.2.0. Customers with ' ||
'extended maintenance support can be always ' ||
'provided with new time zone version files and ' ||
'the updated 9i style script if needed.');
RETURN;
END IF;
--========================================================================
-- Create temporary tables used by the check script
--========================================================================
EXECUTE IMMEDIATE
'SELECT count(*) FROM ALL_ALL_TABLES
WHERE owner = ''SYS'' and table_name = ''SYS_TZUV2_TEMPTAB'''
INTO tz_count;
IF tz_count <> 0 THEN
EXECUTE IMMEDIATE 'DROP TABLE sys.sys_tzuv2_temptab';
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE sys.sys_tzuv2_temptab (
table_owner VARCHAR2(128),
table_name VARCHAR2(128),
column_name VARCHAR2(4000),
rowcount NUMBER,
nested_tab VARCHAR2(3)
)';
EXECUTE IMMEDIATE
'SELECT count(*) FROM ALL_ALL_TABLES
WHERE owner = ''SYS'' and table_name = ''SYS_TZUV2_TEMPTAB1'''
INTO tz_count;
IF tz_count <> 0 THEN
EXECUTE IMMEDIATE 'DROP TABLE sys.sys_tzuv2_temptab1';
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE sys.sys_tzuv2_temptab1 (
time_zone_name VARCHAR2(60)
)';
EXECUTE IMMEDIATE
'SELECT count(*) FROM ALL_ALL_TABLES
WHERE owner = ''SYS'' and
table_name = ''SYS_TZUV2_VA_TEMPTAB'''
INTO tz_count;
IF tz_count <> 0 THEN
EXECUTE IMMEDIATE 'DROP TABLE sys.sys_tzuv2_va_temptab';
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE sys.sys_tzuv2_va_temptab(
table_owner VARCHAR2(128),
table_name VARCHAR2(128),
column_name VARCHAR2(4000),
nested_tab VARCHAR2(3)
)';
EXECUTE IMMEDIATE
'SELECT count(*) FROM ALL_ALL_TABLES
WHERE owner = ''SYS'' and
table_name = ''SYS_TZUV2_VA_TEMPTAB1'''
INTO tz_count;
IF tz_count <> 0 THEN
EXECUTE IMMEDIATE 'DROP TABLE sys.sys_tzuv2_va_temptab1';
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE sys.sys_tzuv2_va_temptab1(
va_of_tstz_typ VARCHAR2(128)
)';
--========================================================================
-- Get $ORACLE_HOME
--========================================================================
EXECUTE IMMEDIATE 'SELECT platform_id FROM v$database'
INTO pfid;
plsql_block := 'BEGIN SYS.DBMS_SYSTEM.GET_ENV(:1, :2); END;';
IF pfid = PLATFORM_OPENVMS THEN
EXECUTE IMMEDIATE plsql_block USING 'ORACLE_HOME_UNIX', OUT oracle_home;
ELSE
EXECUTE IMMEDIATE plsql_block USING 'ORACLE_HOME', OUT oracle_home;
END IF;
--========================================================================
-- Use an external table created on timezdif.csv file to get the
-- affected time zones. In this way, every time when time zone information
-- changes, we only need to provide user with the updated timezdif.csv file
-- without changing utltzuv2.sql.
--
-- 1. Setup the directory for timezdif.csv and log files(log, bad log)
-- 2. Check any existing external table with this name
-- sys.sys_tzuv2_affected_regions
-- 3. Setup the parameters of the external table
--========================================================================
-- get the file separator by looking at the platform ID
IF pfid = PLATFORM_WINDOWS32 OR pfid = PLATFORM_WINDOWS64 OR
pfid = PLATFORM_WINDOWS64AMD
THEN
file_separator := '\';
ELSE
file_separator := '/';
END IF;
EXECUTE IMMEDIATE 'CREATE OR REPLACE DIRECTORY timezdif_dir AS ''' ||
oracle_home || file_separator || 'oracore' ||
file_separator || 'zoneinfo''';
EXECUTE IMMEDIATE
'SELECT count(*) FROM ALL_ALL_TABLES
WHERE owner = ''SYS'' and table_name = ''SYS_TZUV2_AFFECTED_REGIONS'''
INTO tz_count;
IF tz_count <> 0 THEN
EXECUTE IMMEDIATE 'DROP TABLE sys.sys_tzuv2_affected_regions';
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE sys.sys_tzuv2_affected_regions
(
version NUMBER,
time_zone_name VARCHAR2(40),
from_year NUMBER,
to_year NUMBER
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY timezdif_dir
ACCESS PARAMETERS
(
records delimited by ''\n''
skip 3
badfile timezdif_dir:''tzuvext%a_%p.bad''
logfile timezdif_dir:''tzuvext%a_%p.log''
fields terminated by '',''
lrtrim
missing field values are null
(
version, time_zone_name, from_year, to_year
)
)
LOCATION (''timezdif.csv'')
)
REJECT LIMIT UNLIMITED';
EXECUTE IMMEDIATE 'SELECT count(*) FROM sys.sys_tzuv2_affected_regions'
INTO tz_numrows;
IF tz_numrows = 0 THEN
DBMS_OUTPUT.PUT_LINE('The external table ' ||
'sys.sys_tzuv2_affected_regions is not populated ' ||
'correctly.');
DBMS_OUTPUT.PUT_LINE('Please contact Oracle Support for this issue.');
RETURN;
END IF;
--======================================================================
-- Check if the TIMEZONE data is consistent with the latest version.
--======================================================================
EXECUTE IMMEDIATE 'SELECT version FROM v$timezone_file' INTO tz_version;
EXECUTE IMMEDIATE 'SELECT MAX(version) FROM sys_tzuv2_affected_regions'
INTO dbtzv;
IF tz_version = dbtzv THEN
DBMS_OUTPUT.PUT_LINE('The time zone file used for your current RDBMS ' ||
'has the same version (ver ' || dbtzv || ') ' ||
'as what this check script checks. No need to ' ||
'apply the patch of time zone files and ' ||
'TIMESTAMP WITH TIME ZONE data.');
DBMS_OUTPUT.PUT_LINE('If you want to check for another time zone ' ||
'version other than version ' || dbtzv || ', ' ||
'please install the corresponding timezdif.csv ' ||
'file and then run this check script for that ' ||
'version.');
RETURN;
END IF;
--======================================================================
-- Get tables with columns defined as type TIMESTAMP WITH TIME ZONE.
--======================================================================
OPEN cursor_tstz FOR
'SELECT atc.owner, atc.table_name, atc.qualified_col_name
FROM "ALL_TAB_COLS" atc, "ALL_ALL_TABLES" at
WHERE data_type LIKE ''TIMESTAMP%WITH TIME ZONE'' AND
atc.owner = at.owner AND atc.table_name = at.table_name';
--======================================================================
-- Query the external table to get all the affected time zones based
-- on the current database time zone version, and then put them into
-- a temporary table, sys_tzuv2_temptab1.
--======================================================================
EXECUTE IMMEDIATE
'INSERT INTO sys.sys_tzuv2_temptab1
SELECT DISTINCT time_zone_name
FROM sys.sys_tzuv2_affected_regions t
WHERE t.version > ' || tz_version;
EXECUTE IMMEDIATE 'ANALYZE TABLE sys.sys_tzuv2_temptab1 ' ||
'COMPUTE STATISTICS';
--======================================================================
-- Check regular table columns.
--======================================================================
insert_stmt := 'INSERT INTO sys.sys_tzuv2_temptab VALUES(:1,:2,:3,:4,:5)';
LOOP
BEGIN
FETCH cursor_tstz INTO tstz_owner, tstz_tname, tstz_qcname;
EXIT WHEN cursor_tstz%NOTFOUND;
-- If the qualified column is not in the format of "X"."Y"."Z",
-- double enquote the column.
IF INSTR(tstz_qcname, '.') = 0 OR INSTR(tstz_qcname, '"') = 0 THEN
eqtstz_qcname := DBLQT || tstz_qcname || DBLQT;
ELSE
eqtstz_qcname := tstz_qcname;
END IF;
EXECUTE IMMEDIATE
'SELECT /*+ USE_HASH (r t) */ COUNT(1) FROM ' ||
DBLQT || tstz_owner || DBLQT || '.' ||
DBLQT || tstz_tname || DBLQT || ' t, ' ||
'sys.sys_tzuv2_temptab1 r ' ||
'WHERE UPPER(r.time_zone_name) = ' ||
'UPPER(TO_CHAR(t.' || eqtstz_qcname || ', ''TZR'')) '
INTO numrows;
IF numrows > 0 THEN
EXECUTE IMMEDIATE insert_stmt USING
tstz_owner, tstz_tname, tstz_qcname, numrows, 'NO';
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('OWNER : ' || tstz_owner);
DBMS_OUTPUT.PUT_LINE('TABLE : ' || tstz_tname);
DBMS_OUTPUT.PUT_LINE('COLUMN : ' || tstz_qcname);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
END LOOP;
--======================================================================
-- Check nested table columns.
--======================================================================
OPEN cursor_nt_tstz FOR
'SELECT owner, table_name, qualified_col_name ' ||
'FROM "ALL_NESTED_TABLE_COLS" ' ||
'WHERE data_type LIKE ''TIMESTAMP%WITH TIME ZONE'' ';
LOOP
BEGIN
FETCH cursor_nt_tstz INTO tstz_owner, tstz_tname, tstz_qcname;
EXIT WHEN cursor_nt_tstz%NOTFOUND;
-- If the qualified column is not in the format of "X"."Y"."Z",
-- double enquote the column.
IF INSTR(tstz_qcname, '.') = 0 OR INSTR(tstz_qcname, '"') = 0 THEN
eqtstz_qcname := DBLQT || tstz_qcname || DBLQT;
ELSE
eqtstz_qcname := tstz_qcname;
END IF;
EXECUTE IMMEDIATE
'SELECT /*+ NESTED_TABLE_GET_REFS USE_HASH(r t) */ COUNT(1) FROM ' ||
DBLQT || tstz_owner || DBLQT || '.' ||
DBLQT || tstz_tname || DBLQT || ' t, ' ||
'sys.sys_tzuv2_temptab1 r ' ||
'WHERE UPPER(r.time_zone_name) = ' ||
'UPPER(TO_CHAR(t.' || eqtstz_qcname || ', ''TZR'')) '
INTO numrows;
IF numrows > 0 THEN
EXECUTE IMMEDIATE insert_stmt USING
tstz_owner, tstz_tname, tstz_qcname, numrows, 'YES';
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('OWNER : ' || tstz_owner);
DBMS_OUTPUT.PUT_LINE('TABLE : ' || tstz_tname);
DBMS_OUTPUT.PUT_LINE('COLUMN : ' || tstz_qcname);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
END LOOP;
--======================================================================
-- Check varrays containing TSTZ data type. Note that we do not check
-- TSTZ data embedded in varrays. We just report the table names and
-- column names defined over varray types which contain TSTZ data type.
--======================================================================
-- get VARRAY types containing TSTZ data type
EXECUTE IMMEDIATE
'INSERT INTO sys.sys_tzuv2_va_temptab1
SELECT DISTINCT o.name
FROM
( SELECT p_obj# obj# FROM sys.dependency$
START WITH p_obj# in (
SELECT DISTINCT o.obj#
FROM sys.obj$ o, sys.attribute$ a
WHERE o.oid$ = a.toid AND
a.attr_toid = ''0000000000000000000000000000003E''
UNION ALL
SELECT DISTINCT o.obj#
FROM sys.obj$ o, sys.collection$ c
WHERE o.oid$ = c.toid AND
c.elem_toid = ''0000000000000000000000000000003E'')
CONNECT BY prior d_obj# = p_obj# AND
bitand(prior property, 1) = 1
ORDER SIBLINGS BY d_obj#, p_obj# ) t,
sys.obj$ o, sys.coltype$ c
WHERE t.obj# = o.obj# AND o.oid$ = c.toid AND bitand(c.flags, 8) = 8';
-- Report the tables and column names defined over varray types which
-- contain TSTZ data type.
EXECUTE IMMEDIATE
'INSERT INTO sys.sys_tzuv2_va_temptab
SELECT atc.owner, atc.table_name, atc.qualified_col_name, ''NO''
FROM "ALL_TAB_COLS" atc, "ALL_ALL_TABLES" at, sys.sys_tzuv2_va_temptab1
WHERE data_type = va_of_tstz_typ AND
atc.owner = at.owner AND atc.table_name = at.table_name';
EXECUTE IMMEDIATE
'INSERT INTO sys.sys_tzuv2_va_temptab
SELECT owner, table_name, qualified_col_name, ''YES''
FROM "ALL_NESTED_TABLE_COLS", sys.sys_tzuv2_va_temptab1
WHERE data_type = va_of_tstz_typ';
DBMS_OUTPUT.PUT_LINE('Query sys.sys_tzuv2_temptab and ' ||
'sys.sys_tzuv2_va_temptab tables to see ' ||
'if any TIMESTAMP WITH TIME ZONE data are affected ' ||
'when upgrading from the current time zone ' ||
'version ' || tz_version ||
' to a newer version of ' || dbtzv || '.');
EXCEPTION
WHEN OTHERS THEN
IF INSTR(SQLERRM, 'KUP-04063') != 0
THEN
DBMS_OUTPUT.PUT_LINE('Directory for file timezdif.csv is ' ||
'not correctly specified!');
DBMS_OUTPUT.PUT_LINE(sqlerrm);
ELSIF INSTR(SQLERRM, 'KUP-04040') != 0
THEN
DBMS_OUTPUT.PUT_LINE('File timezdif.csv in TIMEZDIF_DIR not found!');
ELSE
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END IF;
END;
/
Rem=========================================================================
SET SERVEROUTPUT OFF
Rem=========================================================================
OHA YOOOO