PostgreSQL her hata mesajına SQL standardına uyan beş karakterlik bir SQLSTATE kodu verir: 23505 benzersizlik ihlali (duplicate key), 42P01 tablo bulunamadı, 42501 yetki yok, 23503 yabancı anahtar ihlali, 22P02 geçersiz girdi sözdizimi, 40001 serileştirme hatası, 40P01 deadlock. İlk iki karakter hata sınıfını, son üç karakter sınıf içindeki durumu belirtir. Uygulamanız hatayı mesaj metninden değil bu koddan tanımalıdır; kılavuza göre kodlar sürümler arasında daha az değişir ve mesajların çevrilmesinden etkilenmez.

Bu yazıda en sık karşılaşılan hataların her birini PostgreSQL 18.1 üzerinde gerçekten ürettik, çıktıları olduğu gibi gösterdik ve her birinin nedenini ve çözümünü anlattık. Mesajlar İngilizcedir, çünkü test sunucusu lc_messages=C ile çalışıyordu; Türkçe açıklamalar bize aittir. Bağlantı kurulamadığında görülen hatalar ayrı bir konudur ve bağlantı hataları yazısında anlatılıyor. Serinin tamamı için PostgreSQL öğrenme rehberine bakabilirsiniz.

SQLSTATE nasıl okunur?

SQLSTATE nasıl okunur? Bağlantı istisnası, Veri istisnası, Bütünlük kısıtı ihlali, Geçersiz transaction durumu

Kılavuzun Ek A bölümündeki tablo PostgreSQL 18.6'da tanımlı tüm kodları listeler. Her sınıfın son üç karakteri 000 olan bir "genel" kodu vardır ve daha özel bir kod atanmamış durumlar için kullanılır. Bu yüzden belirli bir kodu tanımayan bir uygulama bile ilk iki karaktere bakarak ne yapacağına karar verebilir. Tablodaki "Condition Name" sütunu PL/pgSQL'de kullanılacak adı verir; örneğin 23505 için unique_violation. En sık işinize yarayacak sınıflar:

SınıfAnlamıÖrnek kodlar
08Bağlantı istisnası08006 connection_failure, 08001
22Veri istisnası22P02 invalid_text_representation, 22012 division_by_zero, 22001
23Bütünlük kısıtı ihlali23502, 23503, 23505, 23514
25Geçersiz transaction durumu25P02 in_failed_sql_transaction
28Geçersiz yetkilendirme28P01 invalid_password
40Transaction geri alındı40001 serialization_failure, 40P01 deadlock_detected
42Sözdizimi hatası veya erişim kuralı ihlali42601, 42501, 42703, 42883, 42P01, 42P07
53Yetersiz kaynak53300 too_many_connections
55Nesne gerekli durumda değil55P03 lock_not_available
57Operatör müdahalesi57014 query_canceled
P0PL/pgSQL hatasıP0001 raise_exception

psql'de kodu ve ayrıntıları görmek

psql varsayılan olarak SQLSTATE kodunu göstermez. VERBOSITY değişkeni default, verbose, terse veya sqlstate değerlerini alır. verbose kodu, ilgili şema, tablo ve kısıt adlarını ve hatanın sunucu kaynağındaki yerini gösterir:

sql
CREATE TABLE musteri (id integer PRIMARY KEY, eposta text UNIQUE NOT NULL, ad text);
CREATE TABLE siparis (id integer PRIMARY KEY,
                      musteri_id integer REFERENCES musteri(id),
                      tutar numeric CHECK (tutar > 0));
INSERT INTO musteri VALUES (1, 'ayse@ornek.com', 'Ayşe');

\set VERBOSITY verbose
INSERT INTO musteri VALUES (2, 'ayse@ornek.com', 'Ayşe Kaya');
text
ERROR:  23505: duplicate key value violates unique constraint "musteri_eposta_key"
DETAIL:  Key (eposta)=(ayse@ornek.com) already exists.
SCHEMA NAME:  public
TABLE NAME:  musteri
CONSTRAINT NAME:  musteri_eposta_key
LOCATION:  _bt_check_unique, nbtinsert.c:673

Aynı komut \set VERBOSITY sqlstate ile yalnızca ERROR: 23505 satırını basar. Bir hatayı az önce varsayılan ayrıntıda gördüyseniz \errverbose son hatayı en yüksek ayrıntıda tekrarlar. Kılavuz, ayrı alanlarda gelen şema, tablo, sütun ve kısıt adlarının uygulamaların çevrilmiş olabilecek mesaj metnini ayrıştırmak zorunda kalmaması için sağlandığını belirtir; bu kapsamın tam olarak yalnızca sınıf 23 için garanti edildiğini de not eder. Sürücünüz (libpq, PDO, JDBC) bu alanları ayrı ayrı okumanıza izin verir; PHP tarafı için PHP PDO ile veritabanı bağlantısı yazısına bakın. psql değişkenleri psql komutları yazısında.

Sınıf 23: bütünlük kısıtı ihlalleri

23505 unique_violation: duplicate key

Birincil anahtar veya UNIQUE kısıtı olan sütuna var olan bir değeri yazmaya çalıştığınızda oluşur. DETAIL satırı çakışan anahtarı, CONSTRAINT NAME alanı kısıtın adını verir. Çözüm duruma göre değişir: değer gerçekten tekrar ediyorsa uygulama kullanıcıya anlamlı bir mesaj göstermelidir; "varsa güncelle, yoksa ekle" istiyorsanız INSERT ... ON CONFLICT kullanın (bkz. UPSERT ve MERGE). Kimlik sütunu bir sequence'ten geliyorsa ve elle değer eklenmişse sequence geride kalmış olabilir; bu durum identity ve sequence yazısında anlatılıyor.

Kılavuzun serileştirme hatası sayfası ilginç bir noktaya değinir: uygulama mevcut anahtarlara bakıp yeni bir anahtar seçtiyse ve aynı anda başka bir oturum aynı anahtarı seçtiyse ortaya çıkan 23505 aslında bir serileştirme sorunudur ve yeniden denemek uygun olabilir. Ancak 23505 kalıcı bir veri sorununu da gösterebileceği için 40001 gibi koşulsuz yeniden denenmemelidir.

23503 foreign_key_violation

İki yönde ortaya çıkar. Birincisi, alt tabloya üst tabloda olmayan bir anahtar yazmaktır:

sql
INSERT INTO siparis VALUES (10, 99, 100);
text
ERROR:  insert or update on table "siparis" violates foreign key constraint "siparis_musteri_id_fkey"
DETAIL:  Key (musteri_id)=(99) is not present in table "musteri".

İkincisi, hâlâ başvurulan bir üst satırı silmek ya da anahtarını değiştirmektir:

sql
INSERT INTO siparis VALUES (10, 1, 100);
DELETE FROM musteri WHERE id = 1;
text
ERROR:  update or delete on table "musteri" violates foreign key constraint "siparis_musteri_id_fkey" on table "siparis"
DETAIL:  Key (id)=(1) is still referenced from table "siparis".

Çözüm, verinin sırasını düzeltmek (önce üst satırı ekleyin, önce alt satırları silin) ya da ilişki için bilinçli bir ON DELETE CASCADE, SET NULL veya RESTRICT davranışı seçmektir. Seçenekler primary key, foreign key ve kısıtlar yazısında.

23502 not_null_violation ve 23514 check_violation

NOT NULL sütuna değer verilmediğinde ya da CHECK koşulu sağlanmadığında oluşur. İkisinde de DETAIL satırı reddedilen satırın tamamını gösterir; hangi sütunun eksik olduğunu buradan görürsünüz:

sql
INSERT INTO musteri (id, ad) VALUES (3, 'Ali');
INSERT INTO siparis VALUES (11, 1, -5);
text
ERROR:  null value in column "eposta" of relation "musteri" violates not-null constraint
DETAIL:  Failing row contains (3, null, Ali).
ERROR:  new row for relation "siparis" violates check constraint "siparis_tutar_check"
DETAIL:  Failing row contains (11, 1, -5).

Sınıf 22: veri istisnaları

Sınıf 22: veri istisnaları: 22P02 invalid_text_representation, 22012 division_by_zero ve 22001

22P02 invalid_text_representation

Bir metni hedef tipe çeviremediğinde oluşur. Klasik nedenler: sayısal sütuna boş metin göndermek, formdan gelen "abc" değerini tamsayı parametresine bağlamak, UUID sütununa bozuk değer yazmak. Mesaj, sorunlu değeri ve satırdaki konumunu gösterir:

sql
SELECT * FROM siparis WHERE id = '';
text
ERROR:  invalid input syntax for type integer: ""
LINE 1: SELECT * FROM siparis WHERE id = '';
                                         ^

Boş metin NULL değildir; boş girdiyi uygulamada NULL'a çevirin ya da sunucu tarafında NULLIF(deger, '') kullanın. Girdiyi her zaman parametre olarak bağlayın; metin birleştirerek sorgu kurmak hem bu tür hatalara hem de SQL injection riskine yol açar. Tiplerin kabul ettiği girdi biçimleri veri tipleri yazısında.

22012 division_by_zero ve 22001

SELECT 1/0; sorgusu ERROR: division by zero verir; \set VERBOSITY sqlstate ile çıktı ERROR: 22012 olur. Paydanın sıfır olabileceği hesaplarda NULLIF(payda, 0) ile sonucu NULL yapabilirsiniz. Aynı sınıftaki 22001 (string_data_right_truncation) ise varchar(n) sınırını aşan metinde görülür: varchar(3) sütununa 'abcd' eklemek ERROR: 22001: value too long for type character varying(3) verdi. Dikkat: açık dönüşüm farklı davranır; SELECT 'uzun metin'::varchar(3) hata vermeden uzu döndürdü.

Sınıf 42: sözdizimi ve erişim kuralı hataları

42P01 undefined_table: relation does not exist

En sık görülen nedenler: tablo gerçekten yok, tablo başka bir şemada ve search_path içinde değil, ya da büyük/küçük harf sorunu. Kılavuzun sözcük yapısı bölümüne göre tırnaksız tanımlayıcılar küçük harfe katlanır, çift tırnaklı tanımlayıcılar ise olduğu gibi kalır. Tabloyu "Urun" diye tırnakla oluşturduysanız tırnaksız Urun yazmak urun aranmasına yol açar:

sql
CREATE TABLE "Urun" (id int);
SELECT * FROM Urun;
text
ERROR:  relation "urun" does not exist
LINE 1: SELECT * FROM Urun;
                      ^

Mesajdaki adın küçük harfle yazıldığına dikkat edin; bu, katlamanın gerçekleştiğinin ipucudur. SELECT * FROM "Urun"; çalışır. Kalıcı çözüm, tablo ve sütun adlarında tırnak gerektirmeyen küçük harfli adlar kullanmaktır. Şema ve search_path kaynaklı durumlar için veritabanı ve şema oluşturma yazısına bakın; psql'de \dt *.* ile tüm şemalardaki tabloları listeleyebilirsiniz.

42703, 42601 ve 42883

42703 undefined_column olmayan bir sütun adında görülür (column "soyad" does not exist); yine büyük/küçük harf ve tırnak kuralı sık nedendir. 42601 syntax_error yazım hatasında ortaya çıkar ve LINE satırındaki işaret hatanın başladığı yeri gösterir: SELEC 1; için mesaj syntax error at or near "SELEC" oldu. 42883 undefined_function verilen ad ve argüman tipleriyle eşleşen fonksiyon olmadığında gelir:

sql
SELECT length(42);
text
ERROR:  function length(integer) does not exist
LINE 1: SELECT length(42);
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

İpucu satırının dediği gibi argümanı uygun tipe çevirin: length(42::text). Bu hata bir eklentinin fonksiyonunu çağırırken de görülür; eklenti o veritabanında kurulu olmayabilir.

42501 insufficient_privilege: permission denied

Oturumun rolü nesne üzerinde gerekli yetkiye sahip değildir. Yetkisiz bir rolle tabloyu okumayı denediğimizde:

sql
CREATE ROLE okuyucu;
SET ROLE okuyucu;
SELECT * FROM musteri;
text
ERROR:  permission denied for table musteri

Çözüm, gereken en küçük yetkiyi vermektir; örneğin GRANT SELECT ON musteri TO okuyucu;. Şema üzerinde USAGE yetkisi de gerekebilir. Ayrıntılar GRANT ve REVOKE, rol yapısı kullanıcı ve rol yönetimi, satır düzeyinde kısıtlama row level security yazısında. Sorunu süper kullanıcıya geçerek çözmek, uygulamaya gereğinden fazla yetki vermek demektir.

25P02: transaction iptal edildi, komutlar yok sayılıyor

Bir transaction bloğu içinde herhangi bir komut hata verdiğinde, PostgreSQL o bloğun geri kalanındaki komutları çalıştırmaz ve blok bitene kadar her komuta aynı hatayı döndürür:

sql
BEGIN;
SELECT 1/0;
SELECT 1;
ROLLBACK;
text
BEGIN
ERROR:  division by zero
ERROR:  current transaction is aborted, commands ignored until end of transaction block
ROLLBACK

Bu hata asıl sorun değildir; asıl sorun ondan önceki ilk hatadır ve log ya da uygulama çıktısında onu aramanız gerekir. Çıkış yolu ROLLBACK, hatanın öncesine dönmek istiyorsanız SAVEPOINT ve ROLLBACK TO SAVEPOINT'tir. psql'in ON_ERROR_ROLLBACK değişkeni etkileşimli kullanımda bunu otomatikleştirir. Konu BEGIN, COMMIT ve ROLLBACK yazısında ayrıntılı.

Sınıf 40: yeniden denenmesi gereken hatalar

Sınıf 40: yeniden denenmesi gereken hatalar: 40001 serialization_failure, 40P01 deadlock_detected

40001 serialization_failure

REPEATABLE READ ve SERIALIZABLE izolasyon seviyeleri, serileştirme anomalilerini önlemek için bilerek hata üretir. Örnekte birinci oturum REPEATABLE READ transaction'ı içinde bir satırı okudu ve beklerken ikinci oturum aynı satırı güncelleyip commit etti; birinci oturum satırı güncellemeye çalışınca:

sql
-- 1. oturum
\set VERBOSITY verbose
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT bakiye FROM hesap WHERE id = 1;
-- (bu sırada 2. oturum: UPDATE hesap SET bakiye = bakiye + 10 WHERE id = 1;)
UPDATE hesap SET bakiye = bakiye - 100 WHERE id = 1;
text
ERROR:  40001: could not serialize access due to concurrent update
LOCATION:  ExecUpdate, nodeModifyTable.c:2585

Kılavuz bu seviyeleri kullanan uygulamaların serileştirme hatası alan transaction'ları yeniden denemeye hazır olması gerektiğini söyler. Mesaj metni duruma göre değişir ama kod her zaman 40001'dir. Önemli kural: yalnızca son komutu değil, hangi SQL'in çalışacağına ve hangi değerlerin kullanılacağına karar veren mantık dahil tüm transaction yeniden denenmelidir. PostgreSQL bu yüzden otomatik yeniden deneme sunmaz. Yüksek çekişmede birden fazla deneme gerekebilir. Ayrıntı izolasyon seviyeleri ve MVCC yazısında.

40P01 deadlock_detected

İki transaction birbirinin tuttuğu kilidi beklediğinde PostgreSQL döngüyü fark eder ve birini iptal eder. İki oturum hesap 1 ve 2'yi ters sırada güncellediğinde bir taraf şu hatayı aldı, diğeri başarıyla commit etti:

text
ERROR:  40P01: deadlock detected
DETAIL:  Process 33264 waits for ShareLock on transaction 824; blocked by process 56000.
Process 56000 waits for ShareLock on transaction 823; blocked by process 33264.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (0,2) in relation "hesap"
LOCATION:  DeadLockReport, deadlock.c:1138

Kılavuz deadlock hatalarının da yeniden denenmesini önerilebilir sayar. Kalıcı çözüm ise satırları her transaction'da aynı sırayla (örneğin id sırasıyla) kilitlemektir. Kilit modları ve pg_locks ile izleme kilitler ve deadlock yazısında.

KodKoşul adıTipik nedenİlk çözüm
23505unique_violationVar olan anahtar tekrar yazıldıON CONFLICT veya kullanıcıya mesaj
23503foreign_key_violationOlmayan üst satır ya da başvurulan satırı silmekSırayı düzelt, ON DELETE davranışı seç
23502not_null_violationZorunlu sütun boşDeğer veya DEFAULT ver
22P02invalid_text_representationTipe çevrilemeyen metinGirdiyi doğrula, NULLIF kullan
42P01undefined_tableTablo yok, search_path, tırnakŞema adı ve harf durumunu kontrol et
42501insufficient_privilegeYetki eksikGereken GRANT
25P02in_failed_sql_transactionÖnceki hata bloğu iptal ettiROLLBACK, ilk hatayı bul
40001serialization_failureEşzamanlı güncellemeTüm transaction'ı yeniden dene
40P01deadlock_detectedTers sırada kilitYeniden dene, kilit sırasını sabitle

PL/pgSQL içinde kodla yakalamak

Fonksiyon ve DO bloklarında hataları koşul adıyla yakalayabilirsiniz; SQLSTATE değişkeni yakalanan kodu verir:

sql
DO $$
BEGIN
  INSERT INTO musteri VALUES (5, 'ayse@ornek.com', 'Ayşe Demir');
EXCEPTION WHEN unique_violation THEN
  RAISE NOTICE 'Bu e-posta zaten kayıtlı (SQLSTATE %)', SQLSTATE;
END
$$;
text
NOTICE:  Bu e-posta zaten kayıtlı (SQLSTATE 23505)
DO

Kılavuz koşul adlarının büyük veya küçük harfle yazılabileceğini ve PL/pgSQL'in uyarı sınıflarının (00, 01, 02) adlarını tanımadığını belirtir. GET STACKED DIAGNOSTICS, RAISE ile kendi kodunuzu üretmek ve performans etkileri PL/pgSQL hata yakalama yazısında.

Bağlantı ve kimlik doğrulama hataları

Sınıf 08 (bağlantı istisnası), 28 (28P01 invalid_password) ve 53300 too_many_connections sorgu çalışmadan önce, bağlantı aşamasında ortaya çıkar. "Connection refused", "password authentication failed" ve "no pg_hba.conf entry" mesajlarının nedenleri ve teşhis adımları bağlantı hataları ve connection refused yazısında.

Sık yapılan hatalar

  • Hata mesajı metnini ayrıştırmak: mesajlar sunucunun lc_messages ayarına göre çevrilebilir; kodu kullanın.
  • 25P02'yi asıl hata sanmak: önceki ilk hatayı arayın.
  • 40001 aldığında yalnızca başarısız komutu tekrarlamak: tüm transaction mantığını baştan çalıştırın.
  • 23505'i her zaman yeniden denemek: aynı veriyle tekrar denemek aynı hatayı verir; kılavuz bu kodda dikkatli olunmasını ister.
  • Yetki hatasını süper kullanıcıyla bağlanarak geçiştirmek.

Sık Sorulan Sorular

Tüm kodların tam listesi nerede?

Kılavuzun "Appendix A. PostgreSQL Error Codes" sayfasında; tablo PostgreSQL 18.6'da tanımlı tüm kodları ve PL/pgSQL koşul adlarını içerir. Bazı kodlar SQL standardında tanımlı olduğu için listede yer alır ama şu an kullanılmaz.

Hata mesajlarını Türkçe görebilir miyim?

Kılavuza göre mesaj çevirisi derleme sırasında NLS desteği seçildiyse ve lc_messages uygun ayarlandıysa mümkündür. Ancak aynı kılavuz, uygulamaların mesaj metnine değil hata koduna güvenmesini önerir; çeviri açıkken bile kod aynı kalır.

Hata hangi sorgudan geldi, nasıl bulurum?

Deadlock örneğindeki ipucu satırı ayrıntıların sunucu logunda olduğunu söyler. Log ayarları ve uzun süren sorguları bulma pg_stat_activity ve pg_stat_statements yazısında.

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 · Bağlantı hataları · PL/pgSQL hata yakalama · İzolasyon seviyeleri · Kilitler ve deadlock · SQL nedir