oerrのメッセージを日本語で表示させる

今週の名言

「宇宙でもっとも強い力は、幅広い興味である。」
アルベルト・アインシュタイン

oerrとは?

前回からのつながりで、今回もエラー・メッセージについて考えてみたいと思います。

oerrという非常に便利なツールがあります。「ORA-4031」について意味を調べたい時は、以下の要領でエラーメッセージの意味だけでなく、原因や対処についての説明をシェルから次のコマンドで確認することができます。

$ oerr ora 4031
04031, 00000, "unable to allocate %s bytes of shared memory (\"%s\",\"%s\",\"%s\",\"%s\")"
// *Cause: More shared memory is needed than was allocated in the shared
// pool or Streams pool.
// *Action: If the shared pool is out of memory, either use the
// DBMS_SHARED_POOL package to pin large packages,
// reduce your use of shared memory, or increase the amount of
// available shared memory by increasing the value of the
// initialization parameters SHARED_POOL_RESERVED_SIZE and
// SHARED_POOL_SIZE.
// If the large pool is out of memory, increase the initialization
// parameter LARGE_POOL_SIZE.
// If the error is issued from an Oracle Streams or XStream process,
// increase the initialization parameter STREAMS_POOL_SIZE or increase
// the capture or apply parameter MAX_SGA_SIZE.

ORA-以外のエラーメッセージ、例えば「TNS-12541」も以下のように確認することができます。

$ oerr tns 12541
12541, 00000, "TNS:no listener"
// *Cause: The connection request could not be completed because the listener
// is not running.
// *Action: Ensure that the supplied destination address matches one of
// the addresses used by the listener - compare the TNSNAMES.ORA entry with
// the appropriate LISTENER.ORA file (or TNSNAV.ORA if the connection is to
// go by way of an Interchange). Start the listener on the remote machine.

oerrコマンドの正体は?

oerrは実はシェル・スクリプトで、$ORACLE_HOME/bin 以下にあります。

$ which oerr
/u01/app/oracle/product/11.2.0.4/db_1/bin/oerr

スクリプトの内容は以下の110行から成るリストのとおりですが

  • 35行目に定義されたfacility.lisから接頭語と製品名の関連を取得
  • 93行目で製品名ディレクトリ配下にあるmsgファイルを指定
  • 104行目以下のawkコマンドにより該当メッセージの取得

という仕様になっています。

$ cat /u01/app/oracle/product/11.2.0.4/db_1/bin/oerr
#!/bin/sh
#
# $Id: oerr.sh /st_buildtools_11.2.0/1 2012/02/28 11:07:18 mrmehta Exp $
# Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
#
# Usage: oerr facility error
#
# This shell script is used to get the description and the cause and action
# of an error from a message text file when a list of error numbers are passed
# to it.  It supports different language environments and errors from different
# facilities.
#

#
# Turn on script tracing if, requested
[ "$ORACLE_TRACE" = "T" ] && set -x

#
# If ORACLE_HOME is not set, we will not be able to locate
# the message text file.
if [ ! "$ORACLE_HOME" ]
then
echo "ORACLE_HOME not set.  Please set ORACLE_HOME and try again." 1>&2
exit 1
fi

#
# Ignore user locale
LC_ALL=C
export LC_ALL

#
# Definition script "constants"
Facilities_File=$ORACLE_HOME/lib/facility.lis

#
# Check script usage
if [ "$#" != "2" ]
then
exec 1>&2
echo 'Usage: oerr facility error'
echo
echo 'Facility is identified by the prefix string in the error message.'
echo 'For example, if you get ORA-7300, "ora" is the facility and "7300"'
echo 'is the error.  So you should type "oerr ora 7300".'
echo
echo 'If you get LCD-111, type "oerr lcd 111", and so on.'
exit 1
fi

#
# Pickup the command line arguments
Facility="$1"
Platform=`/bin/uname`

case $Platform in
SunOS)
SedPath='/usr/xpg4/bin/sed'
;;
*)
SedPath='/bin/sed'
;;
esac
Code=`echo $2|$SedPath 's/^[0]*//'`

#
# Get the facility information from the oerr data file
Fac_Info=`grep -i "^${Facility}:" $Facilities_File 2> /dev/null`
if [ $? -ne 0 ]
then
echo "oerr: Unknown facility '$Facility'" 1>&2
exit 1
fi

#
# Parse the components from the Fac_Info string into Shell variables
eval `echo "$Fac_Info" | awk -F: '{
if (index ($3, "*") == 0)
printf ("Facility=%s\n", $3);
else
printf ("Facility=%s\n", $1);
printf ("Component=%s\n", $2);
}'`
if [ -z "$Facility" -o -z "$Component" ]
then
echo "oerr: Invalid facilities entry '$Fac_Info'" 1>&2
exit 1
fi

#
# The message file searched is always the US English file
Msg_File=$ORACLE_HOME/$Component/mesg/${Facility}us.msg
if [ ! -r $Msg_File ]
then
echo "oerr: Cannot access the message file $Msg_File" 1>&2
exit 1
fi

#
# Search the message file for the error code, printing the message text
# and any following comments which should give the cause and action for
# the error.
awk "BEGIN { found = 0; }
/^[0]*$Code/	{ found = 1; print ; next;}
/^\/\//		{ if (found) { print; } next; }
{ if (found) { exit; } }" $Msg_File

exit 0

例えば、$OEACLE_HOME/rdbms/mesg/oraus.msg ファイルの内容は以下のようになっています。

1865 00000, 00000, "normal, successful completion"
1866 // *Cause:  Normal exit.
1867 // *Action: None.
1868 00001, 00000, "unique constraint (%s.%s) violated"
1869 // *Cause: An UPDATE or INSERT statement attempted to insert a duplicatee key.
1870 //         For Trusted Oracle configured in DBMS MAC mode, you may see
1871 //         this message if a duplicate entry exists at a different levell.
1872 // *Action: Either remove the unique restriction or do not insert the key.
1873 /0002        reserved for v2 compatibility (null column)
1874 /0003        reserved for v2 compatibility (column value truncated)
1875 /0004        reserved for v2 compatibility (end-of-fetch)
1876 /0009        reserved for v2 compatibility
1877 /
1878 / 10 - 49 user session and session switching errors
1879 /
1880 00017, 00000, "session requested to set trace event"
1881 // *Cause:  The current session was requested to set a trace event by another
1882 //          session.
1883 // *Action: This is used internally; no action is required.

oerrは英語圏の人にとっては非常に便利なツールですが、Oracle 8.1.6を最後に各国語サポートへは対応されなくなっています。

エラー・メッセージだけでも日本語化してみる

原因や対処までは無理としても、せめてエラー・メッセージの意味だけでも日本語で表示させるツールを作ってみたいと思いましたので、以下のようなストアド・ファンクションを作ってみました。
試作版なのですべての接頭語には対応していませんし、エラーハンドリングも考慮していません。(存在しないメッセージ番号を指定すると「該当メッセージはありません」という表示をさせています。)

SQL> CREATE OR REPLACE FUNCTION show_errmsg (err_fac IN VARCHAR2,err_num IN PLS_INTEGER)
  2  RETURN VARCHAR2
  3  IS
  4   wrk_fac VARCHAR2(8);
  5   err_lan VARCHAR2(20) := 'japanese';
  6   err_msg VARCHAR2(512);
  7   i        PLS_INTEGER;
  8  BEGIN
  9   wrk_fac := UPPER(err_fac);
 10  CASE wrk_fac
 11     WHEN 'CRS'  THEN i := UTL_LMS.GET_MESSAGE(err_num,'crs',    err_fac,err_lan,err_msg);
 12     WHEN 'TNS'  THEN i := UTL_LMS.GET_MESSAGE(err_num,'network',err_fac,err_lan,err_msg);
 13     ELSE             i := UTL_LMS.GET_MESSAGE(err_num,'rdbms',  err_fac,err_lan,err_msg);
 14  END CASE;
 15  IF err_msg LIKE 'Message%not found%'
 16  THEN err_msg := wrk_fac || '-' || TO_CHAR(err_num,'FM00000') || ': 該当メッセージはありません';
 17  ELSE err_msg := wrk_fac || '-' || TO_CHAR(err_num,'FM00000') || ': ' || err_msg;
 18  END IF;
 19  RETURN err_msg;
 20  END;
 21  /

ファンクションが作成されました。

このファンクションの使用例は以下のようになります。

SQL> SELECT show_errmsg('ora',4031) ERROR_MESSAGE from dual;

ERROR_MESSAGE
----------------------------------------------------------------------------------------------------
ORA-04031: 共有メモリーの%sバイトを割当てできません("%s"、"%s"、"%s"、"%s")

SQL> SELECT show_errmsg('crs',214) ERROR_MESSAGE from dual;

ERROR_MESSAGE
----------------------------------------------------------------------------------------------------
CRS-00214: リソース'%s'を登録解除できません。

SQL> SELECT show_errmsg('&fac',&num) ERROR_MESSAGE from dual;
facに値を入力してください: rman
numに値を入力してください: 567
旧   1: SELECT show_errmsg('&fac',&num) ERROR_MESSAGE from dual
新   1: SELECT show_errmsg('rman',567) ERROR_MESSAGE from dual

ERROR_MESSAGE
--------------------------------------------------------------------------------
RMAN-00567: Recovery Managerで印刷できなかったエラー・メッセージがあります

SQL> /
facに値を入力してください: tns
numに値を入力してください: 12541
旧   1: SELECT show_errmsg('&fac',&num) ERROR_MESSAGE from dual
新   1: SELECT show_errmsg('tns',12541) ERROR_MESSAGE from dual

ERROR_MESSAGE
--------------------------------------------------------------------------------
TNS-12541: TNS: リスナーがありません。

SQL> /
facに値を入力してください: ora
numに値を入力してください: 2
旧   1: SELECT show_errmsg('&fac',&num) ERROR_MESSAGE from dual
新   1: SELECT show_errmsg('ora',2) ERROR_MESSAGE from dual

ERROR_MESSAGE
--------------------------------------------------------------------------------
ORA-00002: 該当メッセージはありません

SQL> /
facに値を入力してください: abc
numに値を入力してください: 1
旧   1: SELECT show_errmsg('&fac',&num) ERROR_MESSAGE from dual
新   1: SELECT show_errmsg('abc',1) ERROR_MESSAGE from dual

ERROR_MESSAGE
--------------------------------------------------------------------------------
ABC-00001: 該当メッセージはありません

UTL_LMS.GET_MESSAGEはどこからメッセージを取得しているのか?

oerrが参照していたmsgファイルと同じディレクトリに「msb」という拡張子が付いたバイナリファイルがあるのですが、これらのファイルの中にメッセージが格納されているようです。

それでは、日本語と英語メッセージが格納されているであろうファイルをRenameして、メッセージが取得できなくなるかどうか確認してみましょう。

$ cd $ORACLE_HOME/rdbms/mesg
$ mv oraja.msb oraja.msb.bak
$ mv oraus.msb oraus.msb.bak
SQL> SELECT show_errmsg('ora',4031) ERROR_MESSAGE from dual;

ERROR_MESSAGE
--------------------------------------------------------------------------------
ORA-04031: 該当メッセージはありません

やはり、msbファイルが関係していました。

oerrは単純なテキストファイルの内容を表示させる仕様でしたが、Oracle9i以降は英語以外の言語でエラー・メッセージを取得するインターフェースはUTL_LMS.GET_MESSAGEだけになりました。

msbファイルの中にはmsgファイルのような原因や対処に関する説明は書いてあるのでしょうか?こればかりはOracle社の中の人でなければわかりませんが、将来そのような情報が使えるようになるといいですね。

今日はここまで