PostgreSQL’de Uzantı (Extension) Yönetimi: Kurulum, Güncelleme ve Bağımlılık Kontrolü
PostgreSQL kurulumu tamamlandıktan sonra çoğu zaman “bu kadarı yeter” diye düşünürüz. Ama gerçek dünyada işler hiç de böyle yürümez. Bir gün gelir, coğrafi sorgu yapmanız gerekir ve PostGIS’e ihtiyaç duyarsınız. Başka bir gün UUID üretmeniz lazım olur ya da JSON verilerini daha akıllıca işlemeniz gerekir. İşte bu noktada PostgreSQL’in uzantı (extension) sistemi hayat kurtarıcı olur. Ancak uzantıları gelişigüzel kurmak, güncellemeleri takip etmemek ve bağımlılıkları göz ardı etmek, üretim ortamınızda beklenmedik sorunlara kapı aralar. Bu yazıda uzantı yönetimini A’dan Z’ye ele alacağız.
PostgreSQL Uzantı Sistemi Nedir?
PostgreSQL uzantıları, veritabanı motoruna yeni işlevler kazandıran modüler paketlerdir. Bir uzantı; fonksiyonlar, veri tipleri, operatörler, index metodları ve hatta prosedürel diller içerebilir. En güzel yanı şu: uzantılar veritabanı düzeyinde yönetilir, yani aynı PostgreSQL instance’ınızda bazı veritabanlarında aktif bazılarında pasif olabilirler.
Uzantıların iki bileşeni vardır:
- Kontrol dosyası (.control): Uzantının metadata bilgilerini içerir, sürüm, bağımlılıklar, varsayılan şema gibi bilgiler burada tutulur
- SQL/C dosyaları: Gerçek işlevselliği sağlayan kod parçalarıdır
Bu dosyalar genellikle şu dizinde bulunur:
# PostgreSQL extension dizinini bulmak için
pg_config --sharedir
# Çıktı genellikle: /usr/share/postgresql/15/extension
ls /usr/share/postgresql/15/extension/ | head -20
Mevcut Uzantıları Keşfetmek
Başlamadan önce neyin mevcut olduğunu bilmek gerekir. PostgreSQL, kurulu uzantıları listelemek için pg_available_extensions view’ını sunar.
-- Sistemde kurulabilecek tüm uzantıları listele
SELECT name, default_version, comment
FROM pg_available_extensions
ORDER BY name;
-- Şu an aktif olan uzantıları gör
SELECT name, default_version, installed_version, comment
FROM pg_available_extensions
WHERE installed_version IS NOT NULL;
-- Belirli bir veritabanında yüklü uzantıları listele
SELECT extname, extversion, extowner::regrole AS owner
FROM pg_extension
ORDER BY extname;
Bir uzantının sürüm geçmişini ve mevcut güncelleme yollarını görmek için:
-- Uzantı güncelleme yollarını kontrol et
SELECT * FROM pg_extension_update_paths('postgis')
WHERE source = (SELECT extversion FROM pg_extension WHERE extname = 'postgis');
Uzantı Kurulumu
Temel Kurulum
En basit senaryo ile başlayalım. uuid-ossp uzantısını kuralım:
-- Bağlan ilgili veritabanına
c myapp_db
-- Uzantıyı kur
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Doğrula
SELECT uuid_generate_v4();
IF NOT EXISTS kullanmak iyi bir alışkanlıktır. Bu sayede uzantı zaten varsa hata yerine uyarı alırsınız, bu da idempotent deployment script’leri yazmanızı kolaylaştırır.
Şema Belirterek Kurulum
Bazı uzantıları belirli bir şemaya kurmak isteyebilirsiniz. Özellikle kurumsal ortamlarda şema izolasyonu önemlidir:
-- Önce şemayı oluştur
CREATE SCHEMA IF NOT EXISTS extensions;
-- Uzantıyı belirtilen şemaya kur
CREATE EXTENSION pg_trgm SCHEMA extensions;
-- Şimdi şema adıyla erişebilirsiniz
SELECT extensions.similarity('postgresql', 'postgres');
-- Ya da search_path'e ekleyin
SET search_path TO public, extensions;
SELECT similarity('postgresql', 'postgres');
Bağımlılıklı Uzantılar
İşte asıl dikkat edilmesi gereken nokta burasıdır. Bazı uzantılar başka uzantılara bağımlıdır. PostGIS buna güzel bir örnektir:
# Önce işletim sistemi paketlerini kur
sudo apt-get install postgresql-15-postgis-3 postgresql-15-postgis-3-scripts
# ya da RHEL/Rocky için
sudo dnf install postgis33_15
-- PostGIS ve bağımlılıklarını sırayla kur
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_topology;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
-- Kurulumu doğrula
SELECT PostGIS_Full_Version();
Eğer bağımlı uzantıyı önce kurmazsanız şuna benzer bir hata alırsınız:
ERROR: required extension "postgis" is not installed
HINT: Use CREATE EXTENSION ... CASCADE to install required extensions too.
CASCADE seçeneği bu sorunu otomatik çözer:
-- CASCADE ile bağımlı uzantıları otomatik kur
CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder CASCADE;
Ancak dikkatli olun. CASCADE ne kuracağını her zaman önceden kontrol edin. Üretim ortamında sürprizler istemezsiniz.
Gerçek Dünya Senaryosu: E-ticaret Platformu Kurulumu
Bir e-ticaret platformu için gerekli uzantıları sistematik biçimde kuralım. Bu senaryo, gerçek projelerde sıklıkla karşılaşılan bir durumdur:
-- Tüm kurulumları transaction içinde yap
BEGIN;
-- UUID desteği (primary key olarak kullanmak için)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Şifreleme fonksiyonları (müşteri verisi maskeleme)
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Tam metin arama (ürün arama)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS unaccent;
-- İstatistiksel sorgular için
CREATE EXTENSION IF NOT EXISTS tablefunc;
-- Kurulumları doğrula
DO $$
DECLARE
required_extensions TEXT[] := ARRAY['uuid-ossp', 'pgcrypto', 'pg_trgm', 'unaccent', 'tablefunc'];
ext TEXT;
BEGIN
FOREACH ext IN ARRAY required_extensions LOOP
IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = ext) THEN
RAISE EXCEPTION 'Extension % is not installed!', ext;
END IF;
RAISE NOTICE 'Extension % is installed: OK', ext;
END LOOP;
END;
$$;
COMMIT;
Uzantı Güncelleme Stratejileri
Güncelleme Öncesi Hazırlık
Uzantıları güncellemek, veritabanı bakımının en kritik adımlarından biridir. Doğru yapılmadığında veri kaybı ya da uygulama kesintisine yol açabilir.
-- Güncel sürümü ve kurulu sürümü karşılaştır
SELECT
name,
installed_version,
default_version,
CASE
WHEN installed_version = default_version THEN 'Güncel'
WHEN installed_version IS NULL THEN 'Kurulu Değil'
ELSE 'Güncelleme Mevcut: ' || installed_version || ' -> ' || default_version
END AS durum
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
ORDER BY name;
# Bakım penceresini logla
echo "$(date): Extension güncelleme başlıyor" >> /var/log/postgresql/maintenance.log
# Mevcut durumu yedekle
pg_dump -U postgres -d myapp_db --schema-only -f /backup/schema_before_update_$(date +%Y%m%d).sql
Kontrollü Güncelleme
-- Güncelleme yolunu önce kontrol et
SELECT * FROM pg_extension_update_paths('pg_trgm')
WHERE source = (SELECT extversion FROM pg_extension WHERE extname = 'pg_trgm')
AND target = (SELECT default_version FROM pg_available_extensions WHERE name = 'pg_trgm');
-- Güncellemeyi yap
ALTER EXTENSION pg_trgm UPDATE;
-- Belirli bir sürüme güncelle (her sürüme atlamak zorunda değilsiniz)
ALTER EXTENSION pg_trgm UPDATE TO '1.6';
-- Güncelleme sonrası doğrula
SELECT extname, extversion FROM pg_extension WHERE extname = 'pg_trgm';
PostGIS Güncelleme (Özel Senaryo)
PostGIS güncellemesi daha karmaşıktır ve sıra önemlidir:
# Önce işletim sistemi paketlerini güncelle
sudo apt-get install --only-upgrade postgresql-15-postgis-3
# PostgreSQL servisini yeniden başlat (shared library güncellemesi için)
sudo systemctl restart postgresql@15-main
-- PostGIS ve bağımlı uzantıları doğru sırayla güncelle
c myapp_db
ALTER EXTENSION postgis UPDATE;
ALTER EXTENSION postgis_topology UPDATE;
ALTER EXTENSION postgis_tiger_geocoder UPDATE;
-- Güncellemeyi doğrula
SELECT PostGIS_Full_Version();
Bağımlılık Kontrolü ve Yönetimi
Bağımlılık Haritası Çıkarma
Hangi uzantının neye bağımlı olduğunu görmek, kaldırma işlemlerinden önce kritik önem taşır:
-- Uzantı bağımlılıklarını görselleştir
SELECT
e.extname AS uzanti,
re.extname AS bagimli_oldugu_uzanti
FROM pg_extension e
JOIN pg_depend d ON d.objid = e.oid AND d.deptype = 'x'
JOIN pg_extension re ON re.oid = d.refobjid
ORDER BY e.extname;
-- Bir uzantıya bağımlı olan nesneleri bul
SELECT
classid::regclass,
objid,
deptype,
pg_describe_object(classid, objid, 0) AS nesne_aciklamasi
FROM pg_depend
WHERE refobjid = (SELECT oid FROM pg_extension WHERE extname = 'pg_trgm')
AND deptype != 'i'
LIMIT 20;
Uzantı Kaldırma
Kaldırma işlemi kurulumdan daha risklidir. Uygulamanızın ilgili uzantıya bağımlı nesneleri kullanıp kullanmadığını kontrol edin:
-- Önce bağımlı index'leri kontrol et (pg_trgm için örnek)
SELECT
schemaname,
tablename,
indexname,
indexdef
FROM pg_indexes
WHERE indexdef ILIKE '%gin%' OR indexdef ILIKE '%gist%'
ORDER BY tablename;
-- Bağımlı fonksiyonları kontrol et
SELECT
n.nspname AS schema,
p.proname AS fonksiyon,
pg_get_functiondef(p.oid) AS tanim
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE pg_get_functiondef(p.oid) ILIKE '%similarity%'
OR pg_get_functiondef(p.oid) ILIKE '%pg_trgm%';
-- Güvenli kaldırma
-- Önce bağımlı nesneleri kaldır, sonra uzantıyı sil
DROP INDEX CONCURRENTLY IF EXISTS idx_products_name_trgm;
DROP INDEX CONCURRENTLY IF EXISTS idx_products_description_trgm;
-- Şimdi uzantıyı kaldır
DROP EXTENSION pg_trgm;
-- Zorla kaldırma (bağımlı nesneleri de siler, dikkatli kullanın!)
-- DROP EXTENSION pg_trgm CASCADE;
Önemli Uzantıların Pratik Kullanımı
pg_stat_statements: Sorgu Performans Takibi
Bu uzantı her production veritabanında olmalıdır:
# postgresql.conf'a ekle
echo "shared_preload_libraries = 'pg_stat_statements'" >> /etc/postgresql/15/main/postgresql.conf
echo "pg_stat_statements.track = all" >> /etc/postgresql/15/main/postgresql.conf
echo "pg_stat_statements.max = 10000" >> /etc/postgresql/15/main/postgresql.conf
sudo systemctl restart postgresql@15-main
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- En yavaş 10 sorguyu bul
SELECT
round(total_exec_time::numeric, 2) AS toplam_sure_ms,
calls AS cagri_sayisi,
round(mean_exec_time::numeric, 2) AS ortalama_sure_ms,
round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 2) AS yuzde,
left(query, 80) AS sorgu
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
-- İstatistikleri sıfırla (bakım sonrası)
SELECT pg_stat_statements_reset();
pgaudit: Denetim Kaydı
Özellikle finans ve sağlık sektöründe zorunlu olan audit log ihtiyacı için:
sudo apt-get install postgresql-15-audit
# postgresql.conf
echo "shared_preload_libraries = 'pg_stat_statements,pgaudit'" >> /etc/postgresql/15/main/postgresql.conf
sudo systemctl restart postgresql@15-main
CREATE EXTENSION IF NOT EXISTS pgaudit;
-- Oturum düzeyinde denetimi yapılandır
ALTER SYSTEM SET pgaudit.log = 'write, ddl';
ALTER SYSTEM SET pgaudit.log_relation = on;
SELECT pg_reload_conf();
-- Rol bazlı denetim
CREATE ROLE auditor;
ALTER ROLE auditor SET pgaudit.log = 'all';
Uzantı Yönetimi için Otomasyon Script’i
Büyük ortamlarda uzantıları elle yönetmek hem yorucu hem de hata prones. İşte günlük işleri kolaylaştıran bir bash script’i:
#!/bin/bash
# pg_extension_manager.sh
# Kullanım: ./pg_extension_manager.sh [check|update|report] [veritabani_adi]
PGHOST="localhost"
PGPORT="5432"
PGUSER="postgres"
DB="${2:-postgres}"
LOG_FILE="/var/log/postgresql/extension_manager_$(date +%Y%m%d).log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
check_updates() {
log "Güncelleme kontrolü başlıyor: $DB"
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$DB" -t -A -F'|' << 'EOF'
SELECT name, installed_version, default_version
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
AND installed_version != default_version;
EOF
}
update_all() {
log "Tüm uzantılar güncelleniyor: $DB"
EXTENSIONS=$(psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$DB" -t -A << 'EOF'
SELECT extname FROM pg_extension
WHERE extname != 'plpgsql'
ORDER BY extname;
EOF
)
for ext in $EXTENSIONS; do
log "Güncelleniyor: $ext"
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$DB"
-c "ALTER EXTENSION "$ext" UPDATE;" 2>> "$LOG_FILE"
if [ $? -eq 0 ]; then
log "Başarılı: $ext"
else
log "HATA: $ext güncellenemedi!"
fi
done
}
generate_report() {
log "Rapor oluşturuluyor: $DB"
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$DB" << 'EOF'
echo '=== PostgreSQL Extension Raporu ==='
SELECT
name AS uzanti,
installed_version AS kurulu_surum,
default_version AS mevcut_surum,
CASE
WHEN installed_version = default_version THEN 'GUNCEL'
ELSE 'GUNCELLEME_VAR'
END AS durum
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
ORDER BY name;
EOF
}
case "$1" in
check) check_updates ;;
update) update_all ;;
report) generate_report ;;
*) echo "Kullanim: $0 [check|update|report] [db_adi]" ;;
esac
# Script'i çalıştırılabilir yap ve test et
chmod +x pg_extension_manager.sh
# Güncelleme kontrolü
./pg_extension_manager.sh check myapp_db
# Rapor al
./pg_extension_manager.sh report myapp_db
# Cron ile haftalık kontrol
echo "0 9 * * 1 postgres /opt/scripts/pg_extension_manager.sh report myapp_db" | sudo tee -a /etc/cron.d/pg-extension-check
Dikkat Edilmesi Gereken Noktalar
- Superuser gerekliliği:
CREATE EXTENSIONvarsayılan olarak superuser yetkisi gerektirir. PostgreSQL 13 ile gelen güvenilir uzantılar (trusted extensions) normal kullanıcılar tarafından da kurulabilir.pg_available_extensionsview’ındatrustedsütununu kontrol edin.
- Shared library uzantıları:
pg_stat_statements,auto_explaingibi uzantılarshared_preload_libraries‘e eklenmeden çalışmaz. Bu değişiklik PostgreSQL yeniden başlatması gerektirir, bunu bakım penceresinde planlayın.
- Versiyon uyumu: İşletim sistemi paket sürümü ile PostgreSQL major versiyonu uyumlu olmalıdır. PostgreSQL 15 için PostgreSQL 15 uyumlu extension paketlerini kullanın.
- Yedekleme ve extension:
pg_dumpextension tanımlarınıCREATE EXTENSIONkomutu olarak yedekler, verilerini değil. Geri yükleme sırasında ilgili extension paketlerinin hedef sunucuda kurulu olması gerekir.
- Schema migration araçları: Flyway, Liquibase gibi araçlar kullanıyorsanız extension kurulum script’lerini migration dosyalarınıza ekleyin ve tekrar çalıştırılabilir hale getirin.
Sonuç
PostgreSQL uzantı yönetimi, veritabanı yöneticiliğinin görünmez ama kritik bir parçasıdır. Doğru kurulum sırası, bağımlılık takibi ve düzenli güncelleme rutinleri oluşturmak, uzun vadede baş ağrısı yaşamanızı engeller. Üretim ortamlarında şu altın kurallara sadık kalın: her değişikliği önce test ortamında deneyin, güncelleme öncesi mutlaka schema yedeği alın ve her adımı loglayın. Uzantı yönetimini bir script ile otomatize etmek ise hem zaman kazandırır hem de insan hatasını minimuma indirir. PostgreSQL’in güçlü extension ekosisteminden yararlanmak için bu temeli sağlam kurmak şart.
