PL/pgSQL'de hata yakalamak için hata çıkabilecek komutları BEGIN ... EXCEPTION WHEN koşul THEN ... END bloğuna alırsınız; hatanın kodu SQLSTATE, mesajı SQLERRM değişkeninde, ayrıntıları GET STACKED DIAGNOSTICS ile okunur. Kendi hatanızı üretmek için RAISE EXCEPTION 'mesaj %', deger USING ERRCODE = ..., HINT = ... yazarsınız. Hata yakalandığında o blokta yapılan veritabanı değişiklikleri geri alınır, değişkenler ise olduğu gibi kalır.
Bu yazı PL/pgSQL'in hata yönetimini uçtan uca ele alıyor: EXCEPTION bloğunun işleyişi ve geri alma davranışı, koşul adları ve SQLSTATE kodları, GET STACKED DIAGNOSTICS, RAISE seviyeleri ve USING seçenekleri, hatayı yeniden fırlatmak, ASSERT ve sık yapılan hatalar. Dilin temelleri (değişkenler, döngüler, SELECT INTO) için PL/pgSQL temelleri yazısını okumanızı öneririz. Tüm seri PostgreSQL öğrenme rehberinde.
Varsayılan davranış: hata her şeyi durdurur
Kılavuza göre PL/pgSQL fonksiyonunda oluşan bir hata, varsayılan olarak hem fonksiyonun çalışmasını hem de içinde bulunduğu transaction'ı iptal eder. Yani hata yakalamadığınız sürece fonksiyonun o ana kadar yaptığı her şey, çağıran transaction ile birlikte geri alınır. psql'de açık bir BEGIN bloğu içindeyseniz, hatadan sonra gelen komutlar ROLLBACK yapılana kadar reddedilir; bu durum ve SAVEPOINT ile çözümü BEGIN, COMMIT ve ROLLBACK yazısında anlatılıyor.
EXCEPTION yan tümcesi bu varsayılanı blok düzeyinde değiştirir. Sözdizimi normal bloğun bir uzantısıdır:
[ <<etiket>> ]
[ DECLARE
tanımlar ]
BEGIN
komutlar
EXCEPTION
WHEN koşul [ OR koşul ... ] THEN
işleyici_komutlar
[ WHEN koşul [ OR koşul ... ] THEN
işleyici_komutlar
... ]
END;Hata olmazsa blok normal biter ve akış END sonrasına geçer. Komutlardan birinde hata olursa kalan komutlar atlanır ve EXCEPTION listesinde hatayla eşleşen ilk koşul aranır. Eşleşme varsa işleyici çalışır ve akış yine END sonrasına geçer. Eşleşme yoksa hata, EXCEPTION hiç yokmuş gibi dışarı yayılır: dıştaki bir EXCEPTION bloğu yakalayabilir, yoksa fonksiyon iptal olur.
Geri alma davranışı: ne geri alınır, ne kalır?
Kılavuzun en önemli kuralı şudur: hata bir EXCEPTION yan tümcesi tarafından yakalandığında, fonksiyonun yerel değişkenleri hata anındaki değerlerini korur, ancak o blok içinde kalıcı veritabanı durumuna yapılan tüm değişiklikler geri alınır. Bloktan önce yapılan değişiklikler etkilenmez. Bunu iki adımlı bir havale ile görelim: önce alıcıya para ekleniyor, sonra gönderenden düşülüyor. Gönderenin bakiyesi yetmezse CHECK kısıtı hata veriyor.
CREATE TABLE hesap (
id integer PRIMARY KEY,
sahip text NOT NULL,
bakiye numeric(12,2) NOT NULL CONSTRAINT bakiye_eksi_olamaz CHECK (bakiye >= 0)
);
INSERT INTO hesap VALUES (1, 'Ahmet Uçar', 1000), (2, 'Fatma Ateş', 250);
CREATE FUNCTION havale(kimden integer, kime integer, tutar numeric)
RETURNS text
LANGUAGE plpgsql
AS $$
DECLARE
adim integer := 0;
BEGIN
BEGIN
UPDATE hesap SET bakiye = bakiye + tutar WHERE id = kime;
adim := 1;
UPDATE hesap SET bakiye = bakiye - tutar WHERE id = kimden;
adim := 2;
EXCEPTION
WHEN check_violation THEN
RETURN format('iptal (adım=%s, SQLSTATE=%s): %s', adim, SQLSTATE, SQLERRM);
END;
RETURN 'tamam';
END
$$;
SELECT havale(2, 1, 400);
SELECT * FROM hesap ORDER BY id;
SELECT havale(1, 2, 400);
SELECT * FROM hesap ORDER BY id; havale
-------------------------------------------------------------------------------------------------------------
iptal (adım=1, SQLSTATE=23514): new row for relation "hesap" violates check constraint "bakiye_eksi_olamaz"
(1 row)
id | sahip | bakiye
----+------------+---------
1 | Ahmet Uçar | 1000.00
2 | Fatma Ateş | 250.00
(2 rows)
havale
--------
tamam
(1 row)
id | sahip | bakiye
----+------------+--------
1 | Ahmet Uçar | 600.00
2 | Fatma Ateş | 650.00
(2 rows)İlk çağrıda Fatma'nın 250 TL'si 400 TL'lik havaleye yetmedi. Alıcıya eklenen 400 TL de geri alındı, çünkü o UPDATE aynı blok içindeydi; tablo ilk hâline döndü. Buna karşılık adim değişkeni 1 değerini korudu ve mesajda göründü: hata ilk güncellemeden sonra, ikinci güncellemede oluştu. İkinci çağrı başarılı oldu ve iki güncelleme birlikte kalıcı hâle geldi. SQLSTATE değeri 23514, yani check_violation koşulunun kodudur.
Bu davranışın teknik karşılığı alt transaction'dır: kılavuz, EXCEPTION içeren bir bloğun fiilen dış transaction'ı etkilemeden geri alınabilen bir alt transaction oluşturduğunu belirtir. PL/pgSQL'de SAVEPOINT komutları desteklenmez; kılavuz savepoint'lerin tipik kullanımlarının yerine EXCEPTION bloklarının geçtiğini söyler. Aynı sebeple EXCEPTION içeren bir bloğun içinde COMMIT ile transaction bitirilemez.
Koşul adları, SQLSTATE ve OTHERS

WHEN ardından yazılan koşul adları, kılavuzun A ekindeki hata kodları tablosundan gelir: unique_violation, foreign_key_violation, check_violation, not_null_violation, division_by_zero, no_data_found, too_many_rows gibi. Adlar büyük/küçük harfe duyarlı değildir. Aynı koşulu beş karakterli SQLSTATE koduyla da yazabilirsiniz; WHEN division_by_zero ile WHEN SQLSTATE '22012' eşdeğerdir. Bir kategori adı (örneğin integrity_constraint_violation) o kategorideki tüm hatalarla eşleşir.
| Koşul adı | SQLSTATE | Ne zaman |
|---|---|---|
| unique_violation | 23505 | UNIQUE veya PRIMARY KEY ihlali |
| foreign_key_violation | 23503 | Yabancı anahtar ihlali |
| check_violation | 23514 | CHECK kısıtı ihlali |
| not_null_violation | 23502 | NOT NULL sütuna NULL |
| division_by_zero | 22012 | Sıfıra bölme |
| no_data_found | P0002 | INTO STRICT satır bulamadı |
| too_many_rows | P0003 | INTO STRICT birden çok satır buldu |
| raise_exception | P0001 | Kodu belirtilmemiş RAISE EXCEPTION |
| assert_failure | P0004 | ASSERT başarısız |
Özel OTHERS adı, iki istisna dışında her hatayla eşleşir: QUERY_CANCELED ve ASSERT_FAILURE. Kılavuz bu ikisinin adla yakalanabileceğini ama bunun çoğu zaman akıllıca olmadığını söyler. Sorgu iptalini yakalarsanız, kullanıcının veya yöneticinin iptal isteğini fonksiyonunuz yutmuş olur. Hata kodlarının tam listesi ve en sık görülenlerin çözümleri sık hatalar ve SQLSTATE kodları yazısında.
İşleyici içinde yeni bir hata olursa, bu hata aynı EXCEPTION yan tümcesi tarafından yakalanamaz ve dışarı yayılır; onu ancak dıştaki bir blok yakalayabilir.
SQLSTATE, SQLERRM ve GET STACKED DIAGNOSTICS

İşleyici içinde iki özel değişken tanımlıdır: SQLSTATE hata kodunu, SQLERRM hata mesajını tutar. Bu değişkenler işleyici dışında tanımsızdır. Daha fazla bilgi gerekiyorsa GET STACKED DIAGNOSTICS kullanılır. Kullanılabilen öğeler:
| Öğe | İçerik |
|---|---|
| RETURNED_SQLSTATE | Hatanın SQLSTATE kodu |
| MESSAGE_TEXT | Ana hata mesajı |
| PG_EXCEPTION_DETAIL | DETAIL mesajı (varsa) |
| PG_EXCEPTION_HINT | HINT mesajı (varsa) |
| PG_EXCEPTION_CONTEXT | Hata anındaki çağrı yığını |
| COLUMN_NAME, CONSTRAINT_NAME, TABLE_NAME, SCHEMA_NAME | Hatayla ilgili nesne adları |
| PG_DATATYPE_NAME | Hatayla ilgili veri tipi |
Hata ilgili öğeyi doldurmamışsa boş metin döner. Benzersizlik ihlalinde hangi kısıtın ve tablonun söz konusu olduğunu okuyalım:
CREATE TABLE eposta (adres text CONSTRAINT eposta_tekil UNIQUE);
INSERT INTO eposta VALUES ('selin@example.com');
DO $$
DECLARE
v_durum text;
v_mesaj text;
v_detay text;
v_kisit text;
v_tablo text;
BEGIN
INSERT INTO eposta VALUES ('selin@example.com');
EXCEPTION
WHEN unique_violation THEN
GET STACKED DIAGNOSTICS
v_durum = RETURNED_SQLSTATE,
v_mesaj = MESSAGE_TEXT,
v_detay = PG_EXCEPTION_DETAIL,
v_kisit = CONSTRAINT_NAME,
v_tablo = TABLE_NAME;
RAISE NOTICE 'durum=% tablo=% kısıt=%', v_durum, v_tablo, v_kisit;
RAISE NOTICE 'mesaj=%', v_mesaj;
RAISE NOTICE 'detay=%', v_detay;
END
$$;NOTICE: durum=23505 tablo=eposta kısıt=eposta_tekil
NOTICE: mesaj=duplicate key value violates unique constraint "eposta_tekil"
NOTICE: detay=Key (adres)=(selin@example.com) already exists.
DOCONSTRAINT_NAME öğesi özellikle değerlidir. Kılavuzdaki "önce UPDATE dene, yoksa INSERT et, benzersizlik hatası olursa tekrar dene" örneğinin zayıf noktasını kılavuz kendisi belirtir: tabloda birden fazla benzersiz indeks varsa veya hata bir trigger içindeki başka bir INSERT'ten geliyorsa, yanlış hatayı yakalayıp yanlış işlem yapabilirsiniz. Yakaladığınız hatanın beklediğiniz kısıttan geldiğini CONSTRAINT_NAME ile doğrulamak bu riski azaltır. Kılavuz o örnek için uygulamalarda INSERT ... ON CONFLICT DO UPDATE kullanılmasını önerir; bkz. UPSERT ve MERGE.
Bir de GET DIAGNOSTICS ... PG_CONTEXT vardır (STACKED olmadan): hata olmasa bile o anki çağrı yığınını metin olarak verir. PG_EXCEPTION_CONTEXT ise aynı türden yığını, hatanın oluştuğu konum için verir.
RAISE: mesaj ve hata üretmek
RAISE komutu hem bilgi mesajı hem hata üretir. Seviyeler DEBUG, LOG, INFO, NOTICE, WARNING ve EXCEPTION'dır; seviye yazılmazsa EXCEPTION varsayılır. Yalnızca EXCEPTION bir hata yükseltir (ve normalde transaction'ı iptal eder); diğerleri farklı önemde mesajlardır. Bir mesajın istemciye mi, sunucu günlüğüne mi gideceğini client_min_messages ve log_min_messages ayarları belirler.
Biçim metni basit bir metin sabiti olmalıdır, ifade olamaz. Metindeki her % sıradaki argümanın metin hâliyle değiştirilir; düz yüzde işareti için %% yazılır. Argüman sayısı % sayısıyla eşleşmezse fonksiyon derlenirken hata verilir. USING ardından şu seçenekler eklenebilir: MESSAGE, DETAIL, HINT, ERRCODE ve ilgili nesne için COLUMN, CONSTRAINT, DATATYPE, TABLE, SCHEMA.
Aşağıdaki fonksiyon üç durumda üç farklı davranış gösteriyor. psql'de \set VERBOSITY verbose açık olduğu için SQLSTATE kodu mesajın başında görünüyor:
\set VERBOSITY verbose
CREATE FUNCTION para_cek(p_id integer, tutar numeric) RETURNS numeric
LANGUAGE plpgsql
AS $$
DECLARE
v_bakiye hesap.bakiye%TYPE;
BEGIN
IF tutar <= 0 THEN
RAISE EXCEPTION 'Geçersiz tutar: %', tutar
USING ERRCODE = 'invalid_parameter_value',
HINT = 'Tutar sıfırdan büyük olmalı.';
END IF;
SELECT bakiye INTO v_bakiye FROM hesap WHERE id = p_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Hesap bulunamadı: %', p_id
USING ERRCODE = 'HS404', DETAIL = 'hesap tablosunda bu id yok';
END IF;
IF v_bakiye < tutar THEN
RAISE WARNING 'Bakiye yetersiz: % < %', v_bakiye, tutar;
RETURN v_bakiye;
END IF;
UPDATE hesap SET bakiye = bakiye - tutar WHERE id = p_id
RETURNING bakiye INTO v_bakiye;
RETURN v_bakiye;
END
$$;
SELECT para_cek(1, -5);
SELECT para_cek(9, 10);
SELECT para_cek(2, 5000);
\set VERBOSITY defaultERROR: 22023: Geçersiz tutar: -5
HINT: Tutar sıfırdan büyük olmalı.
CONTEXT: PL/pgSQL function para_cek(integer,numeric) line 6 at RAISE
LOCATION: exec_stmt_raise, pl_exec.c:3937
ERROR: HS404: Hesap bulunamadı: 9
DETAIL: hesap tablosunda bu id yok
CONTEXT: PL/pgSQL function para_cek(integer,numeric) line 13 at RAISE
LOCATION: exec_stmt_raise, pl_exec.c:3937
WARNING: 01000: Bakiye yetersiz: 650.00 < 5000
LOCATION: exec_stmt_raise, pl_exec.c:3937
para_cek
----------
650.00
(1 row)- İlk çağrı, koşul adıyla verilen
invalid_parameter_valuekodunu (22023) ve ipucunu taşıyan bir hata üretti. - İkinci çağrı kendi tanımladığımız
HS404kodunu kullandı. Kılavuza göre önceden tanımlı kodlarla sınırlı değilsiniz:00000dışında, rakam ve büyük ASCII harflerinden oluşan beş karakterli herhangi bir kod seçebilirsiniz. Üç sıfırla biten kodlardan kaçının; bunlar kategori kodlarıdır ve ancak bütün kategori yakalanarak yakalanabilir. - Üçüncü çağrı yalnızca bir
WARNINGverdi (01000uyarı kodudur), hata olmadığı için fonksiyon değer döndürmeye devam etti.
Kod belirtilmeyen bir RAISE EXCEPTION, raise_exception (P0001) kodunu kullanır. Mesaj metni verilmezse koşul adı veya SQLSTATE mesaj olarak kullanılır. RAISE unique_violation USING MESSAGE = ... veya RAISE SQLSTATE '22012' gibi biçimler de geçerlidir. CONTEXT: satırının hatanın fonksiyonun hangi satırında yükseltildiğini gösterdiğine dikkat edin.
Hatayı yakalayıp yeniden fırlatmak
Parametresiz RAISE; yalnızca bir EXCEPTION işleyicisinin içinde kullanılabilir ve o an işlenen hatayı yeniden fırlatır. Bu, "hatayı günlüğe yaz, ama yine de çağırana ilet" deseni için kullanışlıdır. Aşağıdaki sarmalayıcı fonksiyon, hesap bulunamazsa işlemi sessizce atlıyor, tutar hatasında ise önce bir not düşüp hatayı aynen yukarı gönderiyor:
CREATE FUNCTION guvenli_cek(p_id integer, tutar numeric) RETURNS text
LANGUAGE plpgsql
AS $$
BEGIN
RETURN 'yeni bakiye: ' || para_cek(p_id, tutar);
EXCEPTION
WHEN SQLSTATE 'HS404' THEN
RETURN 'hesap yok, işlem atlandı';
WHEN invalid_parameter_value THEN
RAISE NOTICE 'tutar hatası günlüğe yazıldı: %', SQLERRM;
RAISE;
END
$$;
SELECT guvenli_cek(1, 100);
SELECT guvenli_cek(42, 100);
SELECT guvenli_cek(1, 0); guvenli_cek
---------------------
yeni bakiye: 500.00
(1 row)
guvenli_cek
--------------------------
hesap yok, işlem atlandı
(1 row)
NOTICE: tutar hatası günlüğe yazıldı: Geçersiz tutar: 0
ERROR: Geçersiz tutar: 0
HINT: Tutar sıfırdan büyük olmalı.
CONTEXT: PL/pgSQL function para_cek(integer,numeric) line 6 at RAISE
PL/pgSQL function guvenli_cek(integer,numeric) line 3 at RETURNKendi HS404 kodumuzu WHEN SQLSTATE 'HS404' ile yakaladık. Üçüncü çağrıda yeniden fırlatılan hata orijinal mesajını ve ipucunu korudu; CONTEXT: artık iki satırlı bir çağrı yığını gösteriyor. Ayrıca ilk çağrıda yapılan para çekme işlemi kalıcı oldu, çünkü hata oluşmadı.
ASSERT ve mesaj seviyeleri
ASSERT koşul [, mesaj] hata ayıklama için kısa bir denetimdir. Koşul doğruysa hiçbir şey olmaz; false veya NULL ise ASSERT_FAILURE hatası yükseltilir. İsteğe bağlı mesaj ifadesi yalnızca denetim başarısız olduğunda hesaplanır ve varsayılan "assertion failed" metninin yerine geçer. Denetimler plpgsql.check_asserts ayarıyla kapatılabilir (varsayılan on); kapalıyken ASSERT hiçbir şey yapmaz. Kılavuz ASSERT'in program hatalarını bulmak için olduğunu, olağan hata durumlarının RAISE ile bildirilmesi gerektiğini vurgular.
DO $$
DECLARE
toplam numeric;
BEGIN
SELECT sum(bakiye) INTO toplam FROM hesap;
ASSERT toplam > 0, 'toplam bakiye pozitif olmalı';
ASSERT toplam < 1000, format('toplam beklenenden büyük: %s', toplam);
RAISE NOTICE 'kontroller geçti';
END
$$;
DO $$
BEGIN
BEGIN
ASSERT false, 'yakalanmayacak';
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'OTHERS yakaladı';
END;
END
$$;
SET client_min_messages = warning;
DO $$ BEGIN RAISE NOTICE 'bu görünmez'; RAISE WARNING 'bu görünür'; END $$;
RESET client_min_messages;ERROR: toplam beklenenden büyük: 1150.00
CONTEXT: PL/pgSQL function inline_code_block line 7 at ASSERT
ERROR: yakalanmayacak
CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT
SET
WARNING: bu görünür
DO
RESETİlk blokta ilk denetim geçti, ikincisi kendi mesajıyla başarısız oldu ve "kontroller geçti" hiç yazılmadı. İkinci blok, OTHERS'ın ASSERT_FAILURE hatasını yakalamadığını doğruluyor: işleyici çalışmadı, hata dışarı çıktı. Son blokta client_min_messages = warning ayarı yüzünden NOTICE istemciye gönderilmedi, WARNING gönderildi.
INTO STRICT hatalarını anlamlı kılmak ve hatayı bilerek yok saymak
SELECT ... INTO STRICT satır bulamadığında no_data_found, birden çok satır bulduğunda too_many_rows hatası verir. Varsayılan mesaj ("query returned no rows") hangi değerle arandığını söylemez. Kılavuz bunun için print_strict_params seçeneğini anlatır: fonksiyon gövdesinin başına #print_strict_params on yazarsanız, STRICT koşulu sağlanmadığında hata mesajının DETAIL kısmına komuta geçirilen parametreler eklenir. Aynı ayar tüm fonksiyonlar için plpgsql.print_strict_params ile açılabilir; bu yalnızca sonradan derlenen fonksiyonları etkiler.
Bazen bir hatayı gerçekten yok saymak istersiniz. PL/pgSQL'de hiçbir şey yapmayan komut NULL;'dır; kılavuz, işleyiciyi tamamen boş bırakmanın da aynı anlama geldiğini ve hangisinin seçileceğinin üslup meselesi olduğunu söyler. Bilinçli olduğunu göstermek için bir yorum eklemek iyi bir alışkanlıktır.
CREATE FUNCTION sahip_bul(p_sahip text) RETURNS integer
LANGUAGE plpgsql
AS $$
#print_strict_params on
DECLARE
v_id integer;
BEGIN
SELECT id INTO STRICT v_id FROM hesap WHERE sahip = p_sahip;
RETURN v_id;
END
$$;
SELECT sahip_bul('Fatma Ateş');
SELECT sahip_bul('Murat Er');
DO $$
DECLARE x numeric;
BEGIN
BEGIN
x := 1 / 0;
EXCEPTION
WHEN division_by_zero THEN
NULL; -- bilerek yok sayılıyor
END;
RAISE NOTICE 'x = %', coalesce(x::text, 'atanmadı');
END
$$; sahip_bul
-----------
2
(1 row)
ERROR: query returned no rows
DETAIL: parameters: p_sahip = 'Murat Er'
CONTEXT: PL/pgSQL function sahip_bul(text) line 6 at SQL statement
NOTICE: x = atanmadı
DOİkinci çağrının DETAIL satırı aranan değeri gösteriyor; bu, özellikle günlükte biriken hataları incelerken zaman kazandırır. Son blokta sıfıra bölme hatası yakalanıp yok sayıldı; atama hiç tamamlanmadığı için x NULL kaldı ve dış blok çalışmaya devam etti.
Sık yapılan hatalar

- Her şeyi WHEN OTHERS ile yutmak: hata kaybolur, transaction devam eder ve sorun sessizce büyür. Yalnızca beklediğiniz koşulları yakalayın; gerekiyorsa günlüğe yazıp
RAISE;ile yeniden fırlatın. - Bloğun dışındaki değişikliklerin de geri alınacağını sanmak: yalnızca
EXCEPTIONbloğunun içindeki değişiklikler geri alınır. İşlemin tamamının atomik olması gerekiyorsa ilgili tüm komutları aynı bloğa koyun. - Değişkenlerin de geri alınacağını sanmak: yerel değişkenler hata anındaki değerlerini korur.
- Döngünün her turunda EXCEPTION bloğu: belirgin maliyet ekler; mümkünse hatayı önceden koşulla engelleyin.
- Mesaj metnine göre karar vermek: SQLSTATE kullanın.
- Üç sıfırla biten özel kod seçmek: kategori kodlarıyla karışır.
- Yanlış hatayı yakalamak: birden çok benzersiz kısıt varsa
CONSTRAINT_NAMEile doğrulayın.
Sık Sorulan Sorular
EXCEPTION bloğu ile SAVEPOINT aynı şey mi?
Etki olarak benzerdir: ikisi de transaction'ın bir kısmını geri almayı sağlar. PL/pgSQL SAVEPOINT komutlarını desteklemez; kılavuz bunun yerine EXCEPTION içeren blokları önerir ve bu blokların arka planda bir alt transaction oluşturduğunu belirtir.
Trigger içinde RAISE EXCEPTION kullanabilir miyim?
Evet; trigger fonksiyonu da bir PL/pgSQL fonksiyonudur ve RAISE EXCEPTION değişikliği yapan komutu iptal eder. Doğrulama trigger'ı örnekleri PostgreSQL trigger oluşturma yazısında.
Hata kodunu uygulama tarafında nasıl okurum?
İstemci kütüphaneleri hatanın SQLSTATE kodunu ayrı bir alan olarak verir. psql'de \set VERBOSITY verbose kodu mesajın başında gösterir. Bağlantı katmanındaki hatalar için bağlantı hataları yazısına bakın.
Yakalanan hatadan sonra transaction devam eder mi?
Evet. Hata bir EXCEPTION yan tümcesinde yakalandıysa yalnızca o bloğun değişiklikleri geri alınır; fonksiyon ve onu çağıran transaction çalışmaya devam eder.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: PostgreSQL öğrenme rehberi · PL/pgSQL temelleri · SQLSTATE kodları · Transaction yönetimi · Trigger oluşturma