Monday, February 14, 2005

Automated alert log archive

Here's a set of scripts (package, PL/SQL and Korn) that when set in place and activated with a Unix cron job, will archive the alert log in a gzipped file. (Note: formatting is messed up in Blogger. Notice that missing pipe characters are missing.)

First, put this in place as a package:

CREATE OR REPLACE PACKAGE alert_log AS
PROCEDURE bdump_dir_setup;
PROCEDURE build_possible_alert_log_names;
PROCEDURE build_alertlog_name;
PROCEDURE check_alertlog_name;
PROCEDURE read(in_date_offset IN NUMBER);
PROCEDURE archive;
PROCEDURE remove(in_date_offset IN NUMBER, number_of_days IN NUMBER);
PROCEDURE list(in_date_offset IN NUMBER);
PROCEDURE INIT_ALERT_LOG;
END alert_log;
/

CREATE OR REPLACE PACKAGE BODY alert_log AS
bdump_dir VARCHAR2(500);
alertlist VARCHAR2(4000);
SID VARCHAR2(100);
comma VARCHAR2(1);
alertfile VARCHAR2(255);
itexists NUMBER;
fileat BFILE;
pointa INTEGER;
pointb INTEGER;
v_length INTEGER;
r_char10 RAW(4);
char10 VARCHAR2(1) := CHR(10);
startdate VARCHAR2(100);
r_startdate RAW(100);
buffer VARCHAR2(800);
buffer2 VARCHAR2(800);
vexists BOOLEAN;
vfile_length NUMBER;
vblocksize NUMBER;
date_offset NUMBER;
date_file VARCHAR2(255);
cursor_id INTEGER;
return_val INTEGER;
file_type UTL_FILE.FILE_TYPE;
sql_statement VARCHAR2(500);

PROCEDURE bdump_dir_setup IS
BEGIN
EXECUTE IMMEDIATE 'SELECT value '
' FROM v$parameter '
' WHERE name = ''background_dump_dest'''
INTO bdump_dir;

EXECUTE IMMEDIATE 'CREATE DIRECTORY BDUMP_DIR '
' AS '''
bdump_dir
'''';
END bdump_dir_setup;

PROCEDURE build_alertlog_name IS
BEGIN
alertlist := NULL;
comma := NULL;

EXECUTE IMMEDIATE 'SELECT value '
' FROM v$parameter '
' WHERE name = ''db_name'''
INTO SID;

build_possible_alert_log_names;

EXECUTE IMMEDIATE 'SELECT value '
' FROM v$parameter '
' WHERE name = ''instance_name'''
INTO SID;

build_possible_alert_log_names;

EXECUTE IMMEDIATE 'SELECT substr(global_name,1,'
' instr(global_name,''.'',-1)-1) '
' FROM global_name'
INTO SID;

build_possible_alert_log_names;
r_char10 := UTL_RAW.cast_to_raw(char10);
END build_alertlog_name;

PROCEDURE check_alertlog_name IS
BEGIN
pointa := 1;

LOOP
pointb := INSTR(alertlist, ',', pointa, 1);
v_length := pointb - pointa;
alertfile := SUBSTR(alertlist, pointa, v_length);
pointa := pointb + 1;
itexists :=
DBMS_LOB.fileexists(BFILENAME('BDUMP_DIR', alertfile));

IF itexists = 1 THEN
pointb := 0;
EXIT;
END IF;
END LOOP;
END check_alertlog_name;

PROCEDURE build_possible_alert_log_names IS
BEGIN
alertlist := alertlist comma 'alert_'
LOWER(SID) '.log';
comma := ',';
alertlist := alertlist comma UPPER(SID)
'ALRT.LOG';
END build_possible_alert_log_names;

PROCEDURE READ(in_date_offset IN NUMBER) IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
build_alertlog_name;
check_alertlog_name;
date_offset := in_date_offset;

IF date_offset = 0 THEN
date_file := alertfile;
ELSE
date_file :=
TO_CHAR(SYSDATE - date_offset, 'YYYYMMDD')
'_'
alertfile;
END IF;

UTL_FILE.fgetattr('BDUMP_DIR',
date_file,
vexists,
vfile_length,
vblocksize);

IF vexists THEN
fileat := BFILENAME('BDUMP_DIR', date_file);
DBMS_LOB.fileopen(fileat, DBMS_LOB.file_readonly);
startdate := TO_CHAR(TRUNC(SYSDATE), 'Dy Mon DD');
r_startdate := UTL_RAW.cast_to_raw(startdate);
pointa := DBMS_LOB.INSTR(fileat, r_startdate, 1, 1);

IF pointa = 0 THEN
EXECUTE IMMEDIATE 'SELECT TO_CHAR(TRUNC(SYSDATE),''Dy Mon '' '
' DECODE(SUBSTR(TO_CHAR(TRUNC(SYSDATE),''DD''),1,1),0,'' '')'
' SUBSTR(TO_CHAR(TRUNC(SYSDATE),''DD''),2,1)'
' FROM dual'
INTO startdate;

r_startdate := UTL_RAW.cast_to_raw(startdate);
pointa := DBMS_LOB.INSTR(fileat, r_startdate, 1, 1);
END IF;

IF pointa != 0 THEN
LOOP
pointb := DBMS_LOB.INSTR(fileat, r_char10, pointa, 1);
EXIT WHEN pointb = 0;
v_length := pointb - pointa;
buffer := DBMS_LOB.SUBSTR(fileat, v_length, pointa);
buffer2 := UTL_RAW.cast_to_varchar2(buffer);
DBMS_OUTPUT.put_line(buffer2);
pointa := pointb + 1;
END LOOP;
END IF;

DBMS_LOB.fileclose(fileat);
END IF;
END READ;

PROCEDURE ARCHIVE IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
build_alertlog_name;
check_alertlog_name;
UTL_FILE.frename('BDUMP_DIR',
alertfile,
'BDUMP_DIR',
TO_CHAR(SYSDATE, 'YYYYMMDD')
'_' alertfile);
SELECT directory_path
INTO bdump_dir
FROM all_directories
WHERE directory_name = 'BDUMP_DIR';

init_alert_log;

DBMS_OUTPUT.put_line('Archived to: '
' bdump_dir '/'
' TO_CHAR(SYSDATE, 'YYYYMMDD')
' '_' alertfile);
END ARCHIVE;

PROCEDURE remove(in_date_offset IN NUMBER,
number_of_days IN NUMBER) IS
BEGIN
build_alertlog_name;
check_alertlog_name;

FOR date_offset IN(in_date_offset - number_of_days)
.. in_date_offset LOOP
date_file :=
TO_CHAR(SYSDATE - date_offset, 'YYYYMMDD') '_'
alertfile;
UTL_FILE.fgetattr('BDUMP_DIR',
date_file,
vexists,
vfile_length,
vblocksize);

IF vexists THEN
UTL_FILE.fremove('BDUMP_DIR', date_file);
DBMS_OUTPUT.put_line(date_file ' - REMOVED');
END IF;
END LOOP;
END remove;

PROCEDURE LIST(in_date_offset IN NUMBER) IS
BEGIN
build_alertlog_name;
check_alertlog_name;

FOR date_offset IN 0 .. in_date_offset LOOP
IF date_offset = 0 THEN
date_file := alertfile;
UTL_FILE.fgetattr('BDUMP_DIR',
date_file,
vexists,
vfile_length,
vblocksize);

IF vexists THEN
DBMS_OUTPUT.put_line( 'OffSet : '
date_offset
', '
date_file
' '
vfile_length);
END IF;
END IF;

date_file :=
TO_CHAR(SYSDATE - date_offset, 'YYYYMMDD') '_'
alertfile;
UTL_FILE.fgetattr('BDUMP_DIR',
date_file,
vexists,
vfile_length,
vblocksize);

IF vexists THEN
DBMS_OUTPUT.put_line( 'OffSet : '
date_offset
', '
date_file
' '
vfile_length);
END IF;
END LOOP;
END LIST;

PROCEDURE INIT_ALERT_LOG IS
BEGIN

build_alertlog_name;
--check_alertlog_name;

-- ******************************************************
-- Open the alert log file for write access
-- ******************************************************
file_type := utl_file.fopen('BDUMP_DIR',alertfile,'W');

-- ******************************************************
-- Write the custom message to the alert log file
-- ******************************************************
utl_file.put_line(file_type,
'Alert log initialized by '
'HP_ALERT_LOG.INIT_ALERT_LOG on '
SYSDATE);

-- ******************************************************
-- Close the alert log file
-- ******************************************************
utl_file.fclose(file_type);

END INIT_ALERT_LOG;
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
END alert_log;
/
Now, save the following as a PL/SQL script:
set serveroutput on;
exec alert_log.archive;
exit;


Finally, save this to a Korn shell script file executable by oracle Unix user only:

#!/bin/ksh
USER=psgPASS=psg4admin
ORATAB=/etc/oratab
# Set path if path not set (if called from /etc/rc)
case $PATH in
"") PATH=/bin:/usr/bin:/etc
export PATH
;;
esac
SAVE_PATH=$PATH
# Save LD_LIBRARY_PATHSAVE_LLP=$LD_LIBRARY_PATH
## Loop for every entry in oratab file and and try to shut down# that ORACLE#
cat $ORATAB while read LINE
do
case $LINE in
\#*) ;; #comment-line in oratab
*)
# Proceed only if third field is 'Y'.
if [ "$(echo $LINE awk -F: '{print $3}' -)" = "Y" ] ; then
ORACLE_SID=$(echo $LINE awk -F: '{print $1}' -)
if [ "$ORACLE_SID" = '*' ] ; then
ORACLE_SID=""
fi
# Called programs use same database ID
export ORACLE_SID
ORACLE_HOME=$(echo $LINE awk -F: '{print $2}' -)
# Called scripts use same home directory
export ORACLE_HOME
# Put $ORACLE_HOME/bin into PATH and export.
PATH=$ORACLE_HOME/bin:/bin:/usr/bin:/etc:$SAVE_PATH
export PATH
# add for bug 652997
LD_LIBRARY_PATH=${SAVE_LLP}:${ORACLE_HOME}/lib
export LD_LIBRARY_PATH
PFILE=${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora
BDUMP_DIR=$(sqlplus $USER/$PASS @runAlertLogRead grep Archive cut -f3 -d' ')
gzip $BDUMP_DIR
PATH=$SAVE_PATH
fi
;;
esac
done



As the final step, add the Korn shell script to a cron job. This can then be run at regular intervals.

Wednesday, January 12, 2005

exp/imp Korn Shell scripts

Today, I am releasing two scripts that will allow the people here to export a schema and re-import it into another database. Both are Korn Shell scripts so obviously they are for Unix or variants.
=======================================================================
export.sh -
Usage: export.sh -p [dba_user_password] -d [tns_entry_of_db_2_exp] -f [export_file_name.dmp] -s [schema_nameschema_name,shema_name<,...>]

These parameters need to be in the order as shown. The -s option can either be a space or comma (no spaces) list.
========================================================================
import.sh -
Usage: import.sh -p [dba_user_password] -d [tns_entry_of_db_2_exp] -f [export_file_name.dmp]
========================================================================

In both scripts the -f option can have the full path to another directory, but will automatically change the path to the predefined variable using the filename supplied.

In both scripts if any option is omitted, the user will be prompted for the value.

Here are the scripts!:

export.sh
=======
#!/usr/bin/ksh
# export.sh - Korn Shell script to export (an) Oracle schema(s).

# January 6, 2005
# Author - John Baughman
# Hardcoded values: copy_dir, pipefile, dba
dba=PSG

copy_dir=/dbcopy/
# Check the dump directory exists
if [[ ! -d "${copy_dir}" ]]then
echo
echo "${copy_dir} does not exist !"
echo
exit 1
fi

pipefile=~/backup/backup_pipe
# Check we have created the named pipe file, or else there is no point
# continuing
if [[ ! -p ${pipefile} ]]then
echo
echo "Create the named pipe file ${pipefile} using "
echo " $ mknod p ${pipefile}"
echo
exit 1
fi

# Loop through the command line parameters
# -p - Currently the PSG user password.
# -d - The Oracle SID of the database to export. This matches the TNS entry.
# -s - The schema to import
# If either/all of these don't exist, prompt for them.
# Now, check the parameters...
while getopts ":p:d:f:s" opt; do
case $opt in
p ) psg_password=$OPTARG ;;
d ) sid=$OPTARG ;;
f ) raw_name=${OPTARG##/*/} ;;
s ) shift $((OPTIND))
raw_schema=$@ ;;
\? ) print "usage: export.sh -p password -d tns_entry -s schema[...]"
exit 1 ;;
esac
done
# Get the missing ${dba} user password

while [[ -z ${psg_password} ]]; do
read psg_password?"Enter ${dba} Password: "
done

# Get the missing SID
while [[ -z ${sid} ]]; do
read sid?"Enter Oracle SID: "
done

# Get the missing "raw" name
while [[ -z ${raw_name} ]]; do
read raw_name?"Enter the export dump file name: "
done

while [[ -z ${raw_schema} ]]; do
print "Enter the schema(s)"
read raw_schema?"(if more than one schema, they can either be space or comma delimited): "
done

# Fix up the raw schema list
schema_list=""
raw_schema=$(print $raw_schema tr "[a-z]" "[A-Z]")
for name in $raw_schema; do
if [[ -z $schema_list ]]; then
schema_list="${name}"
else
schema_list="${schema_list},${name}"
fi
done
raw_schema=${schema_list}
schema_list="(${schema_list})"

# Fix up the export file name here...
export_file=${copy_dir}${raw_name}.Z

# Let's go!!!
print ""
print "*******************************************************"
print "* Exporting ${raw_schema} schema(s) at `date`"
print "*******************************************************"
print

compress < ${pipefile} > ${export_file} &
exp ${dba}/${psg_password}@${sid} direct=y log=${copy_dir}${raw_name}.exp.log statistics=none buffer=1000000 feedback=10000 file=${pipefile} owner=${schema_list}

print
print "*******************************************************"
print "* Export Completed at `date`"
print "*******************************************************"
print

exit 0


import.sh
=======
#!/usr/bin/ksh
# import.sh - Korn Shell script to import (an) Oracle schema(s).

# January 6, 2005
# Author - John Baughman
# Hardcoded values: copy_dir, pipefile, dba

dba=PSG

copy_dir=/dbcopy/
# Check the dump directory exists
if [[ ! -d "${copy_dir}" ]]then
echo
echo "${copy_dir} does not exist !"
echo
exit 1
fi

pipefile=~/backup/backup_pipe
# Check we have created the named pipe file, or else there is no point
# continuing
if [[ ! -p ${pipefile} ]]then
echo
echo "Create the named pipe file ${pipefile} using "
echo " $ mknod p ${pipefile}"
echo
exit 1
fi

# Loop through the command line parameters
# -p - Currently the PSG user password.
# -d - The Oracle SID of the database to export. This matches the TNS entry.
# -f - The export file to import.
# If either/all of these don't exist, prompt for them.
# Now, check the parameters...
while getopts ":p:d:f:" opt; do
case $opt in
p ) psg_password=$OPTARG ;;
d ) sid=$OPTARG ;;
f ) raw_name=${OPTARG##/*/} ;;
\? ) print "usage: export.sh -p password -d tns_entry -f dump_file.dmp"
exit 1 ;;
esac
done

# Get the missing ${dba} user password
while [[ -z ${psg_password} ]]; do
read psg_password?"Enter ${dba} Password: "
done

# Get the missing SID
while [[ -z ${sid} ]]; do
read sid?"Enter Oracle SID: "
done

# Get the missing "raw" name
while [[ -z ${raw_name} ]]; do
read raw_name?"Enter the export dump file name: "
done

# Fix up the export file name here...
export_file=${copy_dir}${raw_name}.Z

# Let's go!!!
print
print "*******************************************************"
print "* Importing ${export_file} at `date`"
print "*******************************************************"
print

uncompress < ${export_file} > ${pipefile} imp ${dba}/${psg_password}@${sid} log=${copy_dir}${raw_name}.imp.log full=y statistics=none buffer=1000000 feedback=10000 file=${pipefile} ignore=y

print
print "*******************************************************"
print "* Import Completed at `date`"
print "*******************************************************"
print

exit 0

Sunday, January 09, 2005

Yes, the name says it all...

According to the Oracle reference: ORA-06950 = No Error

What a fantastic error message. Makes absolutely NO sense what so ever. I mean, why bother setting an error if there is none?

Hopefully, future posts will be more informative. Here's where I'll dump my Oracle knowledge for all to see (if you really care).

-John