Postgres: hapus seluruh database sebelum membuat ulang / mengisi ulang dari skrip bash

146

Saya sedang menulis skrip shell (akan menjadi cronjob) yang akan:

1: buang database produksi saya

2: impor dump ke database pengembangan saya

Antara langkah 1 dan 2, saya perlu menghapus database pengembangan (menghapus semua tabel?). Bagaimana ini paling baik dicapai dari skrip shell? Sejauh ini, tampilannya seperti ini:

#!/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
sumber
3
oneliner untuk orang yang terburu-buru:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
ruuter
oneliner ini mengharuskan Anda memiliki izin untuk membuat / melepas database. pendekatan yang penulis coba tidak memerlukan hak khusus.
ribamar

Jawaban:

199

Saya hanya akan menjatuhkan database dan kemudian membuatnya kembali. Pada sistem UNIX atau Linux, itu harus dilakukan:

$ dropdb development_db_name
$ createdb developmnent_db_name

Begitulah cara saya melakukannya.

Haes
sumber
Ini adalah cara saya melakukannya juga. Kemudian baru kembalikan ke db yang baru dibuat.
Arthur Thomas
3
ya. ini lebih baik karena mungkin ada objek yang bukan bagian dari dump yang Anda pulihkan. dalam hal ini mereka pasti akan dibunuh.
pstanton
7
satu trik yang menghemat waktu saya adalah $ sudo -u postgres dropdb DATABASE_NAME
Alvin
41
Tapi ... bagaimana dengan hak akses dan kepemilikan database?
Emanuele Paolini
6
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_namesaya menambahkan dua terakhir secara default ke semua database
mcalex
94

Jika Anda tidak benar-benar membutuhkan cadangan database yang dibuang ke disk dalam format file skrip .sql teks biasa, Anda dapat menghubungkan pg_dumpdan pg_restorelangsung bersama-sama melalui pipa.

Untuk melepaskan dan membuat ulang tabel, Anda dapat menggunakan --cleanopsi baris perintah untuk pg_dumpmengeluarkan perintah SQL untuk membersihkan (menjatuhkan) objek database sebelum (perintah untuk) membuatnya. (Ini tidak akan menghapus seluruh database, hanya setiap tabel / urutan / indeks / dll. Sebelum membuatnya kembali.)

Dua di atas akan terlihat seperti ini:

pg_dump -U username --clean | pg_restore -U username
Bandit
sumber
2
saya suka solusi ini, karena saya memang menginginkan salinan cadangan, saya sekarang melakukan ini: 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 bekerja dengan sangat baik, terima kasih atas bantuan Anda!
Hoff
38
Waspadalah: opsi --clean hanya menghapus relasi yang ditemukan di file pemulihan. Ini berarti bahwa jika Anda menambahkan tabel untuk pengujian, kemudian ingin menghapusnya (untuk sinkronisasi dengan produksi DB misalnya), itu tidak akan dihapus.
ianaré
6
Penting untuk diingat bahwa opsi --clean pg_dump hanya berfungsi dengan cadangan teks biasa. Seperti yang dinyatakan dengan jelas oleh dokumentasi di sini postgresql.org/docs/9.4/static/app-pgdump.html , Anda perlu menggunakan --clean di pg_restore untuk pencadangan yang diarsipkan.
Kikin-Sama
8
Apakah ada cara untuk menyertakan cascade dalam opsi "--clean". Karena opsi ini terlihat tidak berguna. Saya mendapatkan "ERROR: tidak dapat melepaskan skema publik karena objek lain bergantung padanya" seperti 100% waktu menggunakannya.
pengguna4674453
Pertanyaan yang diajukan tentang menghapus semua tabel. Ini hanya menghapus tabel yang ditemukan dalam database tempat pg_dump dibuang.
jbg
13

Meskipun baris berikut diambil dari skrip batch windows, perintahnya harus sangat mirip:

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

Perintah ini digunakan untuk menghapus seluruh database, dengan benar-benar menjatuhkannya. The $DATABASE(di Windows harus %DATABASE%) dalam perintah adalah variabel lingkungan gaya windows yang mengevaluasi ke nama database. Anda harus menggantinya dengan development_db_name.

Frank Bollack
sumber
5
lalu mengapa tidak menggunakan perintah dropdbdan yang sudah tersedia createdb? Jika Anda dapat menjalankan psql, Anda juga dapat menjalankannya.
Mike 'Pomax' Kamermans
10

Membuang:

pg_dump -Fc mydb > db.dump

Untuk memulihkan:

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

Jika Anda ingin membersihkan database Anda yang bernama "example_db":

1) Masuk ke db lain (misalnya 'postgres'):

psql postgres

2) Hapus database Anda:

DROP DATABASE example_db;

3) Buat kembali database Anda:

CREATE DATABASE example_db;
Kamil Siwek
sumber
7

Catatan: jawaban saya benar-benar menghapus tabel dan objek database lainnya; untuk menghapus semua data dalam tabel, yaitu memotong semua tabel , Endre Both telah memberikan pernyataan yang sama dieksekusi dengan baik (eksekusi langsung) sebulan kemudian.

Untuk kasus di mana Anda tidak bisa hanya DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;atau sesuatu, berikut ini skrip SQL yang berdiri sendiri yang saya tulis, yang aman untuk transaksi (yaitu Anda dapat meletakkannya di antara BEGIN;dan baik ROLLBACK;untuk mengujinya atau COMMIT;benar-benar melakukan perbuatan) dan membersihkan "semua" objek database ... yah, semua yang digunakan dalam database yang digunakan aplikasi kita atau yang bisa saya tambahkan dengan bijak, yaitu:

  • pemicu di tabel
  • kendala pada tabel (FK, PK CHECK,, UNIQUE)
  • indikasi
  • VIEWs (normal atau terwujud)
  • tabel
  • urutan
  • rutinitas (fungsi agregat, fungsi, prosedur)
  • semua nōn-default (yaitu bukan publicatau DB-internal) skema “kami” sendiri: skrip berguna ketika dijalankan sebagai “bukan superuser database”; superuser dapat menghapus semua skema (yang sangat penting masih secara eksplisit dikecualikan)
  • ekstensi (kontribusi pengguna tetapi saya biasanya sengaja membiarkannya)

Tidak dijatuhkan (beberapa disengaja; beberapa hanya karena saya tidak punya contoh di DB kami):

  • yang publicskema (misalnya untuk hal-hal ekstensi yang disediakan di dalamnya)
  • collations dan barang lokal lainnya
  • pemicu acara
  • penelusuran teks,… (lihat di sini untuk hal-hal lain yang mungkin saya lewatkan)
  • peran atau pengaturan keamanan lainnya
  • jenis komposit
  • tabel roti panggang
  • PLRT Asing dan tabel asing

Ini sangat berguna untuk kasus-kasus ketika dump yang ingin Anda pulihkan adalah versi skema database yang berbeda (misalnya dengan Debian dbconfig-common, Flyway atau Liquibase / DB-Manul) daripada database tempat Anda ingin mengembalikannya.

Saya juga mendapat versi yang menghapus "semua kecuali dua tabel dan apa yang menjadi miliknya" (urutan, diuji secara manual, maaf, saya tahu, membosankan) jika ada yang tertarik; perbedaannya kecil. Hubungi saya atau periksa repo ini jika tertarik.

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

Diuji, kecuali tambahan selanjutnya ( extensionsdisumbangkan oleh Clément Prévost ), di PostgreSQL 9.6 ( jessie-backports). Penghilangan agregat diuji pada 9.6 dan 12.2, prosedur penghilangan juga diuji pada 12.2. Perbaikan bug dan peningkatan lebih lanjut diterima!

mirabilos
sumber
6

Saya telah menggunakan:

pg_restore -c -d database_name filename.dump
Troy
sumber