Force drop db alors que d'autres peuvent être connectés

104

Je dois supprimer une base de données d'un cluster DB PostgreSQL. Comment puis-je le faire même s'il y a des connexions actives? J'ai besoin d'une sorte de -forcedrapeau qui supprime toutes les connexions, puis la base de données.

Comment puis-je le mettre en œuvre?

J'utilise dropdbactuellement, mais d'autres outils sont possibles.

Alex
la source

Réponses:

155

Dans PostgreSQL * , vous ne pouvez pas supprimer une base de données tant que des clients y sont connectés.

Du moins, pas avec l' dropdbutilitaire - qui n'est qu'un simple wrapper autour d'une DROP DATABASErequête de serveur.

La solution de contournement assez robuste suit:

Connectez-vous à votre serveur en tant que superutilisateur , utilisateur psqlou autre client. N'utilisez pas la base de données que vous souhaitez supprimer.

psql -h localhost postgres postgres

Maintenant, en utilisant un client de base de données simple, vous pouvez forcer le retrait de la base de données en trois étapes simples:

  1. Assurez-vous que personne ne peut se connecter à cette base de données. Vous pouvez utiliser l’une des méthodes suivantes (la seconde semble plus sûre, mais n’empêche pas les connexions des superutilisateurs).

    /* Method 1: update system catalog */
    UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'mydb';
    
    /* Method 2: use ALTER DATABASE. Superusers still can connect!
    ALTER DATABASE mydb CONNECTION LIMIT 0; */
    
  2. Forcer la déconnexion de tous les clients connectés à cette base de données à l'aide de pg_terminate_backend.

    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE datname = 'mydb';
    
    /* For old versions of PostgreSQL (up to 9.1), change pid to procpid:
    
    SELECT pg_terminate_backend(procpid)
    FROM pg_stat_activity
    WHERE datname = 'mydb'; */
    
  3. Supprimer la base de données.

    DROP DATABASE mydb;

L'étape 1 requiert les privilèges superutilisateur pour la 1re méthode et les privilèges de propriétaire de base de données pour la 2e. L'étape 2 nécessite les privilèges de superutilisateur . L'étape 3 nécessite le privilège du propriétaire de la base de données .


* Ceci s'applique à toutes les versions de PostgreSQL, jusqu'à la version 11.


filiprem
la source
Donc, je ne sais pas ce que j'ai fait de mal, mais maintenant je ne peux même pas me connecter à la base de données que j'ai ciblée! Je ne peux pas non plus le laisser tomber car il est indiqué "La base de données de maintenance ne peut pas être supprimée"
Matt Skeldon le
@MattSkeldon, aucune idée de ce que ce message signifie. Dans Vanilla PostgreSQL, vous pouvez supprimer n'importe quelle base de données à l'exception de template0 et template1. Peut-être que vous utilisez une version non-libre / commerciale? Peut-être que c'est le problème du client pas le problème du serveur? Avez-vous essayé psql?
Filiprem
Malheureusement, je viens d'un environnement SQL, PGSQL est utilisé en raison de son statut non commercial / libre.
Matt Skeldon
Cela ne fonctionne pas pour moi lorsqu'il existe de longues sessions de zombies. pg_terminate_backend () ne tue pas ces sessions, je suis donc un peu coincé sur ce qu'il faut faire: je suis un subrogé, mais je n'ai pas accès au serveur sur lequel il tourne.
Alexander
6

Il existe un moyen de faire cela avec les utilitaires shell dropdb& pg_ctl(ou pg_ctlclusterdans Debian et ses dérivés). Mais la méthode de @ filiprem est supérieure pour plusieurs raisons:

  • Il ne déconnecte que les utilisateurs de la base de données en question.
  • Il n'est pas nécessaire de redémarrer l'ensemble du cluster.
  • Il empêche les reconnexions immédiates, voire l’annulation de la dropdbcommande.

Je cite man pg_ctlcluster:

Avec l' --forceoption, on utilise le mode "rapide" qui annule toutes les transactions actives, déconnecte immédiatement les clients et ferme ainsi proprement. Si cela ne fonctionne pas, une tentative d'arrêt est à nouveau effectuée en mode "immédiat", ce qui peut laisser le cluster dans un état incohérent et entraîner ainsi une reprise au prochain démarrage. Si cela ne résout pas le problème, le processus postmaster est tué. Quitte avec 0 en cas de succès, avec 2 si le serveur n'est pas en cours d'exécution et avec 1 en cas de défaillance. Ce mode ne doit être utilisé que lorsque la machine est sur le point d'être arrêtée.

pg_ctlcluster 9.1 main restart --force

ou

pg_ctl restart -D datadir -m fast

ou

pg_ctl restart -D datadir -m immediate

immédiatement suivi de:

dropdb mydb

Peut-être dans un script pour succession immédiate.

Erwin Brandstetter
la source
4
Non seulement cela est moins qu'idéal car cela déclenche l'instance complète de postgres, mais il n'est pas garanti que cela fonctionne. Il est possible pour un client de se connecter entre le moment où vous redémarrez le serveur et la tentative d'exécution de dropdb à nouveau. La réponse de @filiprem ci-dessus désactive toutes les connexions à la base de données avant la déconnexion et conserve les autres bases de données.
Jim Mitchener
6

Utiliser la réponse de @ filiprem dans mon cas et le simplifier:

-- Connecting to the current user localhost's postgres instance
psql

-- Making sure the database exists
SELECT * from pg_database where datname = 'my_database_name'

-- Disallow new connections
UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'my_database_name';
ALTER DATABASE my_database_name CONNECTION LIMIT 1;

-- Terminate existing connections
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'my_database_name';

-- Drop database
DROP DATABASE my_database_name
Dorian
la source
0

Si vous êtes sur quelque chose comme RDS où les connexions sans base de données sélectionnée vous mettent dans la base de données qui vous a été demandée créée par défaut, vous pouvez utiliser cette variante pour vous éviter d'être la dernière connexion ouverte.

 DROP DATABASE IF EXISTS temporary_db_that_shouldnt_exist; 

 CREATE DATABASE temporary_db_that_shouldnt_exist with OWNER your_user; 

 \connect temporary_db_that_shouldnt_exist 
 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'the_db_you_want_removed'; 


 DROP DATABASE IF EXISTS the_db_you_want_removed; 
 -- 
 -- Name: the_db_you_want_removed; Type: DATABASE; Schema: -; Owner: your_user 
 -- 

 CREATE DATABASE savings_champion WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; 


 ALTER DATABASE the_db_you_want_removed OWNER TO your_user; 

 \connect the_db_you_want_removed 

 DROP DATABASE IF EXISTS temporary_db_that_shouldnt_exist;
Jharwood
la source