PostgreSQL NOT VALID yabancı anahtarı, yeni başvuruları zorunlu kılarken mevcut satırların başlangıç denetimini atlar. Eşleşmeyen eski satırlar onarılıp doğrulanana kadar kalır. PostgreSQL 17: ALTER TABLE
Bu kılavuz, Terminal’de Mac kullanan ve dolu tablolara yabancı anahtar ekleyip sonuçları anlamak isteyen bir geliştiriciye yöneliktir. Eksiksiz node-postgres örneği yalnızca sentetik makale verileri kullanır ve eşleşmeyen tek bir başvuruyu kısıt oluşturma aşamasından onarıma ve başarılı doğrulamaya kadar izler.
Mevcut satırlara ve yeni yazmalara ne olur?
İki tabloyu ele alalım: articles makale kimliklerini ve başlıklarını, article_links ise bu kimliklere yapılan başvuruları içerir. Null olmayan article_id için karşılık gelen bir makale yoksa bağlantı eşleşmemiş demektir. Yabancı anahtarı NOT VALID ile eklemek, bu eski uyumsuzluğun kalmasına izin verirken sonraki yazmalar için kısıtlamayı devreye sokar. PostgreSQL 17: ALTER TABLE
| Kısıt eklendikten sonraki işlem | Bu örnekteki sonuç |
|---|---|
| Eski, eşleşmeyen bir bağlantıyı olduğu gibi bırakmak | Saklanmaya devam eder |
| Mevcut bir makaleye başvuran bağlantı eklemek | Başarılı olur |
| Eksik bir makaleye başvuran bağlantı eklemek | Başarısız olur |
| Bağlantının makale kimliğini başka bir eksik kimlikle değiştirmek | Başarısız olur |
| Eşleşmeyen bir bağlantı varken doğrulamak | Doğrulama başarısız olur |
| Tüm eşleşmeyen başvuruları onarıp sonra doğrulamak | Doğrulama başarılı olabilir |
Bu sonuçlar, kısıtı ekleme ve doğrulama işlemlerinin belgelenmiş biçimde ayrı olmasından kaynaklanır. PostgreSQL 17: ALTER TABLE
Yabancı anahtar değeri değişmeden kalan güncellemeler önemli bir istisnadır. PostgreSQL 18’de eski ve yeni anahtarlar eşit karşılaştırılırsa uygulama yabancı anahtar denetimini atlayabilir. Dolayısıyla eski, eşleşmeyen bir bağlantının yalnızca etiketini değiştirmek, başvurusu onarılmadan başarılı olabilir. Uygulamada geçerli işlem tarafından eklenen bir satır için de özel durum bulunur; bu nedenle bu davranış her güncelleme sırasına genellenmemelidir. PostgreSQL 18 yabancı anahtar uygulaması
Pratikte bu, başarılı etiket düzenlemelerinin temizliğin tamamlandığına dair zayıf bir kanıt olduğu anlamına gelir. Eşleşmeyen başvuruları doğrudan inceleyin ve kısıtın tablo genelinde geçerli olduğunu doğrulamayla belirleyin.
Bunun için hangi PostgreSQL sürümü ve tablo yapısı gerekir?
Aşağıdaki örnek, bölümlenmemiş sıradan tablolar kullanır ve PostgreSQL 17 ile 18’i hedefler. Bölümlenmiş tablolar için önemli bir sürüm sınırı vardır: PostgreSQL 17, bölümlenmiş tablolarda yabancı anahtarların NOT VALID olarak tanımlanmasına açıkça izin vermez; PostgreSQL 18 bu özelliği getirmiştir. Desteği yüklü Node paketinizden çıkarsamak yerine sunucu sürümünü kontrol edin. PostgreSQL 17: ALTER TABLE, PostgreSQL 18 sürüm notları
PostgreSQL 17’de başvuran tablo bölümlenmişse desteklenen doğrudan yol, başvuruları düzeltip yabancı anahtarı normal şekilde eklemek ve ilk doğrulama taramasına izin vermektir. Bölümlenmiş üst tabloda aşamalı doğrulama şartsa PostgreSQL 18 belgelenmiş bu özelliği sunar. Bu öğreticide tablo yapısı basit tutulmuştur; böylece bölüm hiyerarşisinin ayrıntıları satır davranışını gölgelemez.
Başvurulan anahtar için de uygun bir benzersizlik garantisi gerekir. Burada articles.article_id bir birincil anahtardır. Başvuran sütun bilerek NULL değerine izin verir: varsayılan MATCH SIMPLE kuralına göre null başvuru için eşleşen bir makale gerekmez. Her bağlantının bir makaleyi belirtmesi gerekiyorsa ayrıca NOT NULL ekleyin. PostgreSQL 18: CREATE TABLE
Mac’inizden eksiksiz bir sentetik örnek çalıştırın
Çalışan bir PostgreSQL veritabanına, önceden ayarlanmış yerel bağlantı yapılandırmasına ve node-postgres tarafından desteklenen bir Node.js sürümüne ihtiyacınız var. Terminal’de bir proje dizininde aşağıdaki komutla paketi yükleyin, ardından programın tamamını not-valid-demo.mjs adıyla kaydedin. Resmî node-postgres başlangıç kılavuzunda paketin kurulumu belgelenmiştir. node-postgres belgeleri
npm install pg
Program, new pg.Client() kullanır. Bu modül PGHOST, PGPORT, PGDATABASE ve PGUSER gibi PostgreSQL bağlantı ortam değişkenlerini okur. Yerel veritabanınıza zaten bağlanan ayarları kullanın. node-postgres: Bağlanma
Her iki tablo da geçicidir ve yalnızca bu bağlantının oturumu boyunca var olur. PostgreSQL, geçici tablo yabancı anahtarlarının geçici tablolara başvurmasını gerektirir; bu nedenle program her iki tarafı da birlikte oluşturur. PostgreSQL 18: CREATE TABLE
Her işlem ayrı ve await edilen bir sorgu kullanır. Değerler $1 ve $2 gibi node-postgres parametreleriyle aktarılır; tablo ve kısıt adları SQL’de sabittir. node-postgres: Sorgular
import pg from 'pg';
const client = new pg.Client({ connectionTimeoutMillis: 5000 });
const validate = `ALTER TABLE pg_temp.article_links
VALIDATE CONSTRAINT article_links_article_fk`;
async function expectForeignKeyError(label, sql, values = []) {
try {
await client.query(sql, values);
} catch (error) {
if (error.code !== '23503' ||
error.constraint !== 'article_links_article_fk') throw error;
console.log(label + ': 23503 (expected)');
return;
}
throw new Error(label + ': unexpectedly succeeded');
}
async function showStatus(label) {
const result = await client.query(`
SELECT conname, convalidated
FROM pg_constraint
WHERE conrelid = 'pg_temp.article_links'::regclass
AND conname = 'article_links_article_fk'
AND contype = 'f'
`);
console.log(label, result.rows);
}
try {
await client.connect();
console.log((await client.query('SHOW server_version')).rows);
await client.query(`CREATE TEMP TABLE articles (
article_id integer PRIMARY KEY,
title text NOT NULL
)`);
await client.query(`CREATE TEMP TABLE article_links (
link_id integer PRIMARY KEY,
article_id integer,
label text NOT NULL
)`);
await client.query(
'INSERT INTO pg_temp.articles VALUES ($1, $2)',
[1, 'Desk arrangement']
);
await client.query(`INSERT INTO pg_temp.article_links VALUES
(10, 1, 'Matching article'),
(11, 99, 'Older unmatched reference')`);
await client.query(`ALTER TABLE pg_temp.article_links
ADD CONSTRAINT article_links_article_fk
FOREIGN KEY (article_id)
REFERENCES pg_temp.articles (article_id) NOT VALID`);
await showStatus('After adding');
await expectForeignKeyError(
'New unmatched reference',
'INSERT INTO pg_temp.article_links VALUES ($1, $2, $3)',
[12, 99, 'New reference']
);
await expectForeignKeyError(
'Changed unmatched reference',
'UPDATE pg_temp.article_links SET article_id = $1 WHERE link_id = $2',
[98, 11]
);
await client.query(
'UPDATE pg_temp.article_links SET label = $1 WHERE link_id = $2',
['Revised label', 11]
);
console.log('Label-only update succeeded');
const unmatched = await client.query(`
SELECT l.link_id, l.article_id, l.label
FROM pg_temp.article_links AS l
WHERE l.article_id IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM pg_temp.articles AS a
WHERE a.article_id = l.article_id
)
ORDER BY l.link_id
`);
console.log('Unmatched references', unmatched.rows);
await expectForeignKeyError('Validation before repair', validate);
await showStatus('After failed validation');
await client.query(
'UPDATE pg_temp.article_links SET article_id = $1 WHERE link_id = $2',
[1, 11]
);
await client.query(validate);
await showStatus('After successful validation');
} finally {
await client.end();
}
Kaydettiğiniz programı aynı proje dizininden çalıştırın:
node not-valid-demo.mjs
JavaScript, hazırlık sırasında bir Node sözdizimi denetiminden geçti. Araştırma ortamında veritabanı çalıştırılamadığından, aşağıdaki sonuçlar yakalanmış bir çalıştırma günlüğüne değil belgelenmiş davranışa dayanarak beklenmektedir.
Sonuçları nasıl yorumlamalısınız?
Önce kısıt convalidated değerinin false olarak ayarlanmış biçimde görünmelidir. Katalog, bu alanı kısıtın doğrulanıp doğrulanmadığını belirtmek için tanımlar. Sorgu, kısıt adlarının veritabanı genelinde benzersiz olması gerekmediğinden hem tablo adına hem de kısıt adına göre filtre uygular. PostgreSQL: pg_constraint
Ardından, yeni eşleşmeyen ekleme ve değiştirilen eşleşmeyen başvuru işlemlerinin her biri 23503 üretmelidir. PostgreSQL bu SQLSTATE değerini foreign_key_violation olarak tanımlar ve yerelleştirilmiş hata metniyle eşleştirme yapmak yerine hata kodlarının denetlenmesini önerir. Yardımcı işlev yalnızca beklenen kodu ve kısıtı kabul eder; beklenmeyen hatalar programı durdurur. PostgreSQL hata kodları
Yalnızca etiket güncellemesi, 11 bağlantısını 99 makalesine yöneltmeye devam eder. Bu nedenle tanılama sorgusu, şimdi Revised label etiketini taşıyan tam olarak bu bağlantıyı döndürmelidir. Açık IS NOT NULL koşulu isteğe bağlı null başvuruları dışarıda bırakırken NOT EXISTS, articles içinde bulunmayan null olmayan kimlikleri belirler. Bunlar, sağlanan sentetik satırlardan türetilmiş örnek sonuçlardır.
Bu aşamada doğrulama başarısız olmalıdır. Onarım 11 bağlantısını 1 makalesine başvuracak şekilde değiştirdikten sonra doğrulama başarılı olmalı ve convalidated, true değerine dönüşmelidir. VALIDATE CONSTRAINT, daha önce doğrulanmamış kısıtı tabloyu tarayarak denetler; kendisi herhangi bir onarım yapmaz. PostgreSQL: ALTER TABLE
Onarım, bu gösterim için bilinçli bir editoryal seçimdir: her iki bağlantının da Desk arrangement makalesine başvurması amaçlanmıştır. Kendi makale verilerinizde, kimliği düzeltmeye, eksik makaleyi eklemeye ya da güncelliğini yitirmiş bağlantıyı kaldırmaya karar vermeden önce eşleşmeyen her başvuruyu inceleyin. Eşleşmeyen her kimliği otomatik olarak tek bir makaleye yönlendirmek, veritabanının çözemeyeceği ayrımları gizler.
Bu sıralamayı dolu tablolara nasıl uygularsınız?
Üç ayrı aşama kullanın: NOT VALID kısıtını ekleyip işlemi kaydedin, eşleşmeyen başvuruları inceleyip onarın, sonra doğrulayın. Yabancı anahtarı eklemek her iki tabloda da SHARE ROW EXCLUSIVE kilidi alır. Doğrulama, başvuran tabloda SHARE UPDATE EXCLUSIVE, başvurulan tabloda ise ROW SHARE kilidi alır; tarama sırasında olağan güncellemelerin eşzamanlı yapılmasına izin verir. Kilit almak yine de beklemeye neden olabilir. PostgreSQL: ALTER TABLE
İlk ekleme işlemini daha sonraki doğrulamadan ayrı işlemlerde tutmak, aşamalı yaklaşımın amacını korur: daha uzun tarama başlamadan ilk işlem tamamlanabilir. Ekleme işlemini kısa süreli bir işlem olarak planlayın; tanılama sorgusunu temizliğe yardımcı bir araç olarak kullanın ve tamamlanma adımı olarak başarılı doğrulamayı kabul edin.
Gösterim, beklenen ifade hatalarının ayrı ayrı ele alınabilmesi için bilerek açık bir işlem bloğu kullanmaz. Sıralamayı node-postgres işlemlerine uyarlarsanız BEGIN, COMMIT ve ROLLBACK komutlarını kendiniz gönderin ve her ifadeyi aynı istemcideki bir işlem içinde tutun. node-postgres belgeleri, işlem ifadelerinde pool.query() kullanılmaması gerektiğini açıkça belirtir. node-postgres: İşlemler
Örnekteki üç gözlemi birlikte kullanın: eşleşmeyen satırlar onarım işini belirler, reddedilen yeni başvurular kısıtın uygulanmaya devam ettiğini gösterir, convalidated = true ise doğrulamanın başarıyla tamamlandığını kaydeder. Böylece dolu makale tablolarına ilişki eklemek için somut bir tamamlanma ölçütü elde edersiniz.
Kaynaklar
- PostgreSQL 17: ALTER TABLE — PostgreSQL Global Development Group.
- PostgreSQL: ALTER TABLE — PostgreSQL Global Development Group.
- PostgreSQL 18: Release 18 — PostgreSQL Global Development Group.
- PostgreSQL 18: CREATE TABLE — PostgreSQL Global Development Group.
- PostgreSQL: pg_constraint — PostgreSQL Global Development Group.
- PostgreSQL 18: PostgreSQL Error Codes — PostgreSQL Global Development Group.
- PostgreSQL 18 kaynak kodu: ri_triggers.c — PostgreSQL projesi.
- node-postgres belgeleri — node-postgres.
- Bağlanma — node-postgres.
- Sorgular — node-postgres.
- İşlemler — node-postgres.