PostgreSQL Trigger Fonksiyonları Rehberi: INSERT, UPDATE, DELETE Otomasyonu

PostgreSQL'de trigger nasıl yazılır? BEFORE/AFTER farkı, NEW/OLD kayıtları, TG_OP ile işlem tespiti, audit log trigger ve otomatik timestamp güncelleme...

Trigger Nedir?

Trigger, bir tabloda INSERT, UPDATE veya DELETE işlemi gerçekleştiğinde otomatik çalışan bir fonksiyondur. Manuel müdahale gerektirmez — veritabanı seviyesinde otomasyon sağlar.

Yaygın kullanım alanları:

  • Audit log tutma (kim, ne zaman, ne değiştirdi?)
  • updated_at timestamp’ini otomatik güncelleme
  • Tutarsız veri girişini engelleme
  • Bağlı tablolarda cascade güncelleme

Trigger Anatomisi: 2 Parça

PostgreSQL’de trigger fonksiyon ve trigger tanımı olmak üzere 2 parçadan oluşur:

1. CREATE FUNCTION  → Ne yapılacak? (mantık)
2. CREATE TRIGGER   → Ne zaman, nerede, hangi işlemde? (kural)

Temel Trigger Fonksiyonu

CREATE OR REPLACE FUNCTION my_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
  -- Mantığınız buraya
  RETURN NEW; -- veya RETURN OLD (DELETE için)
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER my_trigger
AFTER INSERT ON my_table
FOR EACH ROW
EXECUTE FUNCTION my_trigger_function();

Özel Değişkenler: NEW, OLD, TG_OP

Trigger fonksiyonu içinde PostgreSQL otomatik olarak şu değişkenleri sağlar:

DeğişkenAçıklamaINSERTUPDATEDELETE
NEWYeni kayıt (eklenen/güncellenen)
OLDEski kayıt (güncellenen/silinen)
TG_OPİşlem tipi (text)'INSERT''UPDATE''DELETE'
TG_TABLE_NAMETablo adı
TG_WHEN'BEFORE' veya 'AFTER'

BEFORE vs AFTER — Hangisini Kullanmalı?

ÖzellikBEFOREAFTER
Ne zaman çalışır?İşlemden önceİşlemden sonra
NEW değiştirilebilir mi?✅ Evet❌ Hayır
Kullanım alanıValidasyon, değer değiştirmeLoglama, bildirim
RETURN değeriÖnemli (NULL → iptal)Önemsiz

Pratik Örnek 1: Otomatik updated_at

Her UPDATE’te updated_at alanını otomatik günceller:

CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Herhangi bir tabloya ekleyebilirsiniz:
CREATE TRIGGER set_updated_at_trigger
BEFORE UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();

Neden BEFORE? Çünkü NEW.updated_at değerini değiştiriyoruz — bu sadece BEFORE’da mümkün.

Pratik Örnek 2: Audit Log Trigger

Tüm değişiklikleri otomatik loglar:

-- Audit tablosu
CREATE TABLE audit_log (
  id BIGSERIAL PRIMARY KEY,
  table_name TEXT NOT NULL,
  operation TEXT NOT NULL,
  old_data JSONB,
  new_data JSONB,
  changed_by UUID DEFAULT auth.uid(),
  changed_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tek fonksiyon, tüm tablolar için
CREATE OR REPLACE FUNCTION audit_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'DELETE' THEN
    INSERT INTO audit_log (table_name, operation, old_data)
    VALUES (TG_TABLE_NAME, 'DELETE', row_to_json(OLD)::jsonb);
    RETURN OLD;
    
  ELSIF TG_OP = 'UPDATE' THEN
    INSERT INTO audit_log (table_name, operation, old_data, new_data)
    VALUES (TG_TABLE_NAME, 'UPDATE', 
            row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb);
    RETURN NEW;
    
  ELSIF TG_OP = 'INSERT' THEN
    INSERT INTO audit_log (table_name, operation, new_data)
    VALUES (TG_TABLE_NAME, 'INSERT', row_to_json(NEW)::jsonb);
    RETURN NEW;
  END IF;
  
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- İstediğiniz tablolara ekleyin:
CREATE TRIGGER audit_orders
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();

CREATE TRIGGER audit_products
AFTER INSERT OR UPDATE OR DELETE ON products
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();

Pratik Örnek 3: Stok Kontrolü (BEFORE INSERT)

Sipariş girilirken stok yeterliliğini kontrol eder:

CREATE OR REPLACE FUNCTION check_stock_before_order()
RETURNS TRIGGER AS $$
DECLARE
  available_stock NUMERIC;
BEGIN
  SELECT stock_quantity INTO available_stock
  FROM products WHERE id = NEW.product_id;
  
  IF available_stock < NEW.quantity THEN
    RAISE EXCEPTION 'Yetersiz stok: % adet mevcut, % adet istendi',
      available_stock, NEW.quantity;
  END IF;
  
  -- Stoku düş
  UPDATE products 
  SET stock_quantity = stock_quantity - NEW.quantity
  WHERE id = NEW.product_id;
  
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER check_stock_trigger
BEFORE INSERT ON order_items
FOR EACH ROW EXECUTE FUNCTION check_stock_before_order();

Trigger Ne Zaman Kullanılmamalı?

DurumNeden?Alternatif
Çok karmaşık iş mantığıDebug zorlaşırApplication layer
Dış API çağrısıTransaction bloklarQueue + worker
Yoğun INSERT tablolarıPerformans düşerBatch processing
Döngüsel bağımlılıkSonsuz döngü riskiDikkatli tasarım

Supabase’de Trigger Kullanımı

Supabase Dashboard → SQL Editor’den doğrudan çalıştırabilirsiniz:

-- Supabase'de auth.uid() ile kullanıcı takibi
CREATE OR REPLACE FUNCTION set_user_id()
RETURNS TRIGGER AS $$
BEGIN
  NEW.user_id = auth.uid();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER auto_set_user
BEFORE INSERT ON user_notes
FOR EACH ROW EXECUTE FUNCTION set_user_id();

Dikkat Edilecekler

  1. RETURN değeri: BEFORE trigger’da RETURN NULL yaparsanız işlem iptal olur.
  2. Performans: Her satır için çalışır (FOR EACH ROW). Bulk insert’lerde yavaşlatabilir.
  3. SECURITY DEFINER: Trigger fonksiyonu RLS bypass edebilir — dikkatli kullanın.
  4. Debugging: RAISE NOTICE 'debug: %', NEW.column_name; ile debug yapabilirsiniz.

İlgili Yazılar


Bu rehber PostgreSQL Trigger Docs ve Supabase Database Functions dokümantasyonuna dayanarak hazırlanmıştır.

📚 İlgili Yazılar