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.
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.
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:
--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.
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.