web-dev-qa-db-ja.com

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

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;
11
Taytay

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

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;

whyに関する具体的な参照が見つかりません。

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

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

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

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

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

9
Craig Ringer

この制限を回避して、必要に応じて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フィドル

6