Bir dökümü geri yüklerken tüm kısıtlamaları ve tablo kontrollerini devre dışı bırakın


19

PostgreSQL veri tabanımın bir dökümü ile:

pg_dump -U user-name -d db-name -f dumpfile

hangi ben daha sonra başka bir veritabanında geri yüklemek için devam:

psql X -U postgres  -d db-name-b -f dumpfile

Benim sorunum veritabanı referans kısıtlamaları, denetimleri ve tetikleyicileri içerir ve bu denetimlerin onurlandırılmasına neden olacak sırayla bilgi yüklenmediği için bu bazı (özellikle görünüyor kontroller) geri yükleme sırasında başarısız olmasıdır. Örneğin, bir tabloya satır eklemek, bir koşulun başka bir ilişkisiz tabloda tutup tutulmadığını kontrol CHECKeden bir plpgsqlişlevi çağıran bir işlevle ilişkilendirilebilir . Bu ikinci tablo öncekinden psqlönce yüklenmezse , bir hata oluşur.

Aşağıda, bir kez boşaltılamaz pg_dumpgeri yüklenemez böyle bir veritabanı üreten bir SSCCE :

CREATE OR REPLACE FUNCTION fail_if_b_empty () RETURNS BOOLEAN AS $$
    SELECT EXISTS (SELECT 1 FROM b)
$$ LANGUAGE SQL;

CREATE TABLE IF NOT EXISTS a (
     i              INTEGER                    NOT NULL
);

INSERT INTO a(i) VALUES (0),(1);
CREATE TABLE IF NOT EXISTS b (
    i  INTEGER NOT NULL
);
INSERT INTO b(i) VALUES (0);

ALTER TABLE a ADD CONSTRAINT a_constr_1 CHECK (fail_if_b_empty());

Döküm geri yüklemesi sırasında tüm bu kısıtlamaları devre dışı bırakmanın (komut satırından) ve daha sonra tekrar etkinleştirmenin bir yolu var mı? PostgreSQL 9.1 kullanıyorum.


Merak ediyorum, AFAIK için -Xve -dseçenekleri yok pg_dump. pg_dumpBir dökümü üretir olan boş bir DB restorable.
dezso

1
@dezso doğru, bunlar yazım hatalarıydı, soruyu güncelledim. Ne yazık ki dökümü, alıntı yaptığım nedenlerden dolayı boş bir DB'de geri yüklenemez .
Marcus Junius Brutus

Soru, Postgres sürümüne çok ihtiyaç duyuyor. Bu işaret etmeden açık olmalı.
Erwin Brandstetter

@ErwinBrandstetter Aynı sorunu 9.6'da yeniden oluşturabilirim , başka bir örnek için bugs.debian.org/cgi-bin/bugreport.cgi?bug=859033 adresini ziyaret edin (daha gerçek dünya, biraz daha büyük, MWE)
mirabilos

1
@mirabilos: Şunu söyleyebilirim: bir CHECKkısıtlamadaki diğer tablolara başvuran bir işlev kullanırsanız , tüm garantiler geçersiz olur, çünkü bu resmi olarak desteklenmez, sadece tolere edilir. Ancak CHECKkısıtlamayı ilan etmek NOT VALIDher açıdan benim için işe yaradı. Hiç dokunmadığım köşe vakaları olabilir ...
Erwin Brandstetter

Yanıtlar:


17

Yani diğer tabloları bir CHECKkısıtlamayla ararsınız .

CHECKkısıtlamaların IMMUTABLEkontrolleri yürütmesi gerekiyor . Bir satır için bir seferde Tamam'ı geçenler her zaman Tamam'ı geçmelidir . İşte böyle CHECKkısıtlamalar SQL standardında tanımlanmıştır. Bu kısıtlamanın nedeni de budur ( dokümantasyon başına ):

Şu anda, CHECKifadeler alt sorgular içeremez veya geçerli satırın sütunları dışındaki değişkenleri ifade etmez.

Artık, CHECKkısıtlamalardaki ifadelerin işlevleri, kullanıcı tanımlı işlevleri bile kullanmasına izin verilmektedir. Bunlar IMMUTABLEişlevlerle sınırlandırılmalıdır , ancak Postgres şu anda bunu uygulamamaktadır. Pgsql-hackerlar hakkındaki bu ilgili tartışmaya göre, bunun bir nedeni, IMMUTABLEdoğası gereği olmayan, şimdiki zamana göndermelere izin vermektir .

Ancak, başka bir tablonun satırlarına bakıyorsunuz, bu da CHECKkısıtlamaların nasıl çalışması gerektiğini tamamen ihlal ediyor. Bunun sağlanamamasına şaşırmadım pg_dump.

Çekinizi başka bir tabloda bir tetikleyiciye (doğru araç olan) taşıyın ve Postgres'in modern sürümleriyle çalışmalıdır.

PostgreSQL 9.2 veya üstü

Yukarıdakiler Postgres'in herhangi bir sürümü için geçerli olsa da, durumunuza yardımcı olmak için Postgres 9.2 ile birkaç araç sunulmuştur:

pg_dump seçeneği --exclude-table-data

Basit bir çözüm ile db ihlal tablosu için veri olmadan db dökümü olacaktır:

--exclude-table-data=my_schema.my_tbl

Ardından, dökümün sonuna yalnızca bu tabloya ilişkin verileri ekleyin:

--data-only --table=my_schema.my_tbl

Ancak aynı tablodaki diğer kısıtlamalarla komplikasyonlar ortaya çıkabilir. Daha da iyi bir çözüm var :

NOT VALID

NOT VALIDKısıtlamalar için değiştirici vardır . Yalnızca v9.1'deki FK kısıtlaması için kullanılabilir, ancak bu 9.2'deki CHECKkısıtlamalara genişletilmiştir . Belgelere göre:

Kısıtlama işaretlenirse NOT VALID, tablodaki tüm satırların kısıtlamayı karşıladığını doğrulamak için potansiyel olarak uzun olan ilk kontrol atlanır. Kısıtlama yine de sonraki ekler veya güncellemelere karşı uygulanacaktır [...]

Düz postgres döküm dosyası üç "bölümden" oluşur:

  • pre_data
  • data
  • post-data

Postgres 9.2 ayrıca bölümleri ayrı ayrı dökmek için bir seçenek sundu -- section=sectionname, ancak bu eldeki soruna yardımcı olmuyor.

Burada ilginç hale geliyor. Belgelere göre:

Veri sonrası öğeler, onaylanmış kontrol kısıtlamaları dışındaki dizinlerin, tetikleyicilerin, kuralların ve kısıtlamaların tanımlarını içerir . Ön veri öğeleri diğer tüm veri tanımı öğelerini içerir.

Cesur vurgu benim. Sınırlamayı bölüme taşıyan
rahatsız edici CHECKkısıtlamayı olarak değiştirebilirsiniz . Bırakın ve yeniden oluşturun:NOT VALIDpost-data

ALTER TABLE a DROP CONSTRAINT a_constr_1;
ALTER TABLE a ADD  CONSTRAINT a_constr_1 CHECK (fail_if_b_empty()) NOT VALID;

Bu probleminizi çözmeli. Kısıtlamayı bu durumda bile bırakabilirsiniz , çünkü bu aslında ne yaptığını daha iyi yansıtır: yeni satırları kontrol edin, ancak mevcut veriler için hiçbir garanti vermeyin . Bir NOT VALIDkontrol kısıtlamasıyla ilgili yanlış bir şey yoktur . İsterseniz daha sonra doğrulayabilirsiniz:

ALTER TABLE a VALIDATE CONSTRAINT a_constr_1;

Ama sonra statükoya geri döndün.


Soruyu geri yüklenemeyen bir veritabanı gösteren bir SSCCE ile zenginleştirdim. Söylediklerinizi anlıyorum, ancak SSCCE'mde sergilediğim sorunlu durumun neden kontroller yerine tetikleyicilerle yeniden üretilemediğini anlamıyorum.
Marcus Junius Brutus

1
@MarcusJuniusBrutus: Kontrol kısıtlamaları zaten oluşturma tablosunda bulunan tüm satırlar için değerlendirilirken tetikleyiciler yalnızca tanımlı olaylarda çalışır.
Erwin Brandstetter

Tetikleyicileri kullanarak tam şema mantığını yeniden oluşturdum. Tetikleyicileri kullanarak, geri yükleme gerçekten başarılı olur, ancak bunun yalnızca pg_dumpdöküm dosyasının sonuna tetikleyicileri eklemesinden kaynaklanır, oysa CHECKs CREATE TABLEkomutun bir parçası olarak oluşturulur . Bu nedenle, pg_dumparaç farklı bir yaklaşım kullansaydı, geri yükleme işlemi de geri çekilebilirdi . Tetikleyicileri kullanıyorsam neden DDL'imin neden iyi olduğunu göremiyorum, ancak her iki durumda da aynı mantık uygulandığı için kontrolleri kullanıyorsam (kendi cevabımda tetikleyicileri kullanarak komut dosyasının sürümünü görebilirsiniz).
Marcus Junius Brutus

1
@MarcusJuniusBrutus: pg_dumpÇek kısıtlamaları için farklı DDL oluşturması gerektiğini düşünüyorsanız (örneğin, sonunda hepsini eklemek gibi) bunu bir geliştirme isteği olarak Postgres posta listesine göndermelisiniz. Ancak Erwin'e, tasarlanmayan bir şey için kontrol kısıtlamalarını yanlış kullandığınızı kabul ediyorum. Bu nedenle, bu değişiklik talebinin yakın gelecekte uygulanmasını beklemem. Btw: SSCCE'niz, iki tablo arasında bir yabancı anahtar kullanılarak daha iyi modellenir.
a_horse_with_no_name

@MarcusJuniusBrutus: Postgres 9.2 veya sonrası için çözümler var. Bu yüzden Postgres sürümünüz çok önemlidir. Belki yükseltme sizin için bir seçenektir? Postgres 9.1 yine de yaşlanıyor ...
Erwin Brandstetter

2

Görünüşe göre bu pg_dumpdökümü yaratma yolundan kaynaklanıyor . Gerçek dökümü bakarak CHECK, CREATE TABLEkomutun bir parçası olan sözdizimini kullanarak kısıtlamanın döküm dosyasında bulunduğunu gördüm :

CREATE TABLE a (
    i integer NOT NULL,
    CONSTRAINT a_constr_1 CHECK (fail_if_b_empty())
);      

Bu, tablo aveya tabloda bherhangi bir veri bulunmadan önce denetim gerçekleştirildiğinden veritabanının geri yüklenmesi üzerine hata oluşturur . Bununla birlikte, döküm dosyası düzenlenir ve CHECKdöküm dosyasının sonunda aşağıdaki sözdizimi kullanılarak eklenir:

ALTER TABLE a ADD CONSTRAINT a_constr_1 CHECK (fail_if_b_empty()); 

... o zaman restorasyonda sorun yok.

Aynı mantık TRIGGERaşağıdaki komut dosyasındaki gibi kullanılarak uygulanabilir :

CREATE OR REPLACE FUNCTION fail_if_b_empty (
    ) RETURNS BOOLEAN AS $$
    SELECT EXISTS (SELECT 1 FROM b)
$$ LANGUAGE SQL;

DROP TABLE IF EXISTS a;

CREATE TABLE IF NOT EXISTS a (
    i   INTEGER   NOT NULL
);

INSERT INTO a(i) VALUES (0),(1);

CREATE TABLE IF NOT EXISTS b (
    i  INTEGER NOT NULL
);

INSERT INTO b(i) VALUES (0);

CREATE TRIGGER tr1 AFTER INSERT OR UPDATE ON a
FOR EACH ROW
EXECUTE PROCEDURE fail_if_b_empty();  

Ancak bu durumda, pg_dump(varsayılan olarak) döküm dosyasının sonunda tetikleyiciyi oluşturur (ve CREATE TABLEkontrol durumunda olduğu gibi ifadede değil ) ve böylece geri yükleme başarılı olur.


örneğinizde tetikleyici görmüyorum
Sam Watkins

1
@SamWatkins kopyala-yapıştır hatası, düzeltildi.
Marcus Junius Brutus
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.