web-dev-qa-db-ja.com

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

私は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を実行しています。

21

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

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

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

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

しかし、あなたは別のテーブルの行を調べていますが、これはCHECK制約の機能に完全に違反しています。 pg_dumpがこれに対応していないことに驚いていません。

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

PostgreSQL 9.2以降

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

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

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

--exclude-table-data=my_schema.my_tbl

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

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

ただし、同じテーブルに対する他の制約が複雑になる可能性があります。偶数がありますより良い解決策

NOT VALID

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

制約に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;

しかし、その後、あなたは現状維持に戻ります。

19

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

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ステートメントではなく)トリガーを作成するため、復元は成功します。