I need to write a script that will drop a PostgreSQL database. There may be a lot of connections to it, but the script should ignore that.
The standard DROP DATABASE db_name query doesn't work when there are open connections.
How can I solve the problem?
This will drop existing connections except for yours:
Query pg_stat_activity and get the pid values you want to kill, then issue SELECT pg_terminate_backend(pid int) to them.
PostgreSQL 9.2 and above:
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 and below:
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();
Once you disconnect everyone you will have to disconnect and issue the DROP DATABASE command from a connection from another database aka not the one your trying to drop.
Note the renaming of the procpid column to pid. See this mailing list thread.
In PostgreSQL 9.2 and above, to disconnect everything except your session from the database you are connected to:
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()
AND pid <> pg_backend_pid();
In older versions it's the same, just change pid to procpid. To disconnect from a different database just change current_database() to the name of the database you want to disconnect users from.
You may want to REVOKE the CONNECT right from users of the database before disconnecting users, otherwise users will just keep on reconnecting and you'll never get the chance to drop the DB. See this comment and the question it's associated with, How do I detach all other users from the database.
If you just want to disconnect idle users, see this question.
PostgreSQL 13 introduced FORCE option.
DROP DATABASE
DROP DATABASE drops a database ... Also, if anyone else is connected to the target database, this command will fail unless you use the FORCE option described below.
FORCE
Attempt to terminate all existing connections to the target database. It doesn't terminate if prepared transactions, active logical replication slots or subscriptions are present in the target database.
DROP DATABASE db_name WITH (FORCE);
Easy Peasy.
I just restart the service in Ubuntu to disconnect connected clients.
sudo service postgresql stop
sudo service postgresql start
psql
DROP DATABASE DB_NAME;
You could kill all connections before dropping the database using the pg_terminate_backend(int) function.
You can get all running backends using the system view pg_stat_activity
I'm not entirely sure, but the following would probably kill all sessions:
select pg_terminate_backend(procpid)
from pg_stat_activity
where datname = 'doomed_database'
Of course you may not be connected yourself to that database
Depending on your version of postgresql you might run into a bug, that makes pg_stat_activity to omit active connections from dropped users. These connections are also not shown inside pgAdminIII.
If you are doing automatic testing (in which you also create users) this might be a probable scenario.
In this case you need to revert to queries like:
SELECT pg_terminate_backend(procpid)
FROM pg_stat_get_activity(NULL::integer)
WHERE datid=(SELECT oid from pg_database where datname = 'your_database');
NOTE: In 9.2+ you'll have change procpid to pid.
I noticed that postgres 9.2 now calls the column pid rather than procpid.
I tend to call it from the 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
Hope that is helpful. Thanks to #JustBob for the sql.
PostgreSQL 9.2 and above:
SELECT pg_terminate_backend(pid)FROM pg_stat_activity WHERE datname = 'YOUR_DATABASE_NAME_HERE'
Here's my 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>;"
I put this answer because include a command (above) to block new connections and because any attempt with the command...
REVOKE CONNECT ON DATABASE <DATABASE_NAME> FROM PUBLIC, <USERS_ETC>;
... do not works to block new connections!
Thanks to #araqnid #GoatWalker ! =D
https://stackoverflow.com/a/3185413/3223785
In Linux command Prompt, I would first stop all postgresql processes that are running by tying this command
sudo /etc/init.d/postgresql restart
type the command
bg to check if other postgresql processes are still running
then followed by dropdb dbname to drop the database
sudo /etc/init.d/postgresql restart
bg
dropdb dbname
This works for me on linux command prompt
In my case i had to execute a command to drop all connections including my active administrator connection
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()
which terminated all connections and show me a fatal ''error'' message :
FATAL: terminating connection due to administrator command SQL state: 57P01
After that it was possible to drop the database
Nothing worked for me except, I loggined using pgAdmin4 and on the Dashboard I disconnected all connections except pgAdmin4 and then was able to rename by right lick on the database and properties and typed new name.
Related
I'm a bit confused about why I can't drop my database, so after connecting to my rds postgres instance with
psql --host=mu_user.amazonaws.com --port=5432 --username=my_user --password --dbname=postgres
I've REVOKED new connection to the db I want to drop with
REVOKE CONNECT ON DATABASE mydb FROM public;
then I have terminated all connection with
SELECT pid, pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'mydb' AND pid <> pg_backend_pid();
after that when I want to drop the db I still can't because It will say
ERROR: database "mydb" is being accessed by other users
DETAIL: There are 10 other sessions using the database.
If I inspect live connections with
SELECT
pid
,datname
,usename
,application_name
,client_hostname
,client_port
,backend_start
,query_start
,query
,state
FROM pg_stat_activity
WHERE state = 'active';
there will be none, but if I change active to idle I can see a bunch, and after trying to kill them with pg_terminate_backend(pid) I again can't drop the db and I again have the same ERROR, so can someone please help me understand what I'm I doing wrong here?
Those connections which are idle and always appearing are by my_user, who is also a superuser.
The problem is that those other connections are by a superuser, and superusers are exempt from permission checks, so revoking the CONNECT privilege on the database won't keep these guys out.
You could either block the connections via pg_hba.conf, or you can run both statements immediately after each other:
SELECT pg_terminate_backend(pid) FROM pg_stat_activity; DROP DATABASE mydb;
in the hope that the users won't have time to reconnect before the DROP DATABASE hits.
I am trying to drop a database using the following command and I get an error
DROP DATABASE IF EXISTS mydb;
There are 5 other sessions using the database.
Is there any sql statement/sequence of queries which which drops database killing all the open sessions? I can only use commnand line.
I tried SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'mydb'; and also tried restarting postgres service which ideally should have worked but it did not.
OK the followoing command worked for me great
SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'mydb' AND pid <> pg_backend_pid();
I'm trying to drop the database I'm currently connected to like so, but I'm getting this error:
pq: cannot drop the currently open database
I don't really understand how I'm expected to drop the database if I have to close my connection, because then I don't think I will be able to use dbConn.Exec to execute my DROP DATABASE statement?
dbConn *sql.DB
func stuff() error {
_, err := dbConn.Exec(fmt.Sprintf(`DROP DATABASE %s;`, dbName))
if err != nil {
return err
}
return dbConn.Close()
}
I guess I could connect to a different database and then execute it on that connection, but I'm not even sure if that'd work, and it seems really weird to have to connect to a new database just to drop a different database. Any ideas? Thanks.
Because, you are trying to execute dropDb command on database, to which you have open connection.
According to postgres documentation:
You cannot be connected to the database you are about to remove. Instead, connect to template1 or any other database and run this command again.
This makes sense, because when you drop the entire database, all the open connection referencing to that database becomes invalid, So the recommended approach is to connect to different database, and execute this command again.
If you are facing a situation, where a different client is connected to the database, and you really want to drop the database, you can forcibly disconnect all the client from that particular database.
For example, to forcibly disconnect all clients from database mydb:
If PostgreSQL < 9.2
SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = 'mydb';
Else
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'mydb';
Note: This command requires superuser privileges.
Then, you can connect to different database, and run dropDb command again.
If you encounter this problem in IntelliJ, change the schema with the following dropdown to postgres.
After that, I was able to drop a db.
I am using PostgreSQL 12 and pgAdmin-4 in Windows 10. I had to use a combination of the above answers to drop a database, which I could not drop in pgAdmin because I was unable to close all open connections in pgAdmin.
Close pgAdmin-4.
In Windows command line, assuming my server's name is postgres and my database is mydb:
C:\> psql -U postgres
I logged in with my server password.
I then closed all open connections to mydb:
postgres-# SELECT * FROM pg_stat_activity WHERE pg_stat_activity.datname='mydb';
postgres-# SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'mydb';
Finally, I successfully dropped mydb:
postgres-# DROP DATABASE mydb;
Now if I go back into pgAdmin-4 it is gone.
To drop the database:
\c postgres
Then
DROP DATABASE your_database works
It's simple, just connect to another database \c database2. Once connected execute the drop database command while connected to the other database.
Just Connect to a different database using \c db_name;
and then drop the required database using drop database db_name;
If you are using DBeaver make sure you are not connected to the database you are trying to drop. Go to edit connections and look at the database name.
Switch the connection to a different database and then drop the database you wish.
None of this worked for me since I tried to do it through pgAdmin which kept database connections open as soon as I delete them.
Solution:
C:\Program Files\PostgreSQL\11\scripts\runpsql.bat
after you supply correct information you will be able to get pg command prompt, here you can just type:
dbdrop yourdatabase
After that you might still see database in pgAdmin but now you can just simply delete it with right click and DELETE/DROP option.
you can force to drop database with: DROP DATABASE mydb WITH (FORCE)
I am looking for a basic script/command that will create a copy of a live database (let name them mydb and mydb_test, both on the same server).
Requirements
it has to run even if the mydb_test already exists and have records
it has to work even if mydb and mydb_test do have existing connections
it have to clean the potentially existing database if necessary
Hints:
drop database cannot be used if you have existing connections
The simplest and fastest method to create a complete copy of an existing (live) database is to use CREATE DATABASE with a TEMPLATE:
CREATE DATABASE mydb_test TEMPLATE mydb;
However, there is an important limitation violating your second requirement: the template (source) database cannot have additional connections to it. I quote the manual:
It is possible to create additional template databases, and indeed one
can copy any database in a cluster by specifying its name as the
template for CREATE DATABASE. It is important to understand, however,
that this is not (yet) intended as a general-purpose "COPY DATABASE"
facility. The principal limitation is that no other sessions can be
connected to the source database while it is being copied. CREATE DATABASE
will fail if any other connection exists when it starts; during
the copy operation, new connections to the source database are prevented.
You can terminate all sessions to the template database if you have the necessary privileges with pg_terminate_backend().
To temporarily disallow reconnects, revoke the CONNECT privilege (and GRANT back later).
REVOKE CONNECT ON DATABASE mydb FROM PUBLIC;
-- while connected to another DB - like the default maintenance DB "postgres"
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'mydb' -- name of prospective template db
AND pid <> pg_backend_pid(); -- don't kill your own session
CREATE DATABASE mydb_test TEMPLATE mydb;
GRANT CONNECT ON DATABASE mydb TO PUBLIC; -- only if they had it before
In versions before Postgres 9.2 use procpid instead of pid:
How to drop a PostgreSQL database if there are active connections to it?
Related:
Force drop db while others may be connected
If you cannot afford to terminate concurrent sessions, go with piping the output of pg_dump to psql like has been suggested by other answers already.
That's what I was looking for, but I had to compile it myself :P
I only wish I knew a way to keep the same user and not having to put it inside the script.
#!/bin/bash
DB_SRC=conf
DB_DST=conf_test
DB_OWNER=confuser
T="$(date +%s)"
psql -c "select pg_terminate_backend(procpid) from pg_stat_activity where datname='$DB_DST';" || { echo "disconnect users failed"; exit 1; }
psql -c "drop database if exists $DB_DST;" || { echo "drop failed"; exit 1; }
psql -c "create database $DB_DST owner confuser;" || { echo "create failed"; exit 1; }
pg_dump $DB_SRC|psql $DB_DST || { echo "dump/restore failed"; exit 1; }
T="$(($(date +%s)-T))"
echo "Time in seconds: ${T}"
Since you didn't say it was a problem to drop objects in the database, I think running pg_dump with the --clean option will do what you want. You can pipe the output of pg_dump into psql for this sort of thing.
Are you looking into the Hot Standby with Streaming Replication here?
I have a script in PostgreSQL which restores test database from dump every night. The database is accessed by app servers and processes with connection pool which keeps a few connections alive at all times.
So the script restores dump into my_temp_database. Then it should rename my_database to my_old_database, my_temp_database to my_database, and eventually drop my_old_database.
How can I disconnect all clients, superuser or not, from my_database, so that it can be renamed? How I can I temporarily prevent them from reconnecting?
Is there a better way to do what I need?
To mark database 'applogs' as not accepting new connections:
update pg_database set datallowconn = false where datname = 'applogs';
Another possibility would be to revoke 'connect' access on the database for the client role(s).
Disconnect users from database = kill backend. So to disconnect all other users from "applogs" database, for example:
select pg_terminate_backend(procpid)
from pg_stat_activity
where datname = 'applogs' and procpid <> pg_backend_pid();
Once you've done both of those, you are the only user connected to 'applogs'. Although there might actually be a delay before the backends actually finish disconnecting?
After you have terminated the current active connections you can also issue this command that will only allow super users to login. This assumes you are ok with all super users having access still. Hopefully you don't hand out super user rights to just anyone.
ALTER DATABASE your_db CONNECTION LIMIT 0;
Starting PostgreSQL 9.5 we finally can:
ALTER DATABASE db WITH ALLOW_CONNECTIONS false;
I had a different usage scenario, in which I wanted to disable a DB for everyone (including superuser) and forever, but not plainly drop it just yet, to be able to reactivate it quickly if needed.
This worked fine on an old 8.3 Postgres:
UPDATE pg_database SET datallowconn=false WHERE datname='my_db_name';
If you are connected in session with the DB that you want to dissallow connecting to, and to stay connected in the same session after you disallow connections, then use this :
UPDATE pg_database SET datallowconn = false WHERE datname = '_db_name_' ;
... which allows you to do stuff without any other connection happening until you re-enable
But if you use this, it must be from in a session in another DB :
ALTER DATABASE _db_name_ WITH ALLOW_CONNECTIONS false ;
(( i realise this is almost a summary of the answers above ))