Postgres: wyczyść całą bazę danych przed ponownym utworzeniem / ponownym wypełnieniem ze skryptu bash

139

Piszę skrypt powłoki (stanie się cronjob), który:

1: zrzuć moją produkcyjną bazę danych

2: zaimportuj zrzut do mojej deweloperskiej bazy danych

Między krokiem 1 a 2 muszę wyczyścić bazę danych programowania (usunąć wszystkie tabele?). Jak najlepiej to osiągnąć za pomocą skryptu powłoki? Jak dotąd wygląda to tak:

#!/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
źródło
3
oneliner dla ludzi w pośpiechu:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
ruuter
ten oneliner wymaga posiadania uprawnień do tworzenia / usuwania bazy danych. podejście, które stara się autorka, nie wymaga specjalnych przywilejów.
ribamar,

Odpowiedzi:

188

Po prostu upuściłbym bazę danych, a następnie utworzyłbym ją ponownie. W systemie UNIX lub Linux to powinno wystarczyć:

$ dropdb development_db_name
$ createdb developmnent_db_name

Właściwie tak to robię.

Haes
źródło
W ten sposób też to robię. Następnie po prostu przywróć do nowo utworzonej bazy danych.
Arthur Thomas,
3
tak. jest to lepsze, ponieważ mogą istnieć obiekty, które nie są częścią przywracanego zrzutu. w takim przypadku na pewno zostaną zabici.
pstanton
7
jedna sztuczka, która oszczędza mi czas, to $ sudo -u postgres dropdb DATABASE_NAME
Alvin
36
Ale ... co z uprawnieniami do bazy danych i własnością?
Emanuele Paolini
6
@EmanuelePaolini Domyślnie createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_namedodaję ostatnie dwa do wszystkich baz danych
mcalex
91

Jeśli nie faktycznie trzeba kopii zapasowej bazy danych po cenach dumpingowych na dysku w formacie zwykłego tekstu plik skryptu .sql, można łączyć pg_dumpi pg_restorebezpośrednio ze sobą nad rurą.

Aby usunąć i odtworzyć tabele, można użyć opcji --cleanwiersza polecenia pg_dumpdo emitowania poleceń SQL w celu wyczyszczenia (usunięcia) obiektów bazy danych przed ich utworzeniem (polecenia do) ich utworzenia. (Nie spowoduje to usunięcia całej bazy danych, tylko każdej tabeli / sekwencji / indeksu / itp. Przed ich ponownym utworzeniem).

Powyższe dwa wyglądałyby mniej więcej tak:

pg_dump -U username --clean | pg_restore -U username
Bandyta
źródło
1
Podoba mi się to rozwiązanie, ponieważ chcę kopię zapasową, teraz robię to: pg_dump -Ft -U nazwa_db_produkcji> /backup/dir/backup-${time}.tar pg_restore -U nazwa_deweloperskiej_bazy -d -deweloperska_nazwa_db -O - -clean /backup/dir/backup-${time}.tar działa jak marzenie, dzięki za pomoc!
Hoff
38
Uwaga: opcja --clean usuwa tylko te relacje, które znajdują się w pliku przywracania. Oznacza to, że jeśli dodać tabelę do testowania, to chcesz go usunąć (do synchronizacji z produkcji DB na przykład), to będzie nie zostać usunięte.
ianaré
6
Ważne jest, aby mieć na uwadze, że pg_dump za --clean opcja działa tylko z prostych kopii tekstowych. Jak jasno wynika z dokumentacji postgresql.org/docs/9.4/static/app-pgdump.html , do archiwizacji kopii zapasowych należy użyć opcji --clean na pg_restore.
Kikin-Sama
6
Czy istnieje sposób na włączenie kaskady w opcji „--clean”? W tej chwili ta opcja wygląda na bezużyteczną. Otrzymuję komunikat „BŁĄD: nie można usunąć schematu publicznego, ponieważ zależą od niego inne obiekty”, na przykład przez 100% czasu jego używania.
user4674453
Pytanie zadawane o usunięcie wszystkich tabel. Spowoduje to tylko usunięcie tabel znalezionych w bazie danych, z której zrzuca pg_dump.
jbg
13

Chociaż poniższy wiersz pochodzi ze skryptu wsadowego systemu Windows, polecenie powinno być dość podobne:

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

To polecenie służy do czyszczenia całej bazy danych, faktycznie ją upuszczając. W poleceniu $DATABASE(w systemie Windows powinno być %DATABASE%) jest zmienną środowiskową w stylu systemu Windows, której wynikiem jest nazwa bazy danych. Będziesz musiał zastąpić to swoim development_db_name.

Frank Bollack
źródło
4
dlaczego więc nie skorzystać z już dostępnych poleceń dropdbi createdb? Jeśli możesz uruchomić psql, możesz też je uruchomić.
Mike 'Pomax' Kamermans
10

Rzucić:

pg_dump -Fc mydb > db.dump

Przywrócić:

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U myuser -d my_db db/latest.dump
Carlos Júlio
źródło
7

Jeśli chcesz wyczyścić bazę danych o nazwie „example_db”:

1) Zaloguj się do innej bazy danych (na przykład „postgres”):

psql postgres

2) Usuń swoją bazę danych:

DROP DATABASE example_db;

3) Odtworzyć bazę danych:

CREATE DATABASE example_db;
Kamil Siwek
źródło
6

Użyłem:

pg_restore -c -d database_name filename.dump
Troy
źródło
4

Uwaga: moja odpowiedź dotyczy prawdziwego usunięcia tabel i innych obiektów bazy danych; w celu usunięcia wszystkich danych w tabelach, tj. obcięcia wszystkich tabel , Endre Both dostarczył podobnie dobrze wykonane (bezpośrednie wykonanie) oświadczenie miesiąc później.

W przypadkach, w których nie możesz po prostu DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;lub coś w tym stylu, oto samodzielny skrypt SQL, który napisałem, który jest bezpieczny dla transakcji (tj. Możesz go umieścić pomiędzy BEGIN;i albo ROLLBACK;po prostu to przetestować, albo COMMIT;faktycznie wykonać czynność) i czyści „wszystkie” obiekty bazy danych… no cóż, wszystkie te używane w bazie danych, których używa nasza aplikacja lub mógłbym rozsądnie dodać, czyli:

  • wyzwalacze na stołach
  • ograniczenia w tabelach (FK, PK, CHECK, UNIQUE)
  • indicēs
  • VIEWs (normalne lub zmaterializowane)
  • stoły
  • sekwencje
  • procedury (funkcje agregujące, funkcje, procedury)
  • wszystkie nōn-default (tzn. nie publiclub wewnętrzne DB) schematy, których „posiadamy”: skrypt jest użyteczny, gdy jest uruchamiany jako „nie jest superużytkownikiem bazy danych”; superużytkownik może porzucić wszystkie schematy (te naprawdę ważne są nadal wyraźnie wykluczone)
  • rozszerzenia (tworzone przez użytkowników, ale zwykle celowo je zostawiam)

Nie porzucone (niektóre celowe; niektóre tylko dlatego, że nie miałem przykładu w naszej bazie danych):

  • publicschematu (np przesuwnych warunkiem rzeczy w nich)
  • zestawienia i inne lokalne rzeczy
  • wyzwalacze zdarzeń
  • wyszukiwanie tekstowe… (zobacz tutaj inne rzeczy, które mogłem przegapić)
  • role lub inne ustawienia zabezpieczeń
  • typy złożone
  • tosty
  • FDW i tabele zagraniczne

Jest to naprawdę przydatne w przypadkach, gdy zrzut, który chcesz przywrócić, ma inną wersję schematu bazy danych (np. W przypadku Debiana dbconfig-common, Flyway lub Liquibase / DB-Manul) niż baza danych, do której chcesz go przywrócić.

Mam też wersję, która usuwa „wszystko oprócz dwóch tabel i to, co do nich należy” (sekwencja, testowana ręcznie, przepraszam, wiem, nudna) na wypadek, gdyby ktoś był zainteresowany; różnica jest mała. Skontaktuj się ze mną lub sprawdź to repozytorium, jeśli jesteś zainteresowany.

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

Przetestowano, z wyjątkiem późniejszych dodatków ( nadesłanychextensions przez Clément Prévost ), na PostgreSQL 9.6 ( jessie-backports). Usuwanie kruszywa testowano w wersjach 9.6 i 12.2, usuwanie procedur testowano również w wersji 12.2. Mile widziane poprawki błędów i dalsze ulepszenia!

mirabilos
źródło