web-dev-qa-db-fra.com

Désactivez toutes les contraintes et les vérifications de table lors de la restauration d'un vidage

J'ai obtenu un vidage de ma base de données PostgreSQL avec:

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

que je procède ensuite à restaurer dans une autre base de données avec:

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

Mon problème est que la base de données contient des contraintes référentielles, des vérifications et des déclencheurs et certains de ces contrôles (il semblerait en particulier) échouent pendant la restauration car les informations ne sont pas chargées dans l'ordre qui entraînerait le respect de ces vérifications. Par exemple, l'insertion d'une ligne dans une table peut être associée à une CHECK qui appelle une fonction plpgsql qui vérifie si une condition se vérifie dans une autre table non liée. Si cette dernière table n'est pas chargée par psql avant la première, une erreur se produit.

Ce qui suit est un SSCCE qui produit une telle base de données qui, une fois vidée avec pg_dump ne peut pas être restauré:

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

Existe-t-il un moyen de désactiver (à partir de la ligne de commande) toutes ces contraintes pendant la restauration du vidage et de les réactiver par la suite? J'utilise PostgreSQL 9.1.

21

Vous recherchez donc d'autres tables dans une CHECK contrainte .

CHECK les contraintes sont supposées exécuter des vérifications IMMUTABLE. Ce qui passe OK pour une ligne à la fois doit passer OK à à tout moment . C'est ainsi que les contraintes CHECK sont définies dans le standard SQL. C'est aussi la raison de cette restriction ( par documentation ):

Actuellement, les expressions CHECK ne peuvent pas contenir de sous-requêtes ni faire référence à des variables autres que les colonnes de la ligne actuelle.

Désormais, les expressions dans les contraintes CHECK sont autorisées à utiliser des fonctions, même des fonctions définies par l'utilisateur. Celles-ci devraient être limitées aux fonctions IMMUTABLE, mais Postgres ne les applique pas actuellement. Selon cela discussion connexe sur les hackers pgsql , une des raisons est de permettre les références à l'heure actuelle, qui n'est pas IMMUTABLE par nature.

Mais vous recherchez des lignes d'une autre table, ce qui est complètement en violation du fonctionnement des contraintes CHECK. Je ne suis pas surpris que pg_dump ne le prévoit pas.

Déplacez votre chèque dans une autre table vers un déclencheur (qui est le bon outil), et cela devrait fonctionner avec les versions modernes de Postgres.

PostgreSQL 9.2 ou version ultérieure

Bien que ce qui précède soit vrai pour n'importe quelle version de Postgres, plusieurs outils ont été introduits avec Postgres 9.2 pour vous aider dans votre situation:

option pg_dump --exclude-table-data

Une solution simple serait de vider la base de données sans données pour la table violée avec:

--exclude-table-data=my_schema.my_tbl

Ajoutez ensuite uniquement les données de ce tableau à la fin du vidage avec:

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

Mais des complications avec d'autres contraintes sur la même table pourraient en résulter. Il existe une solution encore meilleure :

NOT VALID

Il y a le NOT VALID modificateur pour les contraintes. Disponible uniquement pour la contrainte FK dans la version 9.1, mais elle a été étendue aux contraintes CHECK dans 9.2. Par documentation:

Si la contrainte est marquée NOT VALID, la vérification initiale potentiellement longue pour vérifier que toutes les lignes de la table satisfont à la contrainte est ignorée. La contrainte sera toujours appliquée contre les insertions ou mises à jour ultérieures [...]

Un fichier de vidage postgres simple se compose de trois "sections":

  • pre_data
  • data
  • post-data

Postgres 9.2 a également introduit une option pour vider les sections séparément avec -- section=sectionname, mais cela ne résout pas le problème actuel.

Voici où cela devient intéressant. Par documentation:

Les éléments de post-données incluent les définitions des index, des déclencheurs, des règles et des contraintes autres que les contraintes de vérification validées . Les éléments de pré-données incluent tous les autres éléments de définition de données.

Accentuation sur moi.
Vous pouvez remplacer la contrainte CHECK incriminée par NOT VALID, qui déplace la contrainte vers post-data section. Déposer et recréer:

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

Cela devrait résoudre votre problème. Vous pouvez même laisser la contrainte dans cet état , car cela reflète mieux ce qu'elle fait réellement: vérifier les nouvelles lignes, mais donner aucune garantie pour les données existantes. Il n'y a rien de mal à un NOT VALID vérifier la contrainte. Si vous préférez, vous pouvez le valider plus tard:

ALTER TABLE a VALIDATE CONSTRAINT a_constr_1;

Mais vous revenez au statu quo ante.

19
Erwin Brandstetter

Il semble que cela soit dû à la façon dont pg_dump crée le vidage. En regardant le vidage réel, j'ai vu que la contrainte CHECK était présente dans le fichier de vidage en utilisant la syntaxe qui fait partie du CREATE TABLE commande:

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

Cela crée l'échec lors de la restauration de la base de données car la vérification est mise en place avant que la table a ou la table b contiennent des données. Si toutefois, le fichier de vidage est modifié et le CHECK est ajouté à l'aide de la syntaxe suivante, à la fin du fichier de vidage:

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

... alors il n'y a pas de problème dans la restauration.

La même logique exacte peut être implémentée à l'aide d'un TRIGGER comme dans le script suivant:

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

Dans ce cas cependant, pg_dump crée (par défaut) le déclencheur à la fin du fichier de vidage (et non dans le CREATE TABLE instruction comme dans le cas d'un chèque) et donc la restauration réussit.

2