MySQL 8.0 リファレンスマニュアル


13.6.7.3 GET DIAGNOSTICS ステートメント

GET [CURRENT | STACKED] DIAGNOSTICS {
    statement_information_item
    [, statement_information_item] ...
  | CONDITION condition_number
    condition_information_item
    [, condition_information_item] ...
}

statement_information_item:
    target = statement_information_item_name

condition_information_item:
    target = condition_information_item_name

statement_information_item_name: {
    NUMBER
  | ROW_COUNT
}

condition_information_item_name: {
    CLASS_ORIGIN
  | SUBCLASS_ORIGIN
  | RETURNED_SQLSTATE
  | MESSAGE_TEXT
  | MYSQL_ERRNO
  | CONSTRAINT_CATALOG
  | CONSTRAINT_SCHEMA
  | CONSTRAINT_NAME
  | CATALOG_NAME
  | SCHEMA_NAME
  | TABLE_NAME
  | COLUMN_NAME
  | CURSOR_NAME
}

condition_number, target:
    (see following discussion)

SQL ステートメントは、診断領域を移入する診断情報を生成します。 GET DIAGNOSTICS ステートメントを使用すると、アプリケーションでこの情報を検査できます。 (SHOW WARNINGS または SHOW ERRORS を使用して、条件またはエラーを確認することもできます。)

GET DIAGNOSTICS を実行するために特殊な権限は必要ありません。

キーワード CURRENT は、現在の診断領域から情報を取得することを示します。 キーワード STACKED は、現在のコンテキストが条件ハンドラである場合にのみ使用可能な 2 番目の診断領域から情報を取得することを意味します。 どちらのキーワードも指定しない場合、デフォルトでは現在の診断領域が使用されます。

GET DIAGNOSTICS ステートメントは通常、ストアドプログラム内のハンドラで使用されます。 これは、任意の SQL ステートメントの実行をチェックするためにハンドラコンテキストの外部で GET [CURRENT] DIAGNOSTICS が許可されている MySQL 拡張機能です。 たとえば、mysql クライアントプログラムを呼び出す場合は、プロンプトで次のステートメントを入力できます。

mysql> DROP TABLE test.no_such_table;
ERROR 1051 (42S02): Unknown table 'test.no_such_table'
mysql> GET DIAGNOSTICS CONDITION 1
         @p1 = RETURNED_SQLSTATE, @p2 = MESSAGE_TEXT;
mysql> SELECT @p1, @p2;
+-------+------------------------------------+
| @p1   | @p2                                |
+-------+------------------------------------+
| 42S02 | Unknown table 'test.no_such_table' |
+-------+------------------------------------+

この拡張機能は、現在の診断領域にのみ適用されます。 GET STACKED DIAGNOSTICS は現在のコンテキストが条件ハンドラである場合にのみ許可されるため、2 番目の診断領域には適用されません。 そうでない場合は、GET STACKED DIAGNOSTICS when handler not active エラーが発生します。

診断領域については、セクション13.6.7.7「MySQL の診断領域」を参照してください。 簡単に言うと、ここには次の 2 種類の情報が含まれています。

  • 発生した条件の数や、影響を受けた行数などのステートメント情報。

  • エラーコードやメッセージなどの条件情報。 ステートメントが複数の条件を発生させた場合、診断領域のこの部分には条件ごとの条件領域が含まれています。 ステートメントがどの条件も発生させない場合、診断領域のこの部分は空です。

3 つの条件を生成するステートメントの場合、診断領域には、次のようなステートメント情報と条件情報が含まれています。

Statement information:
  row count
  ... other statement information items ...
Condition area list:
  Condition area 1:
    error code for condition 1
    error message for condition 1
    ... other condition information items ...
  Condition area 2:
    error code for condition 2:
    error message for condition 2
    ... other condition information items ...
  Condition area 3:
    error code for condition 3
    error message for condition 3
    ... other condition information items ...

GET DIAGNOSTICS はステートメントまたは条件情報のどちらかを取得できますが、同じステートメントで両方を取得することはできません。

  • ステートメント情報を取得するには、目的のステートメント項目をターゲット変数に取得します。 GET DIAGNOSTICS の次の例では、使用可能な条件の数と影響を受けた行数をユーザー変数 @p1@p2 に割り当てます。

    GET DIAGNOSTICS @p1 = NUMBER, @p2 = ROW_COUNT;
  • 条件情報を取得するには、条件番号を指定し、目的の条件項目をターゲット変数に取得します。 GET DIAGNOSTICS の次の例では、SQLSTATE 値とエラーメッセージをユーザー変数 @p3@p4 に割り当てます。

    GET DIAGNOSTICS CONDITION 1
      @p3 = RETURNED_SQLSTATE, @p4 = MESSAGE_TEXT;

取得リストには、カンマで区切られた 1 つ以上の target = item_name 代入を指定します。 各代入では、ターゲット変数と、このステートメントがステートメントまたは条件情報のどちらを取得するかに応じて statement_information_item_name または condition_information_item_name 指示子のどちらかを指定します。

項目情報を格納するための有効な target 指示子は、ストアドプロシージャーやストアドファンクションのパラメータ、DECLARE で宣言されたストアドプログラムのローカル変数、ユーザー定義変数のいずれかです。

有効な condition_number 指示子は、ストアドプロシージャーやストアドファンクションのパラメータ、DECLARE で宣言されたストアドプログラムのローカル変数、ユーザー定義変数、システム変数、リテラルのいずれかです。 文字リテラルには、_charset イントロデューサを含めることができます。 条件番号が 1 から、情報が含まれている条件領域の数までの範囲にない場合は、警告が発生します。 この場合、この警告は、診断領域にその領域をクリアすることなく追加されます。

条件が発生した場合、MySQL は、GET DIAGNOSTICS で認識されるすべての条件アイテムを移入しません。 例:

mysql> GET DIAGNOSTICS CONDITION 1
         @p5 = SCHEMA_NAME, @p6 = TABLE_NAME;
mysql> SELECT @p5, @p6;
+------+------+
| @p5  | @p6  |
+------+------+
|      |      |
+------+------+

標準 SQL では、複数の条件が存在する場合、最初の条件は前の SQL ステートメントに対して返された SQLSTATE 値に関連しています。 MySQL では、これが保証されません。 メインのエラーを取得するために、次のように行うことはできません。

GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;

代わりに、まず条件数を取得し、次にそれを使用してどの条件番号を検査するかを指定します。

GET DIAGNOSTICS @cno = NUMBER;
GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;

許可されるステートメント情報と条件情報の項目、および条件が発生したときにどの項目が移入されるかについては、診断領域の情報項目を参照してください。

ストアドプロシージャーのコンテキストで GET DIAGNOSTICS と例外ハンドラを使用して、挿入操作の結果を評価する例を次に示します。 挿入が成功した場合、このプロシージャーは GET DIAGNOSTICS を使用して、影響を受けた行数を取得します。 これは、現在の診断領域がクリアされていないかぎり、GET DIAGNOSTICS を複数回使用してステートメントに関する情報を取得できることを示しています。

CREATE PROCEDURE do_insert(value INT)
BEGIN
  -- Declare variables to hold diagnostics area information
  DECLARE code CHAR(5) DEFAULT '00000';
  DECLARE msg TEXT;
  DECLARE nrows INT;
  DECLARE result TEXT;
  -- Declare exception handler for failed insert
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
    BEGIN
      GET DIAGNOSTICS CONDITION 1
        code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
    END;

  -- Perform the insert
  INSERT INTO t1 (int_col) VALUES(value);
  -- Check whether the insert was successful
  IF code = '00000' THEN
    GET DIAGNOSTICS nrows = ROW_COUNT;
    SET result = CONCAT('insert succeeded, row count = ',nrows);
  ELSE
    SET result = CONCAT('insert failed, error = ',code,', message = ',msg);
  END IF;
  -- Say what happened
  SELECT result;
END;

t1.int_col が、NOT NULL として宣言された整数カラムであるとします。 このプロシージャーは、NULL 以外の値と NULL 値を挿入するために呼び出されると、それぞれ次の結果を生成します。

mysql> CALL do_insert(1);
+---------------------------------+
| result                          |
+---------------------------------+
| insert succeeded, row count = 1 |
+---------------------------------+

mysql> CALL do_insert(NULL);
+-------------------------------------------------------------------------+
| result                                                                  |
+-------------------------------------------------------------------------+
| insert failed, error = 23000, message = Column 'int_col' cannot be null |
+-------------------------------------------------------------------------+

条件ハンドラがアクティブ化されると、診断領域スタックへのプッシュが発生します:

  • 最初の (現在の) 診断領域が 2 番目の (スタックされた) 診断領域になり、新しい現在の診断領域がそのコピーとして作成されます。

  • ハンドラ内で GET [CURRENT] DIAGNOSTICS および GET STACKED DIAGNOSTICS を使用して、現在の診断領域およびスタック診断領域の内容にアクセスできます。

  • 最初は、両方の診断領域が同じ結果を返すため、ハンドラをアクティブ化した条件に関する情報を現在の診断領域から取得できます。次の期間は、ハンドラ内で現在の診断領域を変更するステートメントを実行しません。

  • ただし、ハンドラ内で実行されるステートメントは、現在の診断領域を変更し、通常の規則に従ってその内容をクリアおよび設定できます (診断領域のクリアおよび移入方法 を参照)。

    ハンドラのアクティブ化条件に関する情報を取得する信頼性の高い方法は、スタック診断領域を使用することです。スタック診断領域は、RESIGNAL 以外のハンドラ内で実行されているステートメントでは変更できません。 現在の診断領域が設定およびクリアされるタイミングの詳細は、セクション13.6.7.7「MySQL の診断領域」 を参照してください。

次の例は、ハンドラステートメントによって現在の診断領域が変更された後でも、ハンドラ内で GET STACKED DIAGNOSTICS を使用して処理された例外に関する情報を取得する方法を示しています。

ストアドプロシージャ p() 内で、TEXT NOT NULL カラムを含むテーブルに 2 つの値を挿入しようとしました。 最初の値は NULL 以外の文字列で、次の値は NULL です。 このカラムでは NULL 値が禁止されているため、最初の挿入は成功しますが、次の挿入によって例外が発生します。 このプロシージャには、空の文字列の挿入に NULL の挿入をマップする例外ハンドラが含まれています:

DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 TEXT NOT NULL);
DROP PROCEDURE IF EXISTS p;
delimiter //
CREATE PROCEDURE p ()
BEGIN
  -- Declare variables to hold diagnostics area information
  DECLARE errcount INT;
  DECLARE errno INT;
  DECLARE msg TEXT;
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    -- Here the current DA is nonempty because no prior statements
    -- executing within the handler have cleared it
    GET CURRENT DIAGNOSTICS CONDITION 1
      errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
    SELECT 'current DA before mapped insert' AS op, errno, msg;
    GET STACKED DIAGNOSTICS CONDITION 1
      errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
    SELECT 'stacked DA before mapped insert' AS op, errno, msg;

    -- Map attempted NULL insert to empty string insert
    INSERT INTO t1 (c1) VALUES('');

    -- Here the current DA should be empty (if the INSERT succeeded),
    -- so check whether there are conditions before attempting to
    -- obtain condition information
    GET CURRENT DIAGNOSTICS errcount = NUMBER;
    IF errcount = 0
    THEN
      SELECT 'mapped insert succeeded, current DA is empty' AS op;
    ELSE
      GET CURRENT DIAGNOSTICS CONDITION 1
        errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
      SELECT 'current DA after mapped insert' AS op, errno, msg;
    END IF ;
    GET STACKED DIAGNOSTICS CONDITION 1
      errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
    SELECT 'stacked DA after mapped insert' AS op, errno, msg;
  END;
  INSERT INTO t1 (c1) VALUES('string 1');
  INSERT INTO t1 (c1) VALUES(NULL);
END;
//
delimiter ;
CALL p();
SELECT * FROM t1;

ハンドラがアクティブになると、現在の診断領域のコピーが診断領域スタックにプッシュされます。 ハンドラは最初に現在の診断領域とスタック診断領域の内容を表示します。これらはどちらも最初は同じです:

+---------------------------------+-------+----------------------------+
| op                              | errno | msg                        |
+---------------------------------+-------+----------------------------+
| current DA before mapped insert |  1048 | Column 'c1' cannot be null |
+---------------------------------+-------+----------------------------+

+---------------------------------+-------+----------------------------+
| op                              | errno | msg                        |
+---------------------------------+-------+----------------------------+
| stacked DA before mapped insert |  1048 | Column 'c1' cannot be null |
+---------------------------------+-------+----------------------------+

GET DIAGNOSTICS ステートメントのあとに実行されるステートメントは、現在の診断領域をリセットできます。ステートメントは、現在の診断領域をリセットできます。 たとえば、ハンドラは NULL 挿入を空の文字列挿入にマップし、結果を表示します。 新しい挿入は成功し、現在の診断領域はクリアされますが、スタックされた診断領域は変更されず、ハンドラをアクティブ化した条件に関する情報が引き続き含まれます:

+----------------------------------------------+
| op                                           |
+----------------------------------------------+
| mapped insert succeeded, current DA is empty |
+----------------------------------------------+

+--------------------------------+-------+----------------------------+
| op                             | errno | msg                        |
+--------------------------------+-------+----------------------------+
| stacked DA after mapped insert |  1048 | Column 'c1' cannot be null |
+--------------------------------+-------+----------------------------+

条件ハンドラが終了すると、その現在の診断領域がスタックからポップされ、スタックされた診断領域がストアドプロシージャの現在の診断領域になります。

プロシージャが戻ると、テーブルには 2 つの行が含まれます。 空の行は、空の文字列の挿入にマップされた NULL を挿入しようとした結果です:

+----------+
| c1       |
+----------+
| string 1 |
|          |
+----------+

前述の例では、現在の診断領域とスタック診断領域から情報を取得する条件ハンドラ内の最初の 2 つの GET DIAGNOSTICS ステートメントが同じ値を返します。 これは、現在の診断領域をリセットするステートメントがハンドラ内で以前に実行された場合には当てはまりません。 p() が、前ではなくハンドラ定義内に DECLARE ステートメントを配置するようにリライトされるとします:

CREATE PROCEDURE p ()
BEGIN
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    -- Declare variables to hold diagnostics area information
    DECLARE errcount INT;
    DECLARE errno INT;
    DECLARE msg TEXT;
    GET CURRENT DIAGNOSTICS CONDITION 1
      errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
    SELECT 'current DA before mapped insert' AS op, errno, msg;
    GET STACKED DIAGNOSTICS CONDITION 1
      errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
    SELECT 'stacked DA before mapped insert' AS op, errno, msg;
...

この場合、結果はバージョンに依存します:

  • MySQL 5.7.2 より前では、DECLARE は現在の診断領域を変更しないため、最初の 2 つの GET DIAGNOSTICS ステートメントは p() の元のバージョンと同じ結果を返します。

    MySQL 5.7.2 では、すべての非診断ステートメントが SQL 標準に従って診断領域に移入されるように作業が行われました。 DECLARE はこれらのいずれかであるため、5.7.2 以上では、ハンドラの先頭で実行されている DECLARE ステートメントによって現在の診断領域がクリアされ、GET DIAGNOSTICS ステートメントによって異なる結果が生成されます:

    +---------------------------------+-------+------+
    | op                              | errno | msg  |
    +---------------------------------+-------+------+
    | current DA before mapped insert |  NULL | NULL |
    +---------------------------------+-------+------+
    
    +---------------------------------+-------+----------------------------+
    | op                              | errno | msg                        |
    +---------------------------------+-------+----------------------------+
    | stacked DA before mapped insert |  1048 | Column 'c1' cannot be null |
    +---------------------------------+-------+----------------------------+

ハンドラをアクティブ化した条件に関する情報を取得するときに条件ハンドラ内でこの問題を回避するには、現在の診断領域ではなく、スタック診断領域にアクセスしてください。


関連キーワード:  ステートメント, 領域, 診断, DIAGNOSTICS, 条件, CREATE, TABLE, ハンドラ, 情報, insert