如何獲取 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;
這種行為似乎是設計使然。
在
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 進入一個函式時,它會將一個項目添加到錯誤上下文回調堆棧中。當它離開一個函式時,它會從該堆棧中刪除一個項目。
如果 PostgreSQL 伺服器的錯誤報告函式,like
ereport
或被elog
呼叫,它會呼叫錯誤上下文回調。但是在 PL/PgSQL 中,如果它注意到它是從RAISE
它的回調中呼叫的,那麼它會故意什麼都不做。鑑於此,如果不修補 PostgreSQL,我看不到任何方法可以實現您想要的。我建議向 pgsql-general 發送郵件,詢問既然 PL/PgSQL 必須使用它,為什麼
RAISE
不提供錯誤上下文。GET STACKED DIAGNOSTICS
(順便說一句,異常上下文本身並不是堆棧跟踪。它看起來有點像,因為 PL/PgSQL 將每個函式呼叫添加到堆棧中,但它也用於伺服器中的其他細節。)