Comment supprimer une base de données PostgreSQL s'il y a des connexions actives?

650

J'ai besoin d'écrire un script qui supprimera une base de données PostgreSQL. Il peut y avoir beaucoup de connexions, mais le script doit ignorer cela.

La DROP DATABASE db_namerequête standard ne fonctionne pas lorsqu'il existe des connexions ouvertes.

Comment puis-je résoudre le problème?

Roman Prykhodchenko
la source
1
Quelle version de PostgreSQL utilisez-vous?
Kuberchaun
1
Problème: bien que vous puissiez tuer les sessions connectées à la base de données, elles peuvent se reconnecter si rapidement que vous ne pouvez toujours pas supprimer la base de données. Heureusement, cet article montre comment verrouiller de nouvelles connexions, vous pouvez donc tuer les connexions actuelles et supprimer la base de données selon le plan: dba.stackexchange.com/questions/11893/…
Max Murphy
1
J'ai trouvé cette réponse sur dba.stackexchange très utile dba.stackexchange.com/a/11895/163539 - succincte mais suffisamment explicative.
hlongmore

Réponses:

1095

Cela supprimera les connexions existantes, sauf la vôtre:

Recherchez pg_stat_activityet obtenez les valeurs pid que vous voulez tuer, puis entrez- SELECT pg_terminate_backend(pid int)les.

PostgreSQL 9.2 et supérieur:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← change this to your DB
  AND pid <> pg_backend_pid();

PostgreSQL 9.1 et versions antérieures:

SELECT pg_terminate_backend(pg_stat_activity.procpid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← change this to your DB
  AND procpid <> pg_backend_pid();

Une fois que vous avez déconnecté tout le monde, vous devrez vous déconnecter et émettre la commande DROP DATABASE à partir d'une connexion d'une autre base de données aka pas celle que vous essayez de supprimer.

Notez le changement de nom de la procpidcolonne en pid. Voir ce fil de liste de diffusion .

Kuberchaun
la source
11
Et bien sûr, assurez-vous de le faire à partir d'une connexion db qui n'est pas une connexion à 'TARGET_DB', sinon vous obtenez 'ERROR'. Une connexion «postgres» fonctionne bien.
Rob
3
En fait, cela déconnecterait les clients un par un, et si votre client se trouve au milieu de la liste, il sera également déconnecté. En conséquence, certaines connexions resteront en vie. Donc, la bonne réponse est de Craig Ringer (voir ci-dessous). SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = current_database () AND pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov
1
Comment puis-je déconnecter les connexions une fois leur transaction en cours terminée, puis supprimer la ou les tables en question?
paulkon
5
Dans mon cas, les clients se reconnecteraient rapidement, donc mettre cela juste avant ; drop database TARGET_DB;a bien fonctionné dans mon cas pour m'assurer que la base de données avait disparu au moment où les choses ont recommencé.
Mat Schaffer
1
Je paierais même de l'argent pour un dropdb --force.
Torsten Bronger
125

Dans PostgreSQL 9.2 et supérieur, pour tout déconnecter sauf votre session de la base de données à laquelle vous êtes connecté:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()
  AND pid <> pg_backend_pid();

Dans les anciennes versions, c'est la même chose, passez simplement pidà procpid. Pour vous déconnecter d'une autre base de données, changez simplement current_database()le nom de la base de données dont vous souhaitez déconnecter les utilisateurs.

Vous pouvez REVOKEle CONNECTdroit des utilisateurs de la base de données avant de déconnecter les utilisateurs, sinon les utilisateurs simplement continuer à vous reconnecter et vous ne serez jamais la chance de laisser tomber le DB. Voir ce commentaire et la question à laquelle il est associé, comment détacher tous les autres utilisateurs de la base de données .

Si vous souhaitez simplement déconnecter les utilisateurs inactifs, consultez cette question .

Craig Ringer
la source
3
SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = current_database () AND pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov
26

Vous pouvez tuer toutes les connexions avant de supprimer la base de données à l'aide de la pg_terminate_backend(int)fonction.

Vous pouvez obtenir tous les backends en cours d'exécution en utilisant la vue système pg_stat_activity

Je ne suis pas entièrement sûr, mais ce qui suit tuerait probablement toutes les sessions:

select pg_terminate_backend(procpid)
from pg_stat_activity
where datname = 'doomed_database'

Bien sûr, vous ne pouvez pas vous connecter à cette base de données

un cheval sans nom
la source
19

En fonction de votre version de postgresql, vous pourriez rencontrer un bogue, ce qui fait que pg_stat_activityles connexions actives des utilisateurs abandonnés sont omises. Ces connexions ne sont pas non plus affichées dans pgAdminIII.

Si vous effectuez des tests automatiques (dans lesquels vous créez également des utilisateurs), cela peut être un scénario probable.

Dans ce cas, vous devez revenir à des requêtes telles que:

 SELECT pg_terminate_backend(procpid) 
 FROM pg_stat_get_activity(NULL::integer) 
 WHERE datid=(SELECT oid from pg_database where datname = 'your_database');

REMARQUE: dans 9.2+, vous devrez passer procpidà pid.

jb.
la source
1
C'est ce que je cherchais mais (en supposant 9.2 et au-delà), vous devez supprimer la référence à pg_stat_activity et changer procpid en pid.
MDR
2
Après avoir changé procpidpour pidcet extrait, fonctionne sur 9.3.
jb.
même sans supprimer pg_stat_activity? J'obtenais une erreur le 9.2
MDR
D'ACCORD. Maintenant je comprends, c'était une faute de frappe. Merci!
jb.
2
À partir de 9.3 et plus SELECT pg_terminate_backend (pid) FROM pg_stat_get_activity (NULL :: integer) WHERE datid = (SELECT oid from pg_database where datname = 'your_database');
Shawn Vader
17

J'ai remarqué que postgres 9.2 appelle maintenant la colonne pid plutôt que procpid.

J'ai tendance à l'appeler depuis le shell:

#!/usr/bin/env bash
# kill all connections to the postgres server
if [ -n "$1" ] ; then
  where="where pg_stat_activity.datname = '$1'"
  echo "killing all connections to database '$1'"
else
  echo "killing all connections to database"
fi

cat <<-EOF | psql -U postgres -d postgres 
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
${where}
EOF

J'espère que cela vous sera utile. Merci à @JustBob pour le sql.

kbrock
la source
15

Je viens de redémarrer le service dans Ubuntu pour déconnecter les clients connectés.

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;
devdrc
la source
10

Dans l'invite de commande Linux, j'arrêterais d'abord tous les processus postgresql en cours d'exécution en liant cette commande sudo /etc/init.d/postgresql restart

tapez la commande bg pour vérifier si d'autres processus postgresql sont toujours en cours d'exécution

puis suivi de dropdb dbname pour supprimer la base de données

sudo /etc/init.d/postgresql restart
bg
dropdb dbname

Cela fonctionne pour moi sur l'invite de commande Linux

Maurice Elagu
la source
6
Ce n'est pas bon si vous avez de nombreuses bases de données et que vous souhaitez uniquement supprimer les connexions pour une seule base de données. Cela tuerait toutes les connexions. C'est un peu "sledge hammer-y".
Nick
2
@Nick true mais rappelez-vous que nous redémarrons toutes les connexions et les
arrêtons
10

PostgreSQL 9.2 et supérieur:

SELECT pg_terminate_backend(pid)FROM pg_stat_activity WHERE datname = 'YOUR_DATABASE_NAME_HERE'

Marcelo C.
la source
Cela ne mettra-t-il pas fin également à la connexion active?
Cocowalla
8

Voici mon hack ... = D

# Make sure no one can connect to this database except you!
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "UPDATE pg_database SET datallowconn=false WHERE datname='<DATABASE_NAME>';"

# Drop all existing connections except for yours!
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '<DATABASE_NAME>' AND pid <> pg_backend_pid();"

# Drop database! =D
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "DROP DATABASE <DATABASE_NAME>;"

Je mets cette réponse car inclure une commande (ci-dessus) pour bloquer les nouvelles connexions et parce que toute tentative avec la commande ...

REVOKE CONNECT ON DATABASE <DATABASE_NAME> FROM PUBLIC, <USERS_ETC>;

... ne fonctionne pas pour bloquer de nouvelles connexions!

Merci à @araqnid @GoatWalker! = D

https://stackoverflow.com/a/3185413/3223785

Eduardo Lucio
la source
5

PostgreSQL 13 à venir présentera une FORCEoption.

DROP DATABASE

DROP DATABASE supprime une base de données ... De plus, si quelqu'un d'autre est connecté à la base de données cible, cette commande échouera sauf si vous utilisez l' option FORCE décrite ci-dessous.

OBLIGER

Essayez de mettre fin à toutes les connexions existantes à la base de données cible. Il ne se termine pas si des transactions préparées, des emplacements de réplication logique actifs ou des abonnements sont présents dans la base de données cible.

DROP DATABASE db_name WITH (FORCE);
Lukasz Szozda
la source
0

Dans mon cas, j'ai dû exécuter une commande pour supprimer toutes les connexions, y compris ma connexion administrateur active

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()

qui a mis fin à toutes les connexions et m'affiche un message '' erreur '' fatal:

FATAL: terminating connection due to administrator command SQL state: 57P01

Après cela, il a été possible de supprimer la base de données

Chtiwi Malek
la source
0

Rien n'a fonctionné pour moi sauf, j'ai ouvert une session à l'aide de pgAdmin4 et sur le tableau de bord, j'ai déconnecté toutes les connexions, sauf pgAdmin4, puis j'ai pu renommer par un clic droit sur la base de données et les propriétés et tapé un nouveau nom.

Ashburn RK
la source