Postgres: borre toda la base de datos antes de volver a crear / volver a llenar desde el script bash

139

Estoy escribiendo un script de shell (se convertirá en un cronjob) que:

1: volcar mi base de datos de producción

2: importar el volcado a mi base de datos de desarrollo

Entre los pasos 1 y 2, necesito borrar la base de datos de desarrollo (¿eliminar todas las tablas?). ¿Cómo se logra esto mejor desde un script de shell? Hasta ahora, se ve así:

#!/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
fuente
3
línea para personas apuradas:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
ruuter
esta línea requiere que tenga permisos para crear / descartar la base de datos. El enfoque que el autor está intentando no requiere privilegios especiales.
ribamar

Respuestas:

188

Simplemente soltaría la base de datos y luego la volvería a crear. En un sistema UNIX o Linux, eso debería hacerlo:

$ dropdb development_db_name
$ createdb developmnent_db_name

Así es como lo hago, en realidad.

Haes
fuente
Así es como lo hago también. Luego solo restaure en el db recién creado.
Arthur Thomas el
3
Sí. esto es mejor porque puede haber objetos que no forman parte del volcado que está restaurando. en este caso definitivamente serán asesinados.
pstanton
77
un truco que me ahorra tiempo es $ sudo -u postgres dropdb DATABASE_NAME
Alvin
36
Pero ... ¿qué pasa con los permisos y la propiedad de la base de datos?
Emanuele Paolini
66
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_nameagrego los dos últimos por defecto a todas las bases de datos
mcalex
91

Si realmente no necesita una copia de seguridad de la base de datos volcada en el disco en un formato de archivo de script .sql de texto plano, puede conectarse pg_dumpy conectarse pg_restoredirectamente a través de una tubería.

Para soltar y volver a crear tablas, puede usar la --cleanopción de línea de pg_dumpcomandos para emitir comandos SQL para limpiar (soltar) los objetos de la base de datos antes de (los comandos para) crearlos. (Esto no eliminará toda la base de datos, solo cada tabla / secuencia / índice / etc. antes de volver a crearlos).

Los dos anteriores se verían así:

pg_dump -U username --clean | pg_restore -U username
Bandido
fuente
1
Me gusta esta solución, ya que quiero una copia de seguridad, ahora estoy haciendo esto: 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 de maravilla, ¡gracias por tu ayuda!
Hoff
38
Cuidado: la opción --clean solo elimina las relaciones encontradas en el archivo de restauración. Esto significa que si agrega una tabla para probar, luego desea eliminarla (para sincronizar con la base de datos de producción, por ejemplo), no se eliminará.
ianaré
66
Es importante tener en cuenta que la opción --clean de pg_dump solo funciona con copias de seguridad de texto sin formato. Como la documentación claramente indica aquí postgresql.org/docs/9.4/static/app-pgdump.html , debe usar --clean en pg_restore para las copias de seguridad archivadas.
Kikin-Sama
66
¿Hay alguna manera de incluir cascada en la opción "--clean"? Como es, esta opción parece inútil. Recibo "ERROR: no se puede colocar el esquema público porque otros objetos dependen de él" como el 100% de las veces que lo uso.
user4674453
La pregunta fue sobre la eliminación de todas las tablas. Esto solo elimina las tablas encontradas en la base de datos de las cuales pg_dump está volcando.
jbg
13

Aunque la siguiente línea se toma de un script por lotes de Windows, el comando debería ser bastante similar:

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

Este comando se utiliza para borrar toda la base de datos, dejándola caer. El $DATABASE(en Windows debería estar %DATABASE%) en el comando es una variable de entorno de estilo Windows que se evalúa como el nombre de la base de datos. Tendrá que sustituir eso por su development_db_name.

Frank Bollack
fuente
44
entonces, ¿por qué no usar los comandos dropdby los ya disponibles createdb? Si puede ejecutar psql, también puede ejecutarlos.
Mike 'Pomax' Kamermans
10

Para volcar:

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
fuente
7

Si desea limpiar su base de datos llamada "example_db":

1) Inicie sesión en otra base de datos (por ejemplo, 'postgres'):

psql postgres

2) Eliminar su base de datos:

DROP DATABASE example_db;

3) Recrea tu base de datos:

CREATE DATABASE example_db;
Kamil Siwek
fuente
6

He usado:

pg_restore -c -d database_name filename.dump
Troya
fuente
4

Nota: mi respuesta es sobre eliminar realmente las tablas y otros objetos de la base de datos; para eliminar todos los datos en las tablas, es decir, truncar todas las tablas , Endre Both ha proporcionado una declaración similarmente bien ejecutada (ejecución directa) un mes después.

Para los casos en los que no puede simplemente DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;o algo así, aquí hay un script SQL independiente que escribí, que es seguro para las transacciones (es decir, puede ponerlo entre BEGIN;y ROLLBACK;para probarlo o COMMIT;hacer el acto) limpia "todos" los objetos de la base de datos ... bueno, todos los que se usan en la base de datos que usa nuestra aplicación o podría agregar sensatamente, que es:

  • disparadores en las mesas
  • restricciones en las tablas (FK, PK, CHECK, UNIQUE)
  • índices
  • VIEWs (normal o materializado)
  • mesas
  • secuencias
  • rutinas (funciones agregadas, funciones, procedimientos)
  • todos publiclos esquemas no predeterminados (es decir, no o internos de la base de datos) que "nosotros" poseemos: el script es útil cuando se ejecuta como "no un superusuario de base de datos"; un superusuario puede soltar todo esquemas (sin embargo, los realmente importantes aún se excluyen explícitamente)
  • extensiones (aportadas por el usuario pero normalmente las dejo deliberadamente)

No se descartan (algunos deliberados; otros solo porque no tenía ningún ejemplo en nuestra base de datos):

  • el publicesquema (p. ej. para cosas proporcionadas por la extensión en ellos)
  • intercalaciones y otras cosas locales
  • disparadores de eventos
  • cosas de búsqueda de texto, ... (vea aquí para otras cosas que podría haber perdido)
  • roles u otras configuraciones de seguridad
  • tipos compuestos
  • mesas tostadas
  • FDW y tablas extranjeras

Esto es realmente útil para los casos en que el volcado que desea restaurar es de una versión de esquema de base de datos diferente (por ejemplo, con Debian dbconfig-common, Flyway o Liquibase / DB-Manul) que la base de datos en la que desea restaurarlo.

También tengo una versión que elimina "todo excepto dos tablas y lo que les pertenece" (una secuencia, probada manualmente, lo siento, lo sé, aburrida) en caso de que alguien esté interesado; La diferencia es pequeña. Contáctame o consulta este repositorio si interesado.

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

Probado, excepto adiciones posteriores ( extensionscontribuido por Clément Prévost ), en PostgreSQL 9.6 ( jessie-backports). Eliminación agregada probada en 9.6 y 12.2, eliminación de procedimiento probada también en 12.2. ¡Correcciones de errores y mejoras adicionales son bienvenidas!

mirabilos
fuente