Làm cách nào để có được bối cảnh ngoại lệ cho một ngoại lệ được nêu thủ công trong PL / pgSQL?


11

Trong Postgres, chúng tôi nhận được "dấu vết ngăn xếp" của các ngoại lệ sử dụng mã này:

EXCEPTION WHEN others THEN
    GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;

Điều này hoạt động tốt cho các ngoại lệ "tự nhiên", nhưng nếu chúng ta đưa ra một ngoại lệ bằng cách sử dụng

RAISE EXCEPTION 'This is an error!';

... Sau đó không có dấu vết ngăn xếp. Theo một mục nhập danh sách gửi thư , điều này có thể có chủ ý, mặc dù tôi không thể tìm ra lý do tại sao. Nó khiến tôi muốn tìm ra một cách khác để ném một ngoại lệ khác ngoài việc sử dụng RAISE. Tôi chỉ thiếu một cái gì đó rõ ràng? Có ai có một mẹo cho việc này? Có một ngoại lệ nào tôi có thể khiến Postgres ném có chứa một chuỗi lựa chọn của tôi, để tôi không chỉ nhận được chuỗi của mình trong thông báo lỗi mà còn cả dấu vết ngăn xếp đầy đủ không?

Đây là một ví dụ đầy đủ:

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;

Nó có thể là một ý tưởng tốt để hiển thị một ví dụ đơn giản ở đây.
Craig Ringer

Điểm tốt @CraigRinger. Làm xong!
Taytay

Nó không khép kín. Có gì error_info? Trông giống như một loại tùy chỉnh.
Craig Ringer

Xin lỗi - nghĩ rằng bạn chỉ muốn bối cảnh chung. Tôi đã loại bỏ những thứ không liên quan.
Taytay

Câu trả lời:


9

Hành vi này dường như là do thiết kế.

Trong src/pl/plpgsql/src/pl_exec.cbối cảnh lỗi, gọi lại rõ ràng để kiểm tra xem liệu nó có được gọi trong ngữ cảnh của RAISEcâu lệnh PL / PGQuery hay không và nếu có, bỏ qua việc phát ra bối cảnh lỗi:

/*
 * 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;

Tôi không thể tìm thấy bất kỳ tài liệu tham khảo cụ thể nào về lý do tại sao đó là trường hợp.

Bên trong máy chủ, ngăn xếp ngữ cảnh được tạo bằng cách xử lý error_context_stack , đó là một cuộc gọi lại chuỗi nối thêm thông tin vào danh sách khi được gọi.

Khi PL / PGQuery nhập vào một chức năng, nó sẽ thêm một mục vào ngăn xếp gọi lại bối cảnh lỗi. Khi nó rời khỏi một chức năng, nó sẽ loại bỏ một mục khỏi ngăn xếp đó.

Nếu các chức năng báo cáo lỗi của máy chủ PostgreSQL, như ereporthoặc elogđược gọi, nó sẽ gọi lại ngữ cảnh lỗi. Nhưng trong PL / PGQuery nếu nó thông báo rằng nó được gọi từ một RAISEcuộc gọi lại cố ý không làm gì cả.

Do đó, tôi không thấy bất kỳ cách nào để đạt được những gì bạn muốn mà không cần vá PostgreSQL. Tôi khuyên bạn nên gửi thư tới pssql-chung hỏi tại sao RAISEbây giờ không cung cấp bối cảnh lỗi mà PL / PGQuery GET STACKED DIAGNOSTICSphải sử dụng nó.

.


Cảm ơn bạn rất nhiều Craig cho câu trả lời nhanh chóng và kỹ lưỡng. Nó có vẻ kỳ lạ đối với tôi, và chắc chắn trái với mong đợi của tôi. Sự hữu ích của RAISEbị giảm đi bởi kiểm tra đó. Tôi sẽ viết thư cho họ.
Taytay

@Taytay Vui lòng bao gồm một liên kết đến câu hỏi của bạn ở đây, nhưng hãy chắc chắn rằng thư của bạn đã hoàn thành và có thể được hiểu mà không cần theo liên kết; nhiều người bỏ qua chỉ liên kết hoặc chủ yếu là các bài đăng. Nếu bạn có cơ hội bật một liên kết đến bài đăng của mình trong các bình luận ở đây, thông qua archives.postgresql.org , điều đó sẽ thực sự tuyệt vời để giúp đỡ người khác sau này.
Craig Ringer

Cảm ơn Craig. Lời khuyên tốt. Tôi đã tạo một chủ đề ở đây: postgresql.org/message-id/ Từ lúc này, họ đang tìm kiếm một giải pháp tốt cho vấn đề này.
Taytay

6

Bạn có thể khắc phục hạn chế này và khiến plpgsql phát ra bối cảnh lỗi như mong muốn bằng cách gọi một chức năng khác làm tăng (cảnh báo, thông báo, ...) lỗi cho bạn.

Tôi đã đăng một giải pháp cho một vài năm trước - trong một trong những bài viết đầu tiên của tôi ở đây trên dba.SE :

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

Chi tiết:

Tôi đã mở rộng trường hợp thử nghiệm được đăng của bạn để chứng minh rằng nó hoạt động trong Postgres 9.3:

Câu đố SQL.


Cảm ơn bạn rất nhiều Erwin! Thật thú vị, tôi thực sự đã thử nghiệm giải pháp của bạn trước khi đăng, nhưng tôi chắc chắn đã làm gì đó sai và tôi đã không nhận được bối cảnh mà tôi mong đợi. Bây giờ tôi đã nhìn thấy câu đố (cảm ơn vì cũng cho tôi thấy điều đó), tôi sẽ cho nó một phát nữa!
Taytay

Hoàn thành tốt; nó không cần thiết, nhưng có vẻ như nó sẽ làm nên chuyện.
Craig Ringer

@CraigRinger: Vì các trường hợp ngoại lệ nên, ngoại lệ , tác động hiệu suất tối thiểu cũng không thành vấn đề. Chúng tôi có tất cả các lựa chọn theo cách này.
Erwin Brandstetter

Hoàn toàn đồng ý, tôi chỉ muốn thấy sự cần thiết phải giải quyết một lúc nào đó.
Craig Ringer

@CraigRinger: Đúng. Nếu điều đó sẽ không xảy ra bất cứ lúc nào sớm, chúng tôi có thể đề xuất cách giải quyết này trong hướng dẫn ...
Erwin Brandstetter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.