How to clone a test database from a production one in one single action? - postgresql

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?

Related

Deleting a database in Postgres with DBeaver [duplicate]

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.

Duplicating an entire RDS instance PostgreSQL DB to another DB within the same instance [duplicate]

Is there a simple way to create a copy of a database or schema in PostgreSQL 8.1?
I'm testing some software which does a lot of updates to a particular schema within a database, and I'd like to make a copy of it so I can run some comparisons against the original.
If it's on the same server, you just use the CREATE DATABASE command with the TEMPLATE parameter. For example:
CREATE DATABASE newdb WITH TEMPLATE olddb;
pg_dump with the --schema-only option.
If you have to copy the schema from the local database to a remote database, you may use one of the following two options.
Option A
Copy the schema from the local database to a dump file.
pg_dump -U postgres -Cs database > dump_file
Copy the dump file from the local server to the remote server.
scp localuser#localhost:dump_file remoteuser#remotehost:dump_file
Connect to the remote server.
ssh remoteuser#remotehost
Copy the schema from the dump file to the remote database.
psql -U postgres database < dump_file
Option B
Copy the schema directly from the local database to the remote database without using an intermediate file.
pg_dump -h localhost -U postgres -Cs database | psql -h remotehost -U postgres database
This blog post might prove helpful for you if you want to learn more about options for copying the database using pg_dump.
This can be done by running the following command:
CREATE DATABASE [Database to create] WITH TEMPLATE [Database to copy] OWNER [Your username];
Once filled in with your database names and your username, this will create a copy of the specified database. This will work as long as there are no other active connections to the database you wish to copy. If there are other active connections you can temporarily terminate the connections by using this command first:
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = '[Database to copy]'
AND pid <> pg_backend_pid();
A good article that I wrote for Chartio's Data School which goes a bit more in depth on how to do this can be found here:
https://dataschool.com/learn/how-to-create-a-copy-of-a-database-in-postgresql-using-psql

PostgreSQL 13: create empty copy of database

I have a AWS RDS PostgreSQL 13 server with some databases. I have to create an empty copy of one database (empty means schema (tables, views, functions) + security (users, roles)).
Is pg_dump -s what I am looking for?
Thanks!
pg_dump -d db_name -s. You will also need to do pg_dumpall -g to get the global data e.g. roles. This will get all global data for the Postgres cluster, so you may have more then you need for the particular database.
Postgres allows the use of any existing database on the server as a template when creating a new database. I'm not sure whether pgAdmin gives you the option on the create database dialog but you should be able to execute the following in a query window if it doesn't:
CREATE DATABASE newdb WITH TEMPLATE originaldb OWNER dbuser;
Still, you may get:
ERROR: source database "originaldb" is being accessed by other users
To disconnect all other users from the database, you can use this query:
SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'originaldb' AND pid <> pg_backend_pid();

Postgres drop database error: pq: cannot drop the currently open database

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)

Trying to rename a database in Redshift cluster

I'm trying to rename a database in my Redshift cluster.
You cannot rename the database when you're connected to it so I've created a temporary database, reconnected with SQL Workbench to the temporary db and issued:
ALTER DATABASE olddb RENAME to newdb;
I get an error stating ERROR: database "olddb" is being accessed by other users [SQL State=55006]
I've checked who is connected and there appear to be some connections from user rdsdb to the database. I assume this is a service account that AWS Redshift use to perform maintenance tasks etc.
How can I rename the database when this superuser is connected?
Many thanks.
You cannot alter the name of (or delete!) the database that is created during the initial cluster creation. I don't believe this is mentioned in the docs but I've confirmed it with them.
We can change the database name which is already created.
Detailed steps on how to do
Connect to the old database and create a new database if you do not have another one already.
create database databasebasename
In this example, I will call the databasename as 'newdb'.
Connect to newdb using connecting string as, jdbc:redshift://.us-east-1.redshift.amazonaws.com:8192/newdb, with the same password and username of your superuser (or the other eligible users as mentioned above).
Now you can alter the database name. Substitute 'database_name_new' with the desired databasename.
alter database old-db-name rename to database_name_new;
If there are any active sessions, you'll have to kill them. To find the pid of active sessions:
select * from STV_SESSIONS where user_name='rdsdb';
Then to kill a session:
SELECT
pg_terminate_backend(<pid>)
FROM
pg_stat_activity
WHERE
-- don't kill my own connection!
procpid <> pg_backend_pid()
-- don't kill the connections to other databases
AND datname = '<old-db-name>';
Once complete, you can connect back to that new database using the new name in the connection string as
jdbc:redshift://<cluser-id>.us-east-1.redshift.amazonaws.com:8192/database_name_new
You can delete the temporary 'newdb'.
drop database databasebasename
That's possible now -- I just renamed the database that was created during the initial cluster creation.
We had a similar situation.
Step 1: Connect to the database which is not the one you are trying to rename. Check the same by executing SELECT CURRENT_DATABASE();.
Step 2: Execute the query below -
SELECT
ss.*, 'select pg_terminate_backend('||process||');'
FROM
stv_sessions ss
ORDER BY
db_name;
The output of the query will have a column at the end with the select statements. Execute those to kill the sessions.
Step 3(Optional): If you are not the owner of the database try to modify the ownership of the database -
ALTER DATABASE <database to be renamed>
OWNER TO <user which is going to do the rename>;
Step 4: Rename the database