PL / pgSQLで手動で発生した例外の例外コンテキストを取得するにはどうすればよいですか?


11

Postgresでは、次のコードを使用して例外の「スタックトレース」を取得します。

EXCEPTION WHEN others THEN
    GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;

これは「自然な」例外では問題なく機能しますが、

RAISE EXCEPTION 'This is an error!';

...その後、スタックトレースはありません。メーリングリストのエントリによると、これは意図的なものである可能性がありますが、私の人生では理由を理解することはできません。を使用する以外の例外をスローする別の方法を理解したいと思いRAISEます。私は明らかな何かを見逃しているだけですか?誰かがこれのためのトリックを持っていますか?私の選択した文字列を含むPostgresをスローできる例外はありますか?そのため、エラーメッセージで私の文字列だけでなく、完全なスタックトレースも取得しますか?

ここに完全な例があります:

CREATE OR REPLACE FUNCTION error_test() RETURNS json AS $$
DECLARE
    v_error_stack text;
BEGIN

    -- Comment this out to see how a "normal" exception will give you the stack trace
    RAISE EXCEPTION 'This exception will not get a stack trace';

    -- This will give a divide by zero error, complete with stack trace
    SELECT 1/0;

-- In case of any exception, wrap it in error object and send it back as json
EXCEPTION WHEN others THEN

    -- If the exception we're catching is one that Postgres threw,
    -- like a divide by zero error, then this will get the full
    -- stack trace of the place where the exception was thrown.
    -- However, since we are catching an exception we raised manually
    -- using RAISE EXCEPTION, there is no context/stack trace!
    GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;

    RAISE WARNING 'The stack trace of the error is: "%"', v_error_stack;

    return to_json(v_error_stack);
END;
$$ LANGUAGE plpgsql;

ここに簡単な例を示すとよいでしょう。
クレイグリンガー、2015

良い点@CraigRinger。できた!
タイタイ、2015

自己完結型ではありません。なにerror_info?カスタムタイプのように見えます。
クレイグリンガー、2015

申し訳ありませんが、一般的なコンテキストが必要だと思っていました。不要なものを削除しました。
タイタイ、2015

回答:


9

この動作は仕様によるものと思われます。

src/pl/plpgsql/src/pl_exec.c、それはにPL / pgSQLののコンテキストで呼び出されていた場合、エラーのコンテキストコールバックを明示的かどうかを確認するにはRAISE:声明と、そうならば、エラーのコンテキストを発するスキップ

/*
 * error context callback to let us supply a call-stack traceback
 */
static void
plpgsql_exec_error_callback(void *arg)
{
        PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;

        /* if we are doing RAISE, don't report its location */
        if (estate->err_text == raise_skip_msg)
                return;

それが事実である理由について、私は特定の参照を見つけることができません。

サーバーの内部では、コンテキストスタックはを処理することによって生成されますerror_context_stack。これは、呼び出されたときにリストに情報を追加するチェーンコールバックです。

PL / PgSQLが関数に入ると、エラーコンテキストコールバックスタックにアイテムが追加されます。関数を離れると、そのスタックからアイテムが削除されます。

ereportまたはのようなPostgreSQLサーバーのエラー報告関数elogが呼び出されると、エラーコンテキストコールバックが呼び出されます。しかし、PL / PgSQLではRAISE、コールバックから呼び出されていることに気付いた場合、意図的に何もしません。

それを考えると、PostgreSQLにパッチを適用せずにあなたが望むものを達成する方法は見当たりません。RAISEPL / PgSQLがエラーコンテキストGET STACKED DIAGNOSTICSを使用する必要があるのに、なぜエラーコンテキストが提供されないのか尋ねて、pgsql-generalにメールを投稿することをお勧めします。

(ところで、例外コンテキストはスタックトレースそのものではありません。PL/ PgSQLが各関数呼び出しをスタックに追加するため、1つのように見えますが、サーバーの他の詳細にも使用されます。)


迅速かつ完全な回答をしてくれたCraigに感謝します。それは私には奇妙に思えますが、確かに私の期待に反しています。の有用性RAISEは、そのチェックによって減少します。彼らに手紙を書きます。
タイタイ、2015

@Taytay質問へのリンクをここに含めてください。ただし、メールが完全で、リンクをたどらなくても理解できることを確認してください。多くの人々は、リンクのみの投稿またはリンクがほとんどの投稿を無視します。投稿へのリンクをここのコメントにポップする機会があったら、archives.postgresql.orgを介して、後で他の人を助けるのは本当に素晴らしいでしょう。
クレイグリンガー

クレイグに感謝します。いいアドバイス。ここにスレッドを作成しました:postgresql.org/message-id/… 現在のところ、彼らはこの問題に対する適切な解決策を探しています。
タイタイ、2015

6

この制限回避し、エラー発生させる(警告、通知など)別の関数を呼び出すことで、必要に応じてplpgsqlにエラーコンテキストを発行せることができます。

私はその解決策を数年前に投稿しました-dba.SEに関する私の最初の投稿の1つ

-- helper function to raise an exception with CONTEXT
CREATE OR REPLACE FUNCTION f_raise(_lvl text = 'EXCEPTION'
                                  ,_msg text = 'Default error msg.')
  RETURNS void AS
$func$
BEGIN
   CASE upper(_lvl)
      WHEN 'EXCEPTION' THEN RAISE EXCEPTION '%', _msg;
      WHEN 'WARNING'   THEN RAISE WARNING   '%', _msg;
      WHEN 'NOTICE'    THEN RAISE NOTICE    '%', _msg;
      WHEN 'DEBUG'     THEN RAISE DEBUG     '%', _msg;
      WHEN 'LOG'       THEN RAISE LOG       '%', _msg;
      WHEN 'INFO'      THEN RAISE INFO      '%', _msg;
      ELSE RAISE EXCEPTION 'f_raise(): unexpected raise-level: "%"', _lvl;
   END CASE;
END
$func$  LANGUAGE plpgsql STRICT;

詳細:

Postgres 9.3で機能することを示すために、投稿したテストケースを拡張しました。

SQL Fiddle。


アーウィン、どうもありがとう!おかしなことに、投稿する前に実際に解決策を試してみましたが、何か間違ったことをしたに違いなく、期待したコンテキストが得られませんでした。フィドルを見たので(それも見せてくれてありがとう)、もう一度試してみましょう!
タイタイ、2015

よくできました。必要はありませんが、うまくいくように見えます。
クレイグリンガー、

@CraigRinger:例外がなければならないので、まあ、例外、パフォーマンスへの影響を最小限に抑えるべきではない問題で、どちらか。この方法にはすべてのオプションがあります。
Erwin Brandstetter

完全に同意します。回避策の必要性がいずれ廃止されることを期待しています。
クレイグリンガー

@CraigRinger:はい。それがすぐに起こらない場合は、マニュアルでこの回避策を提案する可能性があります...
Erwin Brandstetter
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.