Postgres: limpe todo o banco de dados antes de recriar / preencher novamente a partir do script bash

139

Estou escrevendo um script de shell (se tornará um cronjob) que irá:

1: despejar meu banco de dados de produção

2: importar o despejo para meu banco de dados de desenvolvimento

Entre as etapas 1 e 2, preciso limpar o banco de dados de desenvolvimento (largar todas as tabelas?). Como isso é melhor realizado a partir de um script de shell? Até agora, fica assim:

#!/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
fonte
3
oneliner para pessoas com pressa:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
ruuter 28/12
esse oneliner exige que você tenha permissões para criar / descartar o banco de dados. a abordagem que o autor está tentando não requer privilégios especiais.
Ribamar

Respostas:

188

Eu simplesmente larguei o banco de dados e depois o recriei. Em um sistema UNIX ou Linux, isso deve ser feito:

$ dropdb development_db_name
$ createdb developmnent_db_name

É assim que eu faço, na verdade.

Haes
fonte
É assim que eu faço também. Em seguida, basta restaurar no db recém-criado.
Arthur Thomas
3
Sim. isso é melhor porque pode haver objetos que não fazem parte do dump que você está restaurando. neste caso, eles serão definitivamente mortos.
pstanton
7
um truque que me poupa tempo é $ sudo -u postgres dropdb DATABASE_NAME
Alvin
36
Mas ... e as permissões e propriedade do banco de dados?
Emanuele Paolini
6
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_namei adicionar os dois últimos por padrão para todos os bancos de dados
mcalex
91

Se você realmente não precisar de um backup do banco de dados despejado no disco em um formato de arquivo de script .sql em texto sem formatação, poderá conectar-se pg_dumpe pg_restorediretamente juntos por um canal.

Para eliminar e recriar tabelas, você pode usar a --cleanopção de linha de comando pg_dumppara emitir comandos SQL para limpar (descartar) objetos de banco de dados antes de (os comandos para) criá-los. (Isso não descarta o banco de dados inteiro, apenas cada tabela / sequência / índice / etc. Antes de recriá-los.)

Os dois acima ficariam assim:

pg_dump -U username --clean | pg_restore -U username
Bandido
fonte
1
Eu gosto desta solução, já que quero uma cópia de backup, agora estou fazendo o seguinte: 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 funciona como um encanto, obrigado por sua ajuda!
Hoff 27/01
38
Cuidado: a opção --clean remove apenas as relações encontradas no arquivo de restauração. Isso significa que, se você adicionar uma tabela para teste, deseja removê-la (para sincronizar com o banco de dados de produção, por exemplo), ela não será removida.
Ianaré 16/08/2012
6
É importante ter em mente que a opção --clean do pg_dump só funciona com backups de texto sem formatação. Como a documentação afirma claramente aqui postgresql.org/docs/9.4/static/app-pgdump.html , você precisa usar --clean em pg_restore para backups arquivados.
Kikin-Sama
6
Existe alguma maneira de incluir cascata na opção "--clean". Como é esta opção parece inútil. Estou recebendo "ERRO: não é possível excluir o esquema público porque outros objetos dependem dele", como 100% do tempo usando-o.
user4674453
A pergunta foi feita sobre a remoção de todas as tabelas. Isso remove apenas as tabelas encontradas no banco de dados do qual o pg_dump está sendo descarregado.
jbg
13

Embora a linha a seguir seja retirada de um script em lote do Windows, o comando deve ser bastante semelhante:

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

Este comando é usado para limpar o banco de dados inteiro, eliminando-o. O comando $DATABASE(no Windows deve ser %DATABASE%) é uma variável de ambiente no estilo do Windows que avalia o nome do banco de dados. Você precisará substituí-lo pelo seu development_db_name.

Frank Bollack
fonte
4
então por que não usar os comandos dropdbe já disponíveis createdb? Se você pode executar o psql, também pode executá-los.
Mike 'Pomax' Kamermans
10

Para despejar:

pg_dump -Fc mydb > db.dump

Restaurar:

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

Se você deseja limpar seu banco de dados chamado "example_db":

1) Faça login em outro banco de dados (por exemplo, 'postgres'):

psql postgres

2) Remova seu banco de dados:

DROP DATABASE example_db;

3) Recrie seu banco de dados:

CREATE DATABASE example_db;
Kamil Siwek
fonte
6

Eu usei:

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

Nota: minha resposta é sobre realmente excluir as tabelas e outros objetos de banco de dados; para apagar todos os dados em tabelas, ou seja truncar todas as tabelas , Endre Ambos forneceu uma declaração similarily bem executado (execução direta), um mês depois.

Para os casos em que você não pode simplesmente DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;ou algo assim, aqui está um script SQL independente que eu escrevi, que é seguro para transações (ou seja, você pode colocá-lo entre BEGIN;e ROLLBACK;apenas para testá-lo ou COMMIT;realmente fazer a ação) e limpa “todos” os objetos do banco de dados ... bem, todos aqueles usados ​​no banco de dados que nosso aplicativo usa ou eu poderia acrescentar sensatamente, que é:

  • gatilhos nas tabelas
  • restrições de tabela (FK, PK, CHECK, UNIQUE)
  • indicações
  • VIEWs (normal ou materializado)
  • mesas
  • sequências
  • rotinas (funções agregadas, funções, procedimentos)
  • todos os esquemas não padrão (por exemplo, não publicou internos ao banco de dados) "nós" possuímos: o script é útil quando executado como "não um superusuário de banco de dados"; um superusuário pode eliminar todos os esquemas (os realmente importantes ainda são explicitamente excluídos)
  • extensões (contribuídas pelo usuário, mas normalmente as deixo deliberadamente)

Não são descartados (alguns deliberados; outros apenas porque eu não tinha exemplo em nosso banco de dados):

  • o publicesquema (por exemplo, para itens fornecidos por extensão)
  • colações e outras coisas de localidade
  • gatilhos de evento
  • pesquisa de texto,… (veja aqui outras informações que eu poderia ter perdido)
  • funções ou outras configurações de segurança
  • tipos compostos
  • mesas de brinde
  • FDW e tabelas estrangeiras

Isso é realmente útil para os casos em que o dump que você deseja restaurar é de uma versão de esquema de banco de dados diferente (por exemplo, com Debian dbconfig-common, Flyway ou Liquibase / DB-Manul) do que o banco de dados no qual você deseja restaurá-lo.

Eu também tenho uma versão que exclui "tudo, exceto duas tabelas e o que lhes pertence" (uma sequência, testada manualmente, desculpe, eu sei, chata), caso alguém esteja interessado; o diff é pequeno. Entre em contato comigo ou consulte este repositório, se estiver interessado.

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

Testado, exceto adições posteriores ( extensionscontribuído por Clément Prévost ), no PostgreSQL 9.6 ( jessie-backports). Remoção agregada testada em 9.6 e 12.2, remoção de procedimento testada em 12.2 também. Correções de bugs e melhorias adicionais são bem-vindas!

mirabilos
fonte