PL / pgSQL'de elle oluşturulmuş bir istisna için istisna bağlamı nasıl alınır?


11

Postgres'de, bu kodu kullanarak istisnaların "yığın izini" alırız:

EXCEPTION WHEN others THEN
    GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;

Bu, "doğal" istisnalar için iyi çalışır, ancak

RAISE EXCEPTION 'This is an error!';

... o zaman yığın izi yok. Bir posta listesi girişine göre , bu bilerek olabilir, ancak hayatım boyunca nedenini anlayamıyorum. Kullanmak dışında bir istisna atmanın başka bir yolunu bulmak istememi sağlıyor RAISE. Sadece bariz bir şeyi mi kaçırıyorum? Bunun için bir hile var mı? Postgres benim hatamda sadece benim dize değil, aynı zamanda tam yığın izleme de benim dize alacaktı, benim seçim bir dize içerir atmak alabilirsiniz bir istisna var mı?

İşte tam bir örnek:

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;

Burada basit bir örnek göstermek iyi bir fikir olabilir.
Craig Ringer

İyi nokta @CraigRinger. Bitti!
Taytay

Kendi kendine yeten değildir. Nedir error_info? Özel bir türe benziyor.
Craig Ringer

Üzgünüz - sadece genel bağlam istediğinizi düşündüm. Yabancı şeyleri kaldırdım.
Taytay

Yanıtlar:


9

Bu davranış tasarım yüzündendir.

Gelen src/pl/plpgsql/src/pl_exec.cbir PL / PgSQL bağlamında çağrılan eğer hata bağlam callback'inde açıkça denetler RAISE, eğer öyleyse, hata bağlamı yayan atlar tablosu ve:

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

Neden böyle olduğuna dair özel bir referans bulamıyorum .

Dahili olarak sunucuda, bağlam yığını, error_context_stackçağrıldığında bir listeye bilgi ekleyen zincirleme bir geri arama olan işlenir .

PL / PgSQL bir fonksiyona girdiğinde, hata bağlamı geri arama yığınına bir öğe ekler. Bir işlevden ayrıldığında bir öğeyi o yığından kaldırır.

PostgreSQL sunucusunun hata raporlama işlevleri ( çağrılır ereportveya elogçağrılırsa), hata bağlamı geri aramasını çağırır. Ancak PL / PgSQL'de bir RAISEgeri çağrısından çağrıldığını fark ederse kasıtlı olarak hiçbir şey yapmaz.

Bu göz önüne alındığında, PostgreSQL'i yamalamadan istediğinizi elde etmek için hiçbir yol göremiyorum. RAISEPL / PgSQL GET STACKED DIAGNOSTICSkullanmak zorunda şimdi neden hata bağlamı sağlamadığını soran pgsql-general posta göndermenizi öneririz .

(BTW, istisna içeriği böyle bir yığın izlemesi değildir. PL / PgSQL her fonksiyon çağrısını yığına eklediğinden, sunucudaki diğer ayrıntılar için de kullanıldığından, biraz benziyor.)


Hızlı ve kapsamlı cevap için çok teşekkür ederim Craig. Bana tuhaf geliyor ve kesinlikle beklentilerime karşı. Bu kontrolün faydası RAISEazalır. Onlara yazacağım.
Taytay

@Taytay Lütfen sorunuza bir bağlantı ekleyin, ancak postanızın tamamlandığından ve bağlantıyı takip etmeden anlaşılabildiğinden emin olun ; birçok kişi yalnızca bağlantı veya çoğunlukla bağlantı gönderilerini yok sayar. Buradaki yorumlarda yayınınıza bir bağlantı açma şansı elde ederseniz, archives.postgresql.org aracılığıyla daha sonra diğer insanlara yardımcı olmak gerçekten harika olurdu.
Craig Ringer

Teşekkürler Craig. İyi tavsiye. Burada bir iş parçacığı oluşturdum: postgresql.org/message-id/… Şu an itibariyle, soruna iyi bir çözüm arıyorlar.
Taytay

6

Sen olabilir bu kısıtlama geçici ve plpgsql yayarlar yapmak hata bağlamı başka işlevini çağırarak istediğiniz gibi sizin için artırmalar (uyarı, duyuru, ...) hata olduğunu.

Ben birkaç yıl geri bunun için bir çözüm yayınlanmıştır - burada dba.SE benim ilk mesajların birinde :

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

Detaylar:

Postgres 9.3'te çalıştığını göstermek için yayınlanan test durumunuzu genişlettim:

SQL Fiddle.


Çok teşekkür ederim Erwin! Tuhaf bir şekilde, göndermeden önce çözümünüzü gerçekten denedim, ancak yanlış bir şey yapmalıydım ve beklediğim bağlamı alamadım. Artık kemanı gördüğüme göre (bunu bana gösterdiğin için teşekkürler), başka bir şans vereceğim!
Taytay

Güzel yapılmış; gerekli olmamalı, ama hile yapacak gibi görünüyor.
Craig Ringer

@CraigRinger: istisnalar olması gerektiğinden, iyi, istisna minimal performans etkisi olmamalı olsun ya. Tüm seçeneklerimiz bu şekilde.
Erwin Brandstetter

Tamamen katılıyorum, sadece bir noktada geçici çözüm ihtiyacını görmek istiyorum.
Craig Ringer

@CraigRinger: Doğru. Bu yakında gerçekleşmeyecekse, bu geçici çözümü kılavuzda önerebiliriz ...
Erwin Brandstetter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.