Postgres: effacez toute la base de données avant de recréer / re-peupler à partir du script bash

139

J'écris un script shell (qui deviendra un cronjob) qui:

1: vider ma base de données de production

2: importer le dump dans ma base de données de développement

Entre les étapes 1 et 2, je dois effacer la base de données de développement (supprimer toutes les tables?). Comment cela est-il le mieux réalisé à partir d'un script shell? Jusqu'à présent, cela ressemble à ceci:

#!/bin/bash
time=`date '+%Y'-'%m'-'%d'`
# 1. export(dump) the current production database
pg_dump -U production_db_name > /backup/dir/backup-${time}.sql

# missing step: drop all tables from development database so it can be re-populated

# 2. load the backup into the development database
psql -U development_db_name < backup/dir/backup-${time}.sql
Hoff
la source
3
oneliner pour les gens pressés:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
ruuter
cet oneliner nécessite que vous ayez des autorisations pour créer / supprimer la base de données. l'approche que l'auteur essaie ne nécessite pas de privilèges spéciaux.
ribamar du

Réponses:

188

Je déposerais simplement la base de données, puis la recréerais. Sur un système UNIX ou Linux, cela devrait le faire:

$ dropdb development_db_name
$ createdb developmnent_db_name

C'est comme ça que je fais, en fait.

Haes
la source
C'est ainsi que je le fais aussi. Ensuite, restaurez simplement dans la base de données nouvellement créée.
Arthur Thomas
3
oui. c'est mieux car il peut y avoir des objets qui ne font pas partie du vidage que vous restaurez. dans ce cas, ils seront définitivement tués.
pstanton
7
une astuce qui me fait gagner du temps est $ sudo -u postgres dropdb DATABASE_NAME
Alvin
36
Mais ... qu'en est-il des autorisations et de la propriété de la base de données?
Emanuele Paolini
6
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_namej'ajoute les deux derniers par défaut à toutes les bases de données
mcalex
91

Si vous n'avez pas réellement besoin d'une sauvegarde de la base de données sauvegardée sur le disque dans un format de fichier script .sql en texte brut, vous pouvez vous connecter pg_dumpet pg_restoredirectement ensemble via un tube.

Pour supprimer et recréer des tables, vous pouvez utiliser l' --cleanoption de ligne de commande pour pg_dumpémettre des commandes SQL pour nettoyer (supprimer) les objets de base de données avant (les commandes pour) les créer. (Cela ne supprimera pas toute la base de données, juste chaque table / séquence / index / etc. Avant de les recréer.)

Les deux ci-dessus ressembleraient à ceci:

pg_dump -U username --clean | pg_restore -U username
Bandit
la source
1
j'aime cette solution, puisque je veux une copie de sauvegarde, je fais maintenant ceci: pg_dump -Ft -U production_db_name> /backup/dir/backup-${time}.tar pg_restore -U development_db_name -d development_db_name -O - -clean /backup/dir/backup-${time}.tar fonctionne comme un charme, merci pour votre aide!
Hoff
38
Attention: l'option --clean ne supprime que les relations présentes dans le fichier de restauration. Cela signifie que si vous ajoutez une table à tester, puis que vous souhaitez la supprimer (pour la synchroniser avec le DB de production par exemple), elle ne sera pas supprimée.
ianaré
6
Il est important de garder à l'esprit que l' option --clean de pg_dump ne fonctionne qu'avec des sauvegardes en texte brut. Comme la documentation l'indique clairement ici postgresql.org/docs/9.4/static/app-pgdump.html , vous devez utiliser --clean sur pg_restore pour les sauvegardes archivées.
Kikin-Sama
6
Y a-t-il un moyen d'inclure cascade dans l'option "--clean". En l'état, cette option semble inutile. J'obtiens "ERREUR: impossible de supprimer le schéma public parce que d'autres objets en dépendent" comme 100% du temps en l'utilisant.
user4674453
La question posée sur la suppression de toutes les tables. Cela supprime uniquement les tables trouvées dans la base de données à partir de laquelle pg_dump effectue le vidage.
jbg
13

Bien que la ligne suivante soit tirée d'un script de commandes Windows, la commande devrait être assez similaire:

psql -U username -h localhost -d postgres -c "DROP DATABASE \"$DATABASE\";"

Cette commande est utilisée pour effacer toute la base de données, en la supprimant. Le $DATABASE(dans Windows devrait être %DATABASE%) dans la commande est une variable d'environnement de style Windows qui correspond au nom de la base de données. Vous devrez remplacer cela par votre development_db_name.

Frank Bollack
la source
4
alors pourquoi ne pas utiliser les commandes dropdbet déjà disponibles createdb? Si vous pouvez exécuter psql, vous pouvez également les exécuter.
Mike 'Pomax' Kamermans
10

Jeter:

pg_dump -Fc mydb > db.dump

Restaurer:

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U myuser -d my_db db/latest.dump
Carlos Júlio
la source
7

Si vous souhaitez nettoyer votre base de données nommée "example_db":

1) Connectez-vous à une autre base de données (par exemple 'postgres'):

psql postgres

2) Supprimez votre base de données:

DROP DATABASE example_db;

3) Recréez votre base de données:

CREATE DATABASE example_db;
Kamil Siwek
la source
6

J'ai utilisé:

pg_restore -c -d database_name filename.dump
Troie
la source
4

Remarque: ma réponse concerne vraiment la suppression des tables et autres objets de la base de données; pour la suppression de toutes les données dans les tableaux, à savoir tronquer toutes les tables , Endre deux a fourni une déclaration (exécution directe) Similairement bien exécuté un mois plus tard.

Pour les cas où vous ne pouvez pas simplement DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;ou quelque chose du genre, voici un script SQL autonome que j'ai écrit, qui est sûr pour les transactions (c'est-à-dire que vous pouvez le placer entre BEGIN;et soit ROLLBACK;pour simplement le tester, soit COMMIT;pour faire l'acte) et nettoie «tous» les objets de la base de données ... eh bien, tous ceux utilisés dans la base de données que notre application utilise ou je pourrais raisonnablement ajouter, ce qui est:

  • déclencheurs sur les tables
  • les contraintes sur les tables (FK, PK, CHECK, UNIQUE)
  • indicēs
  • VIEWs (normal ou matérialisé)
  • les tables
  • séquences
  • routines (fonctions d'agrégation, fonctions, procédures)
  • tous les schémas nōn-default (c'est-à-dire non publicou internes à la base de données) «nous» possédons: le script est utile lorsqu'il est exécuté en tant que «pas un superutilisateur de base de données»; un superutilisateur peut supprimer tous les schémas (cependant, les plus importants sont toujours explicitement exclus)
  • extensions (fournies par l'utilisateur mais je les laisse normalement délibérément)

Non abandonné sont (certains délibérés; certains uniquement parce que je n'avais aucun exemple dans notre base de données):

  • le publicschéma (par exemple pour les éléments fournis par l'extension)
  • classements et autres informations locales
  • déclencheurs d'événements
  • recherche de texte,… (voir ici pour d'autres choses que j'ai pu manquer)
  • rôles ou autres paramètres de sécurité
  • types composites
  • tables de pain grillé
  • FDW et tables étrangères

Ceci est vraiment utile dans les cas où le vidage que vous voulez restaurer est d'une version de schéma de base de données différente (par exemple avec Debian dbconfig-common, Flyway ou Liquibase / DB-Manul) que la base de données dans laquelle vous voulez le restaurer.

J'ai aussi une version qui supprime «tout sauf deux tables et ce qui leur appartient» (une séquence, testée manuellement, désolé, je sais, ennuyeuse) au cas où quelqu'un serait intéressé; le diff est petit. Contactez-moi ou consultez ce repo si vous êtes intéressé.

SQL

-- Copyright © 2019, 2020
--      mirabilos <[email protected]>
--
-- Provided that these terms and disclaimer and all copyright notices
-- are retained or reproduced in an accompanying document, permission
-- is granted to deal in this work without restriction, including un‐
-- limited rights to use, publicly perform, distribute, sell, modify,
-- merge, give away, or sublicence.
--
-- This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
-- the utmost extent permitted by applicable law, neither express nor
-- implied; without malicious intent or gross negligence. In no event
-- may a licensor, author or contributor be held liable for indirect,
-- direct, other damage, loss, or other issues arising in any way out
-- of dealing in the work, even if advised of the possibility of such
-- damage or existence of a defect, except proven that it results out
-- of said person’s immediate fault when using the work as intended.
-- -
-- Drop everything from the PostgreSQL database.

DO $$
DECLARE
        q TEXT;
        r RECORD;
BEGIN
        -- triggers
        FOR r IN (SELECT pns.nspname, pc.relname, pt.tgname
                FROM pg_catalog.pg_trigger pt, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pt.tgrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pt.tgisinternal=false
            ) LOOP
                EXECUTE format('DROP TRIGGER %I ON %I.%I;',
                    r.tgname, r.nspname, r.relname);
        END LOOP;
        -- constraints #1: foreign key
        FOR r IN (SELECT pns.nspname, pc.relname, pcon.conname
                FROM pg_catalog.pg_constraint pcon, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pcon.conrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pcon.contype='f'
            ) LOOP
                EXECUTE format('ALTER TABLE ONLY %I.%I DROP CONSTRAINT %I;',
                    r.nspname, r.relname, r.conname);
        END LOOP;
        -- constraints #2: the rest
        FOR r IN (SELECT pns.nspname, pc.relname, pcon.conname
                FROM pg_catalog.pg_constraint pcon, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pcon.conrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pcon.contype<>'f'
            ) LOOP
                EXECUTE format('ALTER TABLE ONLY %I.%I DROP CONSTRAINT %I;',
                    r.nspname, r.relname, r.conname);
        END LOOP;
        -- indicēs
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='i'
            ) LOOP
                EXECUTE format('DROP INDEX %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- normal and materialised views
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind IN ('v', 'm')
            ) LOOP
                EXECUTE format('DROP VIEW %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- tables
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='r'
            ) LOOP
                EXECUTE format('DROP TABLE %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- sequences
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='S'
            ) LOOP
                EXECUTE format('DROP SEQUENCE %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- extensions (only if necessary; keep them normally)
        FOR r IN (SELECT pns.nspname, pe.extname
                FROM pg_catalog.pg_extension pe, pg_catalog.pg_namespace pns
                WHERE pns.oid=pe.extnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
            ) LOOP
                EXECUTE format('DROP EXTENSION %I;', r.extname);
        END LOOP;
        -- aggregate functions first (because they depend on other functions)
        FOR r IN (SELECT pns.nspname, pp.proname, pp.oid
                FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pns, pg_catalog.pg_aggregate pagg
                WHERE pns.oid=pp.pronamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pagg.aggfnoid=pp.oid
            ) LOOP
                EXECUTE format('DROP AGGREGATE %I.%I(%s);',
                    r.nspname, r.proname,
                    pg_get_function_identity_arguments(r.oid));
        END LOOP;
        -- routines (functions, aggregate functions, procedures, window functions)
        IF EXISTS (SELECT * FROM pg_catalog.pg_attribute
                WHERE attrelid='pg_catalog.pg_proc'::regclass
                    AND attname='prokind' -- PostgreSQL 11+
            ) THEN
                q := 'CASE pp.prokind
                        WHEN ''p'' THEN ''PROCEDURE''
                        WHEN ''a'' THEN ''AGGREGATE''
                        ELSE ''FUNCTION''
                    END';
        ELSIF EXISTS (SELECT * FROM pg_catalog.pg_attribute
                WHERE attrelid='pg_catalog.pg_proc'::regclass
                    AND attname='proisagg' -- PostgreSQL ≤10
            ) THEN
                q := 'CASE pp.proisagg
                        WHEN true THEN ''AGGREGATE''
                        ELSE ''FUNCTION''
                    END';
        ELSE
                q := '''FUNCTION''';
        END IF;
        FOR r IN EXECUTE 'SELECT pns.nspname, pp.proname, pp.oid, ' || q || ' AS pt
                FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pns
                WHERE pns.oid=pp.pronamespace
                    AND pns.nspname NOT IN (''information_schema'', ''pg_catalog'', ''pg_toast'')
            ' LOOP
                EXECUTE format('DROP %s %I.%I(%s);', r.pt,
                    r.nspname, r.proname,
                    pg_get_function_identity_arguments(r.oid));
        END LOOP;
        -- nōn-default schemata we own; assume to be run by a not-superuser
        FOR r IN (SELECT pns.nspname
                FROM pg_catalog.pg_namespace pns, pg_catalog.pg_roles pr
                WHERE pr.oid=pns.nspowner
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast', 'public')
                    AND pr.rolname=current_user
            ) LOOP
                EXECUTE format('DROP SCHEMA %I;', r.nspname);
        END LOOP;
        -- voilà
        RAISE NOTICE 'Database cleared!';
END; $$;

Testé, sauf ajouts ultérieurs ( extensionsapportés par Clément Prévost ), sur PostgreSQL 9.6 ( jessie-backports). Élimination des agrégats testée sur 9.6 et 12.2, élimination de procédure testée également sur 12.2. Corrections de bugs et autres améliorations bienvenues!

mirabilos
la source