Nonaktifkan semua kendala dan pemeriksaan tabel saat memulihkan dump

19

Saya telah mendapatkan dump database PostgreSQL saya dengan:

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

yang kemudian saya lanjutkan untuk memulihkan di database lain dengan:

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

Masalah saya adalah bahwa database berisi batasan referensi, pemeriksaan dan pemicu dan beberapa di antaranya (cek tampaknya akan khususnya) gagal selama restorasi karena informasi tidak dimuat dalam urutan yang akan menyebabkan pemeriksaan tersebut dihormati. Misalnya, memasukkan baris dalam tabel dapat dikaitkan dengan fungsi CHECKpanggilan plpgsqlyang memeriksa apakah suatu kondisi berlaku di beberapa tabel lain yang tidak terkait. Jika tabel terakhir itu tidak dimuat oleh psqlsebelum yang pertama, kesalahan terjadi.

Berikut ini adalah SSCCE yang menghasilkan database yang pernah dibuang pg_dumptidak dapat dipulihkan:

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());

Apakah ada cara untuk menonaktifkan (dari baris perintah) semua kendala seperti itu selama restorasi dump dan mengaktifkannya kembali setelah itu? Saya menjalankan PostgreSQL 9.1.

Marcus Junius Brutus
sumber
Saya bertanya-tanya, AFAIK tidak ada -Xdan -dopsi untuk pg_dump. pg_dumpmenghasilkan dump yang dapat disimpan dalam DB kosong.
dezso
1
@dezso benar, ini kesalahan ketik, saya sudah memperbarui pertanyaan. Sayangnya, dump tidak dapat disimpan dalam DB kosong karena alasan yang saya kutip.
Marcus Junius Brutus
Pertanyaannya sangat membutuhkan versi Postgres Anda. Ini harus jelas tanpa saya tunjukkan.
Erwin Brandstetter
@ErwinBrandstetter Saya dapat mereproduksi masalah yang sama pada 9.6, lihat bugs.debian.org/cgi-bin/bugreport.cgi?bug=859033 untuk contoh lain (dunia nyata, sedikit lebih besar, MWE)
mirabilos
1
@mirabilos: Saya akan mengatakan: jika Anda menggunakan fungsi yang mereferensikan tabel lain dalam CHECKbatasan, maka semua jaminan dibatalkan, karena itu tidak didukung secara resmi, hanya ditoleransi. Tetapi menyatakan CHECKkendala NOT VALIDmembuatnya bekerja untuk saya dalam segala hal. Mungkin ada kasing sudut yang tidak pernah saya sentuh ...
Erwin Brandstetter

Jawaban:

17

Jadi Anda mencari tabel lain di a CHECK batasan .

CHECKkendala seharusnya menjalankan IMMUTABLEpemeriksaan. Apa yang lulus OK untuk satu baris sekaligus harus lulus OK setiap saat. Begitulah CHECKbatasan didefinisikan dalam standar SQL. Itu juga alasan pembatasan ini ( per dokumentasi ):

Saat ini, CHECK ekspresi tidak dapat berisi subquery atau merujuk ke variabel selain kolom dari baris saat ini.

Sekarang, ekspresi dalam CHECKbatasan diizinkan untuk menggunakan fungsi, bahkan fungsi yang ditentukan pengguna. Itu harus dibatasi pada IMMUTABLEfungsi, tetapi Postgres saat ini tidak memberlakukan ini. Menurut Ini diskusi terkait tentang peretas pgsql ini , salah satu alasannya adalah untuk memungkinkan referensi ke waktu saat ini, yang IMMUTABLEpada dasarnya tidak.

Tapi Anda mencari deretan meja lain, yang benar-benar melanggar cara CHECK seharusnya kendala bekerja. Saya tidak terkejut bahwa pg_dumpgagal menyediakan ini.

Pindahkan cek Anda di tabel lain ke a pemicu (yang merupakan alat yang tepat), dan seharusnya berfungsi dengan versi modern Postgres.

PostgreSQL 9.2 atau lebih baru

Sementara hal di atas berlaku untuk semua versi Postgres, beberapa alat telah diperkenalkan dengan Postgres 9.2 untuk membantu situasi Anda:

opsi pg_dump --exclude-table-data

Solusi sederhana adalah membuang db tanpa data untuk tabel yang melanggar dengan:

--exclude-table-data=my_schema.my_tbl

Kemudian tambahkan hanya data untuk tabel ini di akhir dump dengan:

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

Tetapi komplikasi dengan kendala lain pada tabel yang sama mungkin terjadi. Ada solusi yang lebih baik lagi :

NOT VALID

Ada NOT VALIDpengubah untuk kendala. Hanya tersedia untuk kendala FK di v9.1, tapi ini diperluas menjadi CHECKkendala di 9.2. Per dokumentasi:

Jika kendala ditandai NOT VALID, pemeriksaan awal yang berpotensi panjang untuk memverifikasi bahwa semua baris dalam tabel memenuhi batasan dilewati. Kendala masih akan diberlakukan terhadap sisipan atau pembaruan berikutnya [...]

File dump postgres polos terdiri dari tiga "bagian":

  • pre_data
  • data
  • post-data

Postgres 9.2 juga memperkenalkan opsi untuk membuang bagian secara terpisah -- section=sectionname, tetapi itu tidak membantu masalah yang ada.

Di sinilah mulai menarik. Per dokumentasi:

Pos-pos data termasuk definisi indeks, pemicu, aturan, dan kendala selain dari kendala pemeriksaan tervalidasi . Item pra-data termasuk semua item definisi data lainnya.

Penekanan berani saya.
Anda dapat mengubah CHECKbatasan yang menyinggung NOT VALID, yang memindahkan kendala ke post-databagian. Jatuhkan dan buat ulang:

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

Ini harus menyelesaikan masalah Anda. Anda bahkan dapat meninggalkan kendala dalam keadaan itu , karena itu lebih mencerminkan apa yang sebenarnya dilakukannya: memeriksa baris baru, tetapi tidak memberikan jaminan untuk data yang ada. Tidak ada yang salah dengan NOT VALIDkendala pemeriksaan. Jika suka, Anda dapat memvalidasinya nanti:

ALTER TABLE a VALIDATE CONSTRAINT a_constr_1;

Tetapi kemudian Anda kembali ke status quo ante.

Erwin Brandstetter
sumber
Saya telah memperkaya pertanyaan dengan SSCCE yang menunjukkan database yang tidak dapat dipulihkan. Saya mengerti apa yang Anda katakan, namun saya tidak melihat mengapa situasi bermasalah yang saya tunjukkan di SSCCE saya juga tidak dapat direproduksi dengan pemicu alih-alih cek.
Marcus Junius Brutus
1
@MarcusJuniusBrutus: Karena kendala pemeriksaan dievaluasi untuk semua baris yang sudah ada di tabel pada penciptaan, sementara pemicu hanya berjalan pada peristiwa yang ditentukan.
Erwin Brandstetter
Saya mereproduksi logika skema yang tepat menggunakan pemicu. Menggunakan pemicu, pemulihan memang berhasil tetapi tampaknya ini hanya karena fakta pg_dumpmenambahkan pemicu di akhir file dump, sedangkan itu menciptakan CHECKs sebagai bagian dari CREATE TABLEperintah. Jadi restorasi bisa juga berhasil untuk kasus pemeriksaan jika pg_dumpalat menggunakan pendekatan yang berbeda. Saya gagal melihat mengapa DDL saya OK jika saya menggunakan pemicu tetapi tidak OK jika saya menggunakan pemeriksaan karena logika yang sama persis diterapkan dalam kedua kasus (Anda dapat melihat versi skrip menggunakan pemicu dalam jawaban saya sendiri).
Marcus Junius Brutus
1
@MarcusJuniusBrutus: jika Anda merasa pg_dumpharus menghasilkan DDL berbeda untuk kendala pemeriksaan (misalnya menambahkan semuanya di akhir), Anda harus mempostingnya ke milis Postgres sebagai permintaan tambahan. Tapi saya setuju dengan Erwin bahwa Anda salah menggunakan batasan pemeriksaan untuk sesuatu yang tidak dirancang untuk mereka. Jadi saya tidak akan mengharapkan permintaan perubahan itu diterapkan dalam waktu dekat. Btw: SSCCE Anda akan dimodelkan lebih baik menggunakan kunci asing antara dua tabel.
a_horse_with_no_name
@MarcusJuniusBrutus: Ada solusi untuk Postgres 9.2 atau yang lebih baru. Itu sebabnya versi Postgres Anda sangat penting. Mungkin peningkatan adalah pilihan bagi Anda? Bagaimanapun, Postgres 9.1 sedang menua ...
Erwin Brandstetter
2

Tampaknya ini karena cara pg_dumpmembuat dump. Melihat dump aktual saya melihat bahwa CHECKkendala hadir dalam file dump menggunakan sintaks yang merupakan bagian dari CREATE TABLEperintah:

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

Ini menciptakan kegagalan pada pemulihan database karena cek dilakukan sebelum tabel aatau tabel bmemiliki data di dalamnya. Namun jika, file dump diedit dan CHECKditambahkan menggunakan sintaks berikut sebagai gantinya, di akhir file dump:

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

... maka tidak ada masalah dalam restorasi.

Logika yang sama persis dapat diimplementasikan menggunakan TRIGGERseperti pada skrip berikut:

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();  

Namun dalam kasus ini, pg_dumpmembuat (secara default) pemicu di akhir file dump (dan tidak dalam CREATE TABLEpernyataan seperti dalam kasus cek) dan pemulihan berhasil.

Marcus Junius Brutus
sumber
tidak melihat pemicu dalam contoh Anda
Sam Watkins
1
@SamWatkins kesalahan salin-tempel, perbaiki.
Marcus Junius Brutus