Postgres: cancella l'intero database prima di ricreare / ripopolare dallo script bash


139

Sto scrivendo uno script di shell (diventerà un cronjob) che:

1: scarica il mio database di produzione

2: importare il dump nel mio database di sviluppo

Tra i passaggi 1 e 2, è necessario cancellare il database di sviluppo (eliminare tutte le tabelle?). Come si ottiene questo meglio da uno script di shell? Finora sembra così:

#!/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

3
oneliner per le persone che dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
vanno di

questo oneliner richiede di disporre delle autorizzazioni per creare / eliminare il database. l'approccio che l'autore sta provando non richiede privilegi speciali.
Ribamar,

Risposte:


188

Vorrei semplicemente rilasciare il database e quindi ricrearlo. Su un sistema UNIX o Linux, dovrebbe farlo:

$ dropdb development_db_name
$ createdb developmnent_db_name

Ecco come lo faccio, in realtà.


È così che lo faccio anche io. Quindi ripristina semplicemente nel nuovo db creato.
Arthur Thomas,

3
Sì. questo è meglio perché potrebbero esserci oggetti che non fanno parte del dump che stai ripristinando. in questo caso verranno sicuramente uccisi.
Pstanton,

7
un trucco che mi fa risparmiare tempo è $ sudo -u postgres dropdb DATABASE_NAME
Alvin

36
Ma ... che dire delle autorizzazioni e della proprietà del database?
Emanuele Paolini,

6
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_nameaggiungo gli ultimi due per impostazione predefinita a tutti i database
mcalex,

91

Se in realtà non è necessario un backup del database scaricato su disco in un formato di file di script .sql di testo normale, è possibile connettersi pg_dumpepg_restore direttamente insieme su una pipe.

Per eliminare e ricreare le tabelle, è possibile utilizzare l' --cleanopzione della riga di comando perpg_dump emettere comandi SQL per pulire (eliminare) gli oggetti del database prima di (i comandi per) crearli. (Questo non eliminerà l'intero database, solo ogni tabella / sequenza / indice / ecc. Prima di ricrearli.)

I due precedenti sembrerebbero qualcosa del genere:

pg_dump -U username --clean | pg_restore -U username

1
mi piace questa soluzione, poiché desidero una copia di backup, lo sto facendo ora: 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 funziona come un incantesimo, grazie per il tuo aiuto!
Hoff,

38
Attenzione: l'opzione --clean rimuove solo le relazioni trovate nel file di ripristino. Questo significa che se si aggiunge un tavolo per il test, quindi da rimuovere esso (per sincronizzare con la produzione DB per esempio), sarà non essere rimossi.
ianaré,

6
È importante tenere presente che l' opzione --clean di pg_dump funziona solo con backup di testo normale. Come indicato chiaramente nella documentazione postgresql.org/docs/9.4/static/app-pgdump.html , è necessario utilizzare --clean su pg_restore per i backup archiviati.
Kikin-Sama,

6
C'è un modo per includere la cascata nell'opzione "--clean". Poiché questa opzione sembra inutile. Ricevo "ERRORE: impossibile eliminare lo schema pubblico perché altri oggetti dipendono da esso" come il 100% delle volte che lo utilizza.
user4674453

La domanda è stata posta sulla rimozione di tutte le tabelle. Questo rimuove solo le tabelle trovate nel database da cui pg_dump sta eseguendo il dumping.
jbg

13

Sebbene la seguente riga sia presa da uno script batch di Windows, il comando dovrebbe essere abbastanza simile:

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

Questo comando viene utilizzato per cancellare l'intero database, eliminandolo effettivamente. Il $DATABASE(in Windows dovrebbe essere %DATABASE%) nel comando è una variabile di ambiente in stile Windows che restituisce il nome del database. Dovrai sostituirlo con il tuo development_db_name.


4
allora perché non usare i comandi dropdbe quelli già disponibili createdb? Se è possibile eseguire psql, è possibile eseguire anche quelli.
Mike 'Pomax' Kamermans,

10

Scaricare:

pg_dump -Fc mydb > db.dump

Ripristinare:

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U myuser -d my_db db/latest.dump

7

Se si desidera pulire il database denominato "esempio_db":

1) Accedi ad un altro db (ad esempio 'postgres'):

psql postgres

2) Rimuovi il tuo database:

DROP DATABASE example_db;

3) Ricrea il tuo database:

CREATE DATABASE example_db;

6

Ho usato:

pg_restore -c -d database_name filename.dump

4

Nota: la mia risposta riguarda davvero l'eliminazione delle tabelle e di altri oggetti del database; per eliminare tutti i dati nelle tabelle, ovvero troncare tutte le tabelle , Endre Both ha fornito un'istruzione altrettanto ben eseguita (esecuzione diretta) un mese dopo.

Per i casi in cui non puoi semplicemente DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;o qualcosa del genere, ecco uno script SQL autonomo che ho scritto, che è sicuro per le transazioni (cioè puoi metterlo tra BEGIN;e o ROLLBACK;semplicemente testarlo o COMMIT;fare effettivamente l'atto) e pulisce "tutti" gli oggetti del database ... beh, tutti quelli utilizzati nel database utilizzato dalla nostra applicazione o che potrei aggiungere sensibilmente, che è:

  • si innesca sui tavoli
  • vincoli sulle tabelle (FK, PK, CHECK, UNIQUE)
  • Indici
  • VIEWs (normale o materializzato)
  • tavoli
  • sequenze
  • routine (funzioni aggregate, funzioni, procedure)
  • tutti gli schemi nōn-default (cioè no publico DB-internal) "noi" possediamo: lo script è utile quando eseguito come "non un superutente del database"; un superutente può eliminare tutti gli schemi (quelli veramente importanti sono comunque esplicitamente esclusi)
  • estensioni (fornite dall'utente ma normalmente le lascio deliberatamente)

Non vengono rilasciati (alcuni deliberati; alcuni solo perché non avevo un esempio nel nostro DB):

  • lo publicschema (ad es. per contenuti forniti dall'estensione in essi)
  • collazioni e altre cose locali
  • trigger di eventi
  • ricerca di testo, ... (vedi qui per altre cose che potrei aver perso)
  • ruoli o altre impostazioni di sicurezza
  • tipi compositi
  • tavoli toast
  • FDW e tavoli stranieri

Ciò è molto utile nei casi in cui il dump che si desidera ripristinare abbia una versione dello schema del database diversa (ad es. Con Debian dbconfig-common, Flyway o Liquibase / DB-Manul) rispetto al database in cui si desidera ripristinarlo.

Ho anche una versione che elimina "tutto tranne due tabelle e ciò che appartiene a loro" (una sequenza, testata manualmente, scusate, lo so, noioso) nel caso qualcuno fosse interessato; il diff è piccolo. Contattatemi o controllate questo repository se interessati.

SQL

-- Copyright © 2019, 2020
--      mirabilos <t.glaser@tarent.de>
--
-- 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; $$;

Testato, tranne aggiunte successive ( extensionsfornite da Clément Prévost ), su PostgreSQL 9.6 ( jessie-backports). Rimozione aggregata testata su 9.6 e 12.2, rimozione della procedura testata anche su 12.2. Bugfix e ulteriori miglioramenti sono benvenuti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.