ダンプの復元中にすべての制約とテーブルチェックを無効にします


19

私は私のPostgreSQLデータベースのダンプを取得しました:

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

次に、次のコマンドを使用して別のデータベースに復元します。

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

私の問題は、データベースに参照制約、チェック、トリガーが含まれていることと、これらのチェック(特にチェック)の一部が復元中に失敗することです。たとえば、テーブルへの行の挿入は、他の無関係なテーブルで条件が成立するかどうかをチェックCHECKするplpgsql関数を呼び出すに関連付けられます。後者のテーブルがpsql前者の前に読み込まれない場合、エラーが発生します。

以下は、一度ダンプするpg_dumpと復元できないデータベースを生成する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());

ダンプの復元中にこのような制約をすべて(コマンドラインから)無効にし、後で再び有効にする方法はありますか?PostgreSQL 9.1を実行しています。


私が知りたいのは、-Xには-dオプションがありませんpg_dumppg_dumpダンプ生成され、空のDBで復元を。
dezso 14

1
@dezsoそう、これらはタイプミスでした、私は質問を更新しました。悲しいことに、このダンプ、私が引用している理由により、空のDBに復元できません
マーカスジュニウスブルータス14

質問には、Postgresのバージョンが必要です。これは私が指摘しない限り明らかなはずです。
アーウィンブランドステッター14

@ErwinBrandstetter 9.6でも同じ問題を再現できます。別の例についてはbugs.debian.org/cgi-bin/bugreport.cgi?bug=859033を参照してください(より現実的な、わずかに大きい、MWE)
ミラビロス

1
@mirabilos:私は言います:CHECK制約内の他のテーブルを参照する関数を使用する場合、すべての保証は無効になります。しかし、CHECK制約を宣言するNOT VALIDことで、あらゆる点で機能しました。私が触れたことがないコーナーケースがあるかもしれません...
アーウィンブランドステッター

回答:


17

したがって、CHECK制約内の他のテーブルを検索します

CHECK制約はIMMUTABLEチェックを実行することになっています。一度に行にOKを渡すものは、いつでも OKを渡すはずです。これCHECKが、SQL標準での制約の定義方法です。それがこの制限の理由でもありますドキュメントごと):

現在、CHECK式にはサブクエリを含めることも、現在の行の列以外の変数を参照することもできません。

現在、CHECK制約内の式は、ユーザー定義関数であっても関数を使用できます。それらはIMMUTABLE機能に制限されるべきですが、Postgresは現在これを強制していません。pgsql-hackersに関するこの関連する議論によると、1つの理由はIMMUTABLE、本来ではない現在時刻への参照を許可することです。

しかし、別のテーブルの行を検索していますが、これはCHECK制約の動作方法に完全に違反しています。pg_dumpこれを提供できなかったことに驚かない。

別のテーブルのチェックをトリガー(適切なツール)に移動すると、Postgresの最新バージョンで動作するはずです。

PostgreSQL 9.2以降

上記はPostgresのどのバージョンにも当てはまりますが、Postgres 9.2には状況に役立ついくつかのツールが導入されています。

pg_dumpオプション --exclude-table-data

簡単な解決策は、違反テーブルのデータなしでdbをダンプすることです:

--exclude-table-data=my_schema.my_tbl

次に、このテーブルのデータだけをダンプの最後に追加します:

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

ただし、同じテーブルの他の制約との合併症が発生する可能性があります。さらに良い解決策があります

NOT VALID

NOT VALID制約の修飾子があります。v9.1ではFK制約でのみ使用できますがCHECK、9.2では制約に拡張されました。ドキュメントごと:

制約がマークされている場合、NOT VALIDテーブル内のすべての行が制約を満たすことを確認するための潜在的に長い初期チェックはスキップされます。制約は引き続き、後続の挿入または更新に対して実施されます[...]

単純なpostgresダンプファイルは、3つの「セクション」で構成されています。

  • pre_data
  • data
  • post-data

Postgres 9.2では、でセクションを個別にダンプするオプションも導入されました-- section=sectionnameが、それは当面の問題には役立ちません。

ここが面白いところです。ドキュメントごと:

ポストデータ項目には、検証済みのチェック制約以外のインデックス、トリガー、ルール、および制約の定義が含まれ ます。事前データ項目には、他のすべてのデータ定義項目が含まれます。

大胆な強調鉱山。
問題のCHECK制約をに変更してNOT VALID、制約をpost-dataセクションに移動できます。ドロップして再作成:

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

これで問題が解決するはずです。制約をその状態のままにしておくこともできます。これは、実際の動作をよりよく反映しているためです。新しい行をチェックしますが、既存のデータは保証しません。NOT VALIDチェック制約には何も問題はありません。必要に応じて、後で検証できます。

ALTER TABLE a VALIDATE CONSTRAINT a_constr_1;

しかし、その後は現状に戻ります。


復元できないデータベースを示すSSCCEで質問を充実させました。私はあなたの言っていることを理解していますが、SSCCEで紹介した問題のある状況が、チェックではなくトリガーで再現できない理由がわかりません。
マーカスジュニウスブルータス14

1
@MarcusJuniusBrutus:チェック制約は、作成時にすでにテーブルに存在するすべての行に対して評価されるため、トリガーは定義されたイベントでのみ実行されます。
アーウィンブランドステッター14

トリガーを使用して正確なスキーマロジックを再現しました。トリガーを使用すると、復元は実際に成功しますが、これはコマンドの一部としてsをpg_dump作成するCHECKのに対して、ダンプファイルの最後にトリガーを追加するという事実のみによるものと思われますCREATE TABLE。したがって、pg_dumpツールが別のアプローチを使用していれば、チェックケースの復元も成功した可能性があります。トリガーを使用するとDDLが正常である理由を確認できませんが、両方のケースでまったく同じロジックが実装されているため、チェックを使用するとOKではありません(自分の回答でトリガーを使用してスクリプトのバージョンを確認できます)。
マーカスジュニウスブルータス14

1
@MarcusJuniusBrutus:pg_dumpチェック制約に対して異なるDDLを生成する必要がある場合(たとえば、すべてを最後に追加する場合)、それをPostgresメーリングリストに拡張要求として投稿する必要があります。しかし、設計されていないもののチェック制約を誤って使用していることに、私はアーウィンに同意します。そのため、近い将来に変更要求が実装されるとは思わないでしょう。ところで:SSCCEは、2つのテーブル間の外部キーを使用してより適切にモデル化されます。
a_horse_with_no_name 14

@MarcusJuniusBrutus:Postgres 9.2以降には解決策があります。それが、Postgresのバージョンが重要である理由です。たぶんアップグレードはあなたのためのオプションですか?とにかくPostgres 9.1は老化しています...
アーウィンブランドステッター14

2

これpg_dumpは、ダンプの作成方法によるものと思われます。実際のダンプを見るとCHECKCREATE TABLEコマンドの一部である構文を使用して、ダンプファイルに制約が存在することがわかりました。

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

これにより、テーブルaまたはテーブルにbデータが含まれる前にチェックが行われるため、データベースの復元時に障害が発生します。ただし、ダンプファイルが編集CHECKされ、代わりに次の構文を使用して、ダンプファイルの最後に追加されます。

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

...その後、復元に問題はありません。

TRIGGER次のスクリプトのように、まったく同じロジックを実装できます。

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

ただし、この場合pg_dump、ダンプファイルの最後に(デフォルトで)トリガーを作成し(CREATE TABLEチェックの場合のようにステートメント内ではない)、復元は成功します。


例にトリガーが表示されない
サムワトキンス

1
@SamWatkinsのコピーアンドペーストエラー、修正。
マーカスジュニウスブルータス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.