Postgres: Löschen Sie die gesamte Datenbank, bevor Sie das Bash-Skript neu erstellen / neu füllen

139

Ich schreibe ein Shell-Skript (wird zu einem Cronjob), das:

1: Dump meine Produktionsdatenbank

2: Importiere den Dump in meine Entwicklungsdatenbank

Zwischen Schritt 1 und 2 muss ich die Entwicklungsdatenbank löschen (alle Tabellen löschen?). Wie wird dies am besten mit einem Shell-Skript erreicht? Bisher sieht es so aus:

#!/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
quelle
3
Oneliner für Leute in Eile:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
Ruuter
Für diesen Oneliner benötigen Sie Berechtigungen zum Erstellen / Löschen der Datenbank. Der Ansatz, den der Autor versucht, erfordert keine besonderen Berechtigungen.
Ribamar

Antworten:

187

Ich würde einfach die Datenbank löschen und sie dann neu erstellen. Auf einem UNIX- oder Linux-System sollte dies Folgendes tun:

$ dropdb development_db_name
$ createdb developmnent_db_name

So mache ich es eigentlich.

Haes
quelle
So mache ich es auch. Dann einfach in die neu erstellte Datenbank wiederherstellen.
Arthur Thomas
3
ja. Dies ist besser, da möglicherweise Objekte vorhanden sind, die nicht Teil des wiederherzustellenden Speicherauszugs sind. In diesem Fall werden sie definitiv getötet.
Pstanton
7
Ein Trick, der mir Zeit spart, ist $ sudo -u postgres dropdb DATABASE_NAME
Alvin
36
Aber ... was ist mit Datenbankberechtigungen und -besitz?
Emanuele Paolini
6
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_nameIch füge die letzten beiden standardmäßig zu allen Datenbanken hinzu
mcalex
91

Wenn Sie keine Sicherung der Datenbank benötigen, die in einem SQL-Skriptdateiformat im Klartext auf der Festplatte gespeichert ist, können Sie eine Verbindung herstellen pg_dumpund diese pg_restoredirekt über eine Pipe miteinander verbinden.

Zum Löschen und Neuerstellen von Tabellen können Sie die --cleanBefehlszeilenoption verwenden pg_dump, um SQL-Befehle zum Bereinigen (Löschen) von Datenbankobjekten vor dem Erstellen (der Befehle zum) Löschen von Datenbankobjekten auszugeben. (Dadurch wird nicht die gesamte Datenbank gelöscht, sondern nur jede Tabelle / Sequenz / Index / etc., Bevor sie neu erstellt werden.)

Die beiden oben genannten würden ungefähr so ​​aussehen:

pg_dump -U username --clean | pg_restore -U username
Bandit
quelle
1
Ich mag diese Lösung, da ich eine Sicherungskopie möchte, mache ich jetzt folgendes: pg_dump -Ft -U Produktionsdatenbankname> /backup/dir/backup-${time}.tar pg_restore -U Entwicklungsdatenbankname -d Entwicklungsdatenbankname -O - -clean /backup/dir/backup-${time}.tar funktioniert wie ein Zauber, danke für deine Hilfe!
Hoff
38
Achtung: Mit der Option --clean werden nur die in der Wiederherstellungsdatei gefundenen Beziehungen entfernt. Dies bedeutet, dass eine Tabelle, die Sie zum Testen hinzufügen und dann entfernen möchten (z. B. zur Synchronisierung mit der Produktionsdatenbank), nicht entfernt wird.
Ianaré
6
Es ist wichtig zu beachten, dass die Option --clean von pg_dump nur mit Nur-Text-Sicherungen funktioniert. Wie in der Dokumentation hier eindeutig angegeben , müssen Sie für archivierte Sicherungen --clean auf pg_restore verwenden.
Kikin-Sama
6
Gibt es eine Möglichkeit, die Kaskade in die Option "--clean" aufzunehmen? Wie es ist, sieht diese Option nutzlos aus. Ich erhalte die Meldung "FEHLER: Schema kann nicht öffentlich gelöscht werden, da andere Objekte davon abhängen", z. B. 100% der Zeit, in der es verwendet wird.
user4674453
Die Frage zum Entfernen aller Tabellen. Dadurch werden nur Tabellen entfernt, die in der Datenbank gefunden wurden, aus der pg_dump ausgegeben wird.
jbg
13

Obwohl die folgende Zeile aus einem Windows-Batch-Skript stammt, sollte der Befehl ziemlich ähnlich sein:

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

Dieser Befehl wird verwendet, um die gesamte Datenbank zu löschen, indem sie tatsächlich gelöscht wird. Die $DATABASE(in Windows sollte %DATABASE%) im Befehl ist eine Umgebungsvariable im Windows- Stil, die den Datenbanknamen ergibt. Sie müssen dies durch Ihre ersetzen development_db_name.

Frank Bollack
quelle
4
Warum dann nicht die bereits verfügbaren dropdbund createdbBefehle verwenden? Wenn Sie psql ausführen können, können Sie diese auch ausführen.
Mike 'Pomax' Kamermans
10

So entsorgen Sie:

pg_dump -Fc mydb > db.dump

Etwas wiederherstellen:

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

Wenn Sie Ihre Datenbank mit dem Namen "example_db" bereinigen möchten:

1) Melden Sie sich bei einer anderen Datenbank an (zum Beispiel 'postgres'):

psql postgres

2) Entfernen Sie Ihre Datenbank:

DROP DATABASE example_db;

3) Erstellen Sie Ihre Datenbank neu:

CREATE DATABASE example_db;
Kamil Siwek
quelle
6

Ich habe verwendet:

pg_restore -c -d database_name filename.dump
Troy
quelle
4

Hinweis: In meiner Antwort geht es darum, die Tabellen und andere Datenbankobjekte wirklich zu löschen. Zum Löschen aller Daten in den Tabellen, dh zum Abschneiden aller Tabellen , hat Endre Both einen Monat später eine ähnlich gut ausgeführte Anweisung (direkte Ausführung) bereitgestellt.

Für die Fälle , in denen Sie nicht können DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;oder etwas, hier ist ein Stand-alone - SQL - Skript schrieb ich, die transaktionssicher ist (dh Sie es zwischen setzen können BEGIN;und entweder ROLLBACK;nur testen Sie es aus oder COMMIT;tatsächlich zu tun , die Tat) und Bereinigt "alle" Datenbankobjekte ... nun, alle, die in der Datenbank verwendet werden, die unsere Anwendung verwendet, oder ich könnte vernünftigerweise hinzufügen, nämlich:

  • Trigger auf Tabellen
  • Einschränkungen für Tabellen (FK, PK, CHECK, UNIQUE)
  • indicēs
  • VIEWs (normal oder materialisiert)
  • Tabellen
  • Sequenzen
  • Routinen (Aggregatfunktionen, Funktionen, Prozeduren)
  • Alle nicht standardmäßigen (dh nicht publicoder DB-internen) Schemata, die „wir“ besitzen: Das Skript ist nützlich, wenn es als „kein Datenbank-Superuser“ ausgeführt wird. Ein Superuser kann alle Schemata löschen (die wirklich wichtigen sind jedoch immer noch ausdrücklich ausgeschlossen).
  • Erweiterungen (vom Benutzer beigesteuert, aber ich lasse sie normalerweise absichtlich in)

Nicht fallen gelassen werden (einige absichtlich; einige nur, weil ich kein Beispiel in unserer DB hatte):

  • das publicSchema (z. B. für von Erweiterungen bereitgestellte Inhalte)
  • Kollatierungen und andere Gebietsschemata
  • Ereignisauslöser
  • Textsuche,… (siehe hier für andere Sachen, die ich vielleicht verpasst habe)
  • Rollen oder andere Sicherheitseinstellungen
  • zusammengesetzte Typen
  • Toasttische
  • FDW und fremde Tabellen

Dies ist sehr nützlich für Fälle, in denen der Dump, den Sie wiederherstellen möchten, eine andere Datenbankschemaversion aufweist (z. B. mit Debian dbconfig-common, Flyway oder Liquibase / DB-Manul) als die Datenbank, in der Sie ihn wiederherstellen möchten.

Ich habe auch eine Version, die "alles außer zwei Tabellen und was dazu gehört" löscht (eine Sequenz, manuell getestet, sorry, ich weiß, langweilig), falls jemand interessiert ist; Der Unterschied ist klein. Kontaktieren Sie mich oder überprüfen Sie dieses Repo bei Interesse.

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; $$;

Getestet, mit Ausnahme späterer Ergänzungen ( extensionsbeigesteuert von Clément Prévost ), auf PostgreSQL 9.6 ( jessie-backports). Die Aggregatentfernung wurde unter 9.6 und 12.2 getestet, die Verfahrensentfernung ebenfalls unter 12.2. Bugfixes und weitere Verbesserungen sind willkommen!

Mirabilos
quelle