psql, PostgreSQL ile gelen etkileşimli terminal istemcisidir: psql -h sunucu -p port -U kullanıcı -d veritabanı ile bağlanır, SQL komutlarını noktalı virgülle bitirerek çalıştırırsınız. Ters eğik çizgiyle başlayan meta komutları ise sunucu değil psql'in kendisi işler; en çok kullanılanları veritabanlarını listeleyen \l, tabloları listeleyen \dt, bir tablonun yapısını gösteren \d tablo, başka veritabanına geçen \c, dosya çalıştıran \i, geniş satırları alt alta gösteren \x ve süre ölçen \timing komutlarıdır.

Bu yazıda psql başvuru sayfasındaki bağlantı seçeneklerini, sık kullanılan meta komutları, psql değişkenlerini, betik çalıştırırken hata davranışını ve .psqlrc başlangıç dosyasını, her birini PostgreSQL 18.1 üzerinde çalıştırdığımız örneklerle anlatıyoruz. Örneklerin tamamı -X (başlangıç dosyasını okuma) ve -f ile verilen betiklerle çalıştırıldı; serinin diğer yazıları PostgreSQL öğrenme rehberi sayfasında. Çıktılarda yer kazanmak için CREATE ve INSERT gibi komutların onay satırlarını yer yer çıkardık; sorgu sonuçları ve hata mesajları olduğu gibidir.

Bağlanmak: seçenekler ve varsayılanlar

Bir veritabanına bağlanmak için dört bilgi gerekir: sunucu adresi, port, veritabanı kullanıcısı ve veritabanı adı. Bunlar sırasıyla -h, -p, -U ve -d seçenekleriyle verilir; seçeneğe ait olmayan ilk argüman veritabanı adı, ikincisi kullanıcı adı olarak yorumlanır. Belgeye göre adres verilmezse psql Unix sistemlerde yerel soket üzerinden, Windows'ta ise TCP ile localhost adresine bağlanır. Varsayılan port derleme sırasında belirlenir (genellikle 5432), varsayılan kullanıcı işletim sistemi kullanıcı adınızdır, varsayılan veritabanı da kullanıcı adıyla aynıdır. Sık bağlandığınız bir sunucu için PGHOST, PGPORT, PGUSER ve PGDATABASE ortam değişkenleri yazmayı azaltır; parolalar için de ~/.pgpass dosyası önerilir.

Bağlantı bilgileri tek bir bağlantı dizesiyle (conninfo) ya da URI ile de verilebilir; -d değeri bir bağlantı dizesiyse içindeki parametreler komut satırındaki çakışan seçenekleri ezer:

bash
psql -h 127.0.0.1 -p 46410 -U postgres -d ornek_psql
psql "host=127.0.0.1 port=46410 dbname=ornek_psql user=postgres sslmode=disable"
psql postgresql://postgres@127.0.0.1:46410/ornek_psql

Bağlandıktan sonra hangi sunucuya, hangi kullanıcıyla bağlı olduğunuzu \conninfo gösterir. PostgreSQL 18 sürüm notlarına göre bu komutun çıktısı tablo biçimine geçti ve daha fazla bilgi içeriyor:

bash
\conninfo
text
      Connection Information
      Parameter       |   Value    
----------------------+------------
 Database             | ornek_psql
 Client User          | postgres
 Host                 | 127.0.0.1
 Server Port          | 46410
 Options              | 
 Protocol Version     | 3.0
 Password Used        | false
 GSSAPI Authenticated | false
 Backend PID          | 90428
 SSL Connection       | false
 Superuser            | on
 Hot Standby          | off
(12 rows)

Çıktıdaki Password Used | false satırı, bu deneme kümesinin yalnızca yerel testler için parolasız ayarlandığını gösteriyor; gerçek sunucularda bunu yapmayın. Belge, Client User alanının bağlantı anındaki kullanıcıyı, Superuser alanının ise o anki kullanıcının süper kullanıcı olup olmadığını gösterdiğini, ikisinin SET ROLE sonrasında farklılaşabileceğini belirtir. Oturum içinde başka bir veritabanına geçmek için \c veritabanı yazılır; başarısız bir \c etkileşimli kipte eski bağlantıyı korur ama betik içinde eski bağlantıyı kapatır, böylece betik yanlış veritabanında çalışmaya devam etmez.

Nesneleri listelemek: \l, \dn, \dt, \d, \du

Nesneleri listelemek: \l, \dn, \dt, \d, \du: \l veya \list, \c ad, Şemalar, \dt, \di, \dv, \ds

Örnekler için ornek_psql veritabanında satis adlı bir şema, içinde bir musteri tablosu ve public şemasında bir urun tablosu oluşturduk:

sql
CREATE SCHEMA satis;
CREATE TABLE satis.musteri (
    id integer PRIMARY KEY,
    ad text NOT NULL,
    il text DEFAULT 'İstanbul'
);
INSERT INTO satis.musteri VALUES (1, 'Ayşe Yılmaz', 'İzmir'), (2, 'Mehmet Öztürk', DEFAULT), (3, 'Çağla Şen', NULL);
CREATE TABLE urun (kod text PRIMARY KEY, fiyat numeric(10,2));
bash
\dn
\dt
\dt satis.*
text
      List of schemas
  Name  |       Owner       
--------+-------------------
 public | pg_database_owner
 satis  | postgres
(2 rows)

          List of tables
 Schema | Name | Type  |  Owner   
--------+------+-------+----------
 public | urun | table | postgres
(1 row)

           List of tables
 Schema |  Name   | Type  |  Owner   
--------+---------+-------+----------
 satis  | musteri | table | postgres
(1 row)

\dt neden musteri tablosunu göstermedi? Belgenin Patterns bölümüne göre desen verilmezse \d ailesi yalnızca arama yolunda (search_path) görünür olan nesneleri listeler; satis şeması arama yolunda olmadığı için tablo görünmez. Şema ile nesne adını nokta ile ayıran satis.* deseni, bütün şemalar için ise *.* deseni kullanılır. Desenlerde * herhangi bir karakter dizisine, ? tek karaktere karşılık gelir ve adlar SQL'deki gibi küçük harfe çevrilir; büyük harf korunsun istiyorsanız çift tırnak kullanırsınız. Arama yolunun nasıl çalıştığı veritabanı ve şema oluşturma yazısında.

bash
\d satis.musteri
text
                   Table "satis.musteri"
 Column |  Type   | Collation | Nullable |     Default      
--------+---------+-----------+----------+------------------
 id     | integer |           | not null | 
 ad     | text    |           | not null | 
 il     | text    |           |          | 'İstanbul'::text
Indexes:
    "musteri_pkey" PRIMARY KEY, btree (id)

\d sütunları, tiplerini, NULL durumunu ve varsayılanları, altında da indeksleri, kısıtları ve tetikleyicileri gösterir; \d+ buna sütun açıklamalarını, depolama bilgisini ve view tanımını ekler. Komut harflerinin bir sistemi vardır: \dt tablo, \di indeks, \dv view, \dm materialized view, \ds sequence, \dE yabancı tablo listeler ve harfler birleştirilebilir (\dti tablo ve indeksleri birlikte listeler). Sonuna + eklemek ayrıntıyı, S eklemek sistem nesnelerini, x eklemek genişletilmiş görünümü açar.

bash
\du
text
                             List of roles
 Role name |                         Attributes                         
-----------+------------------------------------------------------------
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS
KomutNe gösterir veya yapar
\l veya \listVeritabanları, sahipleri, kodlama, locale ve erişim yetkileri
\c adBaşka bir veritabanına (veya kullanıcıyla) yeniden bağlanır
\dnŞemalar
\dt, \di, \dv, \dsTablolar, indeksler, view ve sequence nesneleri
\d adTek bir nesnenin sütunları, indeksleri, kısıtları
\dfFonksiyonlar ve prosedürler
\duRoller ve öznitelikleri
\dp veya \zTablo, view ve sequence erişim yetkileri
\dxKurulu eklentiler
\xGenişletilmiş (alt alta) çıktıyı açar veya kapatır
\i dosyaDosyadaki komutları klavyeden yazılmış gibi çalıştırır
\timingHer komutun süresini milisaniye olarak gösterir
\h komutSQL komutunun söz dizimi özeti
\?Meta komutların listesi
\qpsql oturumunu kapatır

Çıktıyı biçimlendirmek: \x, \pset ve \gx

Çok sütunlu bir satır terminale sığmadığında \x genişletilmiş görünümü açar ve her sütunu ayrı satırda gösterir. Tek bir sorgu için bunu kalıcı olarak açmak istemiyorsanız sorguyu noktalı virgül yerine \gx ile bitirirsiniz. \pset ise çıktı ayarlarını değiştirir; örneğin NULL değerlerin boş görünmesi yüzünden boş metinle karıştırılmasını önlemek için \pset null ile görünür bir işaret atayabilirsiniz:

bash
\x on
SELECT * FROM satis.musteri WHERE id = 1;
\x off
\pset null '(boş)'
SELECT id, ad, il FROM satis.musteri ORDER BY id;
text
Expanded display is on.
-[ RECORD 1 ]---
id | 1
ad | Ayşe Yılmaz
il | İzmir

Expanded display is off.
Null display is "(boş)".
 id |      ad       |    il    
----+---------------+----------
  1 | Ayşe Yılmaz   | İzmir
  2 | Mehmet Öztürk | İstanbul
  3 | Çağla Şen     | (boş)
(3 rows)

İkinci müşteride DEFAULT yazdığımız için sütun varsayılanı olan İstanbul kullanıldı, üçüncüde ise açıkça NULL verdik. \x auto değeri, genişletilmiş görünümü yalnızca çıktı terminal genişliğine sığmadığında açar. \pset format ile aligned (varsayılan), unaligned, csv, html gibi biçimler seçilebilir; komut satırında aynı işi -A, --csv, -H seçenekleri, başlıksız tek değer almak için de -t görür. Betiklerden tek bir değer okumak için -At birleşimi kullanışlıdır:

bash
psql -h 127.0.0.1 -p 46410 -U postgres -X -d ornek_psql -At -c "SELECT current_database(), current_user"
text
ornek_psql|postgres

psql değişkenleri ve güvenli yer değiştirme

psql'in kendi değişkenleri vardır ve \set ad değer ile atanır; değer, önüne iki nokta konarak (:ad) hem SQL komutlarında hem meta komutlarda kullanılır. Belge burada önemli bir güvenlik ayrımı yapar: :ad biçimi değeri hiçbir tırnaklama yapmadan olduğu gibi kopyalar; değeri SQL metni olarak kullanmak için :'ad', tanımlayıcı (tablo veya sütun adı) olarak kullanmak için :"ad" yazmanız gerekir. Bu iki biçim, değerin içindeki tırnakları ve özel karakterleri doğru kaçışlar. Tırnaklı bir metnin içinde (':ad') yer değiştirme yapılmaz.

bash
\set sehir 'İzmir'
\set tablo musteri
SELECT ad FROM satis.:"tablo" WHERE il = :'sehir';
text
     ad      
-------------
 Ayşe Yılmaz
(1 row)

Bir sorgunun sonucunu değişkene almak için sorguyu \gset ile bitirirsiniz; sorgu tam olarak bir satır döndürmelidir ve her sütun aynı adlı bir değişkene yazılır. NULL dönen sütunun değişkeni atanmak yerine silinir:

bash
SELECT count(*) AS adet, max(id) AS son FROM satis.musteri \gset
\echo Toplam :adet müşteri, son id :son
text
Toplam 3 müşteri, son id 3

Bazı değişkenler özeldir ve büyük harfle yazılır: bağlantı bilgisini tutan DBNAME, USER, HOST, PORT; son sorgunun durumunu tutan ERROR, SQLSTATE, ROW_COUNT; davranışı değiştiren AUTOCOMMIT, ON_ERROR_STOP, ON_ERROR_ROLLBACK, ECHO, VERBOSITY, PROMPT1. Belge, kendi değişkenleriniz için tamamen büyük harfli adlardan kaçınmanızı önerir. Değişkenler komut satırında -v ad=değer ile de verilebilir. psql değişkenlerinin sunucu parametrelerini değiştiren SQL SET komutuyla bir ilgisi yoktur.

\gset ile \if birlikte kullanılınca betiklere koşul eklenebilir. Aşağıdaki betik, bir ürün kodunun var olup olmadığına bakıp yoksa ekliyor:

bash
SELECT EXISTS (SELECT 1 FROM urun WHERE kod = 'Z9') AS var_mi \gset
\if :var_mi
  \echo Z9 zaten var
\else
  \echo Z9 yok, ekleniyor
  INSERT INTO urun VALUES ('Z9', 99.90);
\endif
text
Z9 yok, ekleniyor
INSERT 0 1

Betik çalıştırmak: -f, \i ve hata davranışı

Betik çalıştırmak: -f, \i ve hata davranışı: Normal bitiş (ON_ERROR_STOP kapalıysa SQL hataları da 0 döndürebilir), psql'in kendi ölümcül hatası, örneğin dosya bulunamadı...

Bir dosyadaki komutları psql -f dosya.sql ile ya da oturum içinden \i dosya.sql ile çalıştırırsınız. Belge -f kullanımının psql < dosya.sql yönlendirmesine göre satır numaralı hata mesajları gibi avantajlar sağladığını belirtir. \ir ise göreli dosya adlarını o anki çalışma dizinine değil, çalışan betiğin bulunduğu dizine göre çözer; birbirini çağıran betiklerde bu fark önemlidir.

Asıl dikkat edilmesi gereken nokta hata davranışıdır. Varsayılan olarak psql bir komut hata verdiğinde sonraki komutlarla devam eder ve sonunda 0 koduyla çıkar. Aşağıdaki üç satırlık betikte ikinci satır birincil anahtar ihlali veriyor:

sql
INSERT INTO urun VALUES ('A1', 10.50);
INSERT INTO urun VALUES ('A1', 12.00);
INSERT INTO urun VALUES ('B2', 7.25);
bash
psql -h 127.0.0.1 -p 46410 -U postgres -X -d ornek_psql -f hatali.sql; echo "exit=$?"
text
INSERT 0 1
psql:hatali.sql:2: ERROR:  duplicate key value violates unique constraint "urun_pkey"
DETAIL:  Key (kod)=(A1) already exists.
INSERT 0 1
exit=0

Üçüncü satır çalıştı ve dönüş kodu 0 oldu; bir otomasyon betiği bu çalıştırmayı başarılı sayardı. ON_ERROR_STOP değişkeni açıldığında psql ilk hatada durur ve betik modunda 3 koduyla çıkar. Tabloyu boşaltıp aynı dosyayı bu kez -v ON_ERROR_STOP=1 ile çalıştırdık:

bash
psql -h 127.0.0.1 -p 46410 -U postgres -X -d ornek_psql -v ON_ERROR_STOP=1 -f hatali.sql; echo "exit=$?"
text
INSERT 0 1
psql:hatali.sql:2: ERROR:  duplicate key value violates unique constraint "urun_pkey"
DETAIL:  Key (kod)=(A1) already exists.
exit=3

Bu kez üçüncü satır çalışmadı ama ilk satırın eklediği kayıt tabloda kaldı. Ya hep ya hiç davranışı istiyorsanız -1 (--single-transaction) seçeneğini de ekleyin: psql bütün dosyayı tek bir BEGIN ve COMMIT arasına alır, ON_ERROR_STOP açıkken bir komut başarısız olursa COMMIT yerine ROLLBACK gönderir. Aynı denemeyi -1 -v ON_ERROR_STOP=1 ile yaptığımızda yine 3 kodu döndü ve SELECT count(*) FROM urun sorgusu 0 satır gösterdi; ilk ekleme de geri alınmıştı. Psql belgesi, dosyanın kendisi BEGIN, COMMIT veya transaction içinde çalışamayan bir komut içeriyorsa bu seçeneğin beklenen sonucu vermeyeceğini de belirtir. Transaction davranışının ayrıntıları ve ON_ERROR_ROLLBACK değişkeni transaction yazısında anlatılıyor.

Dönüş koduAnlamı
0Normal bitiş (ON_ERROR_STOP kapalıysa SQL hataları da 0 döndürebilir)
1psql'in kendi ölümcül hatası, örneğin dosya bulunamadı
2Etkileşimsiz oturumda sunucu bağlantısı koptu veya kurulamadı
3Betikte hata oluştu ve ON_ERROR_STOP açıktı

Hata ayıklama ve ölçüm: \errverbose, \timing, \watch

Son hatayı en ayrıntılı biçimde görmek için \errverbose yazılır; çıktı SQLSTATE kodunu ve hatanın sunucu kaynak kodundaki yerini de içerir. Aynı oturumda SQLSTATE ve ERROR değişkenleri de güncellenir:

bash
SELECT * FROM olmayan_tablo;
\errverbose
\echo :SQLSTATE :ERROR
text
psql:psql2.sql:7: ERROR:  relation "olmayan_tablo" does not exist
LINE 1: SELECT * FROM olmayan_tablo;
                      ^
psql:psql2.sql:8: error: ERROR:  42P01: relation "olmayan_tablo" does not exist
LINE 1: SELECT * FROM olmayan_tablo;
                      ^
LOCATION:  parserOpenTable, parse_relation.c:1469
42P01 true

42P01 gibi kodların anlamı ve çözümleri için SQLSTATE kodları yazısına bakın. \timing on her komuttan sonra süreyi yazdırır; ölçülen süre istemcinin gözünden toplam süredir. Bizim Windows denememizde ondalık ayırıcı virgül olarak göründü:

bash
\timing on
SELECT count(*) FROM generate_series(1, 100000);
text
Timing is on.
 count  
--------
 100000
(1 row)

Time: 6,364 ms

Sorgunun planını ve sunucu tarafındaki süreleri ayrıntılı görmek istiyorsanız \timing yerine EXPLAIN ANALYZE kullanın. \watch ise son sorguyu belirli aralıklarla tekrar çalıştırır; PostgreSQL 18 ile varsayılan bekleme süresini ayarlayan WATCH_INTERVAL değişkeni eklendi. i= aralığı, c= tekrar sayısını belirler:

bash
SELECT count(*) AS urun_sayisi FROM urun \watch i=1 c=2
text
7.11.2026 12:25:31 (every 1s)

 urun_sayisi 
-------------
           2
(1 row)

7.11.2026 12:25:32 (every 1s)

 urun_sayisi 
-------------
           2
(1 row)

.psqlrc başlangıç dosyası

psql, -X verilmedikçe bağlandıktan sonra önce sistem genelindeki psqlrc dosyasını, ardından kullanıcının kişisel başlangıç dosyasını çalıştırır. Kişisel dosya Unix'te ev dizinindeki .psqlrc, Windows'ta %APPDATA%\postgresql\psqlrc.conf dosyasıdır ve yolu PSQLRC ortam değişkeniyle değiştirilebilir. Dosya adına -18 gibi bir sürüm eki eklenirse yalnızca o sürüm okur. Biz kullanıcının kendi dosyasına dokunmamak için PSQLRC ile geçici bir dosya gösterdik:

bash
\set QUIET 1
\pset null '(null)'
\set PROMPT1 '%n@%/%R%# '
\set HISTCONTROL ignoredups
\timing on
\unset QUIET

QUIET açıkken çalışan ayar komutları bilgi mesajı yazmaz, sonda kapatılarak normal davranışa dönülür. PROMPT1 içindeki %n kullanıcı adına, %/ veritabanı adına, %R psql'in durumuna, %# ise süper kullanıcı için #, diğerleri için > işaretine dönüşür. Komut istemine %x eklemek, açık bir transaction içinde *, hatalı bir transaction içinde ! göstererek unutulmuş transaction'ları fark etmenizi sağlar. Otomasyon betiklerinde kişisel ayarların sonucu değiştirmemesi için her zaman -X kullanın.

Sık yapılan hatalar

Sık yapılan hatalar: Meta komutu noktalı virgülle bitirmek veya SQL ile aynı -c içinde..., Tek -c içindeki birden çok SQL komutunun ayrı transaction olduğunu..., Betiklerde...
  • Meta komutu noktalı virgülle bitirmek veya SQL ile aynı -c içinde karıştırmak: Bir -c değeri ya sunucunun anlayacağı saf SQL ya da tek bir meta komut olmalıdır; ikisini birden çalıştırmak için -c seçeneğini tekrarlayın.
  • Tek -c içindeki birden çok SQL komutunun ayrı transaction olduğunu sanmak: Belgeye göre tek bir -c dizesi sunucuya tek istek olarak gider ve içinde açık BEGIN/COMMIT yoksa tek transaction gibi çalışır.
  • Betiklerde ON_ERROR_STOP kullanmamak: Hata veren bir dağıtım betiği 0 koduyla bitebilir; CI ve cron işlerinde -v ON_ERROR_STOP=1 verin.
  • Değişkeni tırnaksız yerleştirmek: Kullanıcıdan gelen bir değeri :ad ile SQL'e koymak tırnak kaçışı yapmaz; metin için :'ad', tanımlayıcı için :"ad" kullanın.
  • Görünmeyen tabloyu silinmiş sanmak: \dt yalnızca arama yolundaki nesneleri listeler; \dt *.* veya \dt şema.* ile bakın.
  • Windows komut satırında Türkçe karakterli -c kullanmak: Bizim denememizde komut satırından verilen Türkçe karakterler kodlamayı bozdu; Türkçe içeren komutları UTF-8 bir dosyaya yazıp -f ile çalıştırın.

Sık Sorulan Sorular

psql ile CSV dosyası nasıl içe aktarılır?

psql'in \copy meta komutu, dosyayı sunucunun değil sizin makinenizin dosya sisteminden okur ve süper kullanıcı yetkisi gerektirmez. Seçenekler SQL COPY komutuyla aynıdır; ayrıntılar ve örnekler COPY ile CSV içe ve dışa aktarma yazısında.

psql sürümü sunucu sürümüyle aynı olmalı mı?

Belge, psql'in aynı veya daha eski ana sürümdeki sunucularla en iyi çalıştığını söyler. Özellikle \d ailesi gibi meta komutlar, psql'den daha yeni bir sunucuda başarısız olabilir; farklı sürümlü sunuculara bağlanıyorsanız en yeni psql sürümünü kullanmanız önerilir.

Sorgunun hangi SQL ile çalıştığını, yani \d komutlarının arkasını görebilir miyim?

Evet. psql'i -E ile başlatırsanız ya da \set ECHO_HIDDEN on yazarsanız meta komutların sunucuya gönderdiği katalog sorguları ekrana yazdırılır. Bu, sistem kataloglarını öğrenmek için iyi bir yoldur.

Komut geçmişi nerede saklanır?

Unix sistemlerde ~/.psql_history, Windows'ta %APPDATA%\postgresql\psql_history dosyasında saklanır. Yolu HISTFILE değişkeni veya PSQL_HISTORY ortam değişkeniyle değiştirebilir, \s ile geçmişi görüntüleyebilirsiniz.

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 · PostgreSQL kurulumu · Veritabanı ve şema oluşturma · Transaction yönetimi · COPY ile CSV aktarma · SQLSTATE hata kodları