Postgresql
Install
apt-get install postgresql
Notes
- To connect locally using the system user:
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# -- DO YOUR QUERIES postgres=# \q
- To connect remotely with a password protected instance:
--password (or -W) tell the command to ask for a password
psql --host=127.0.0.1 --port=5432 --username=postgres --dbname=postgres --password
PGPASSWORD env var tell the command to use the var content as password
PGPASSWORD=pass1234 psql --host=127.0.0.1 --port=5432 --username=postgres --dbname=postgres --no-password
- To connect to the read-write instance of a postgres cluster:
psql 'postgresql://HOST1:PORT1,HOST2:PORT2,HOST3:PORT3/DATABASE?target_session_attrs=read-write' -U USER
- To setup the client to show text-wrapped query results:
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# \pset format wrapped postgres=# -- DO YOUR QUERIES postgres=# \q
System query
List users
root@host:~# su - postgres postgres@host:~$ psql postgres=# SELECT usename FROM pg_user; # or \dg postgres=# \q
List databases
root@host:~# su - postgres postgres@host:~$ psql -U postgres -l
or
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# SELECT datname FROM pg_database; # or \l postgres=# \q
Show database size
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# SELECT pg_size_pretty(pg_database_size('NAME_DB')); postgres=# \q
Show all databases sizes
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC; postgres=# \q
List db tables
root@host:~# su - postgres postgres@host:~$ psql -U postgres -d name_db postgres=# SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'; # or \d postgres=# \q
Show table size
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# SELECT pg_size_pretty(pg_total_relation_size('db_table')); postgres=# \q
Show all tables sizes
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# SELECT table_catalog AS database, table_schema || '.' || table_name AS name, pg_size_pretty(pg_total_relation_size(table_schema || '.' || table_name)) AS size FROM information_schema.tables ORDER BY pg_total_relation_size(table_schema || '.' || table_name) DESC; postgres=# \q
List table columns
root@host:~# su - postgres postgres@host:~$ psql -U postgres -d name_db postgres=# SELECT column_name, is_nullable, data_type, character_maximum_length, numeric_precision FROM information_schema.columns WHERE table_name ='db_table'; # or \d db_table postgres=# \q
Show connections
- show connection info
SELECT * FROM (SELECT count(*) used FROM pg_stat_activity) q1, (SELECT setting::int res_for_super FROM pg_settings WHERE name='superuser_reserved_connections') q2, (SELECT setting::int max_conn FROM pg_settings WHERE name='max_connections') q3;
- count connections by user
SELECT usename, count(*) AS count FROM pg_stat_activity GROUP BY usename ORDER BY count DESC;
List current queries by time
SELECT pid, datname, usename, EXTRACT(epoch FROM now() - xact_start) AS seconds, state, query FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND state IN ('idle in transaction', 'active') ORDER BY xact_start ASC;
List blocking query and kill them!
- From
- https://wiki.postgresql.org/wiki/Lock_Monitoring
- http://stackoverflow.com/questions/11291456/heroku-postgres-terminate-hung-query-idle-in-transaction
To find blocked processes, working for Postgresql version >= 9.2
root@host:~# su - postgres postgres@host:~$ psql -U postgres -d name_db postgres=# \pset format wrapped postgres=# SELECT DISTINCT blocking_locks.pid AS blocking_pid, blocking_activity.usename AS b_user, blocking_activity.query_start AS b_start, blocking_locks.relation::regclass AS b_relation, blocking_activity.query AS b_statement, blocking_activity.waiting AS b_wait, blocking_activity.state AS b_state, waiting_locks.pid AS waiting_pid, waiting_activity.usename AS w_user, waiting_activity.query_start AS w_start, waiting_locks.relation::regclass AS w_relation, waiting_activity.query AS w_statement, waiting_activity.waiting AS w_wait, waiting_activity.state AS w_state FROM pg_catalog.pg_locks waiting_locks JOIN pg_catalog.pg_stat_activity waiting_activity ON waiting_activity.pid = waiting_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = waiting_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM waiting_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM waiting_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM waiting_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM waiting_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM waiting_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM waiting_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM waiting_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM waiting_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM waiting_locks.objsubid AND blocking_locks.pid != waiting_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid WHERE NOT waiting_locks.GRANTED ORDER BY b_start DESC; postgres=# \q postgres@host:~$ exit root@host:~#
To find blocked processes, working for Postgresql version < 9.2
root@host:~# su - postgres postgres@host:~$ psql -U postgres -d name_db postgres=# \pset format wrapped postgres=# SELECT DISTINCT blocking_locks.pid AS blocking_pid, blocking_activity.usename AS b_user, blocking_activity.query_start AS b_start, blocking_locks.relation::regclass AS b_relation, blocking_activity.current_query AS b_statement, blocking_activity.waiting AS b_wait, --blocking_activity.state AS b_state, waiting_locks.pid AS waiting_pid, waiting_activity.usename AS w_user, waiting_activity.query_start AS w_start, waiting_locks.relation::regclass AS w_relation, waiting_activity.current_query AS w_statement, waiting_activity.waiting AS w_wait --waiting_activity.state AS w_state FROM pg_catalog.pg_locks waiting_locks JOIN pg_catalog.pg_stat_activity waiting_activity ON waiting_activity.procpid = waiting_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.transactionid = waiting_locks.transactionid AND blocking_locks.pid != waiting_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.procpid = blocking_locks.pid WHERE NOT waiting_locks.GRANTED ORDER BY b_start DESC; postgres=# \q postgres@host:~$ exit root@host:~#
Kill a postgresql process by PID
root@host:~# su - postgres postgres@host:~$ psql -U postgres -d name_db postgres=# SELECT pg_terminate_backend(PID); postgres=# \q postgres@host:~$ exit root@host:~#
Administration
Create user
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# CREATE ROLE db_user LOGIN PASSWORD 'db_pass';
Create super user
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres@host:~$ psql -U postgres postgres=# CREATE ROLE superuser WITH LOGIN PASSWORD 'superuser' SUPERUSER;
Create readonly user
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres postgres=# CREATE ROLE readonly_user LOGIN PASSWORD XXX; postgres=# GRANT CONNECT ON DATABASE database_to_query TO readonly_user; postgres=# \q postgres@host:~$ psql -U postgres postgres database_to_query database_to_query=# GRANT USAGE ON SCHEMA public TO readonly_user; database_to_query=# GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user; database_to_query=# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_user; postgres=# quit=# \q
Create database
root@host:~# su - postgres postgres@host:~$ psql -U postgres postgres=# CREATE DATABASE name_db WITH ENCODING 'UTF8' OWNER "db_user" TEMPLATE template0;
NOTE: the default template used to create a new database is template1, which can contain customized data admins want to be added in any new database. But if you dump a db created from template1 and restore in a new db created from template1, the customization can be done twice leading to conflicts, so the empty db to use as a destination for the restore should be created from template0. Source https://www.postgresql.org/docs/current/manage-ag-templatedbs.html .
Test user
root@host:~# psql -h 127.0.0.1 -U db_user -W -d name_db
Backup and restore
The simplest mode is to skip backup of database permissions. The follow scripts can help:
- A database full backup
#!/bin/bash # show_usage() { echo "Usage: "`basename $0`" <host> <port> <user> <database>" echo "Example: "`basename $0`" 127.0.0.1 5432 postgres postgres" } if test -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" then show_usage >&2 exit 1 fi HOST="$1" PORT="$2" USER="$3" DB="$4" pg_dump --clean --if-exists --no-owner --no-acl -h "${HOST}" -p "${PORT}" -U "${USER}" "${DB}" > "${DB}_all_`date +%Y-%m-%d_%H-%M-%S`.sql" # End
- A database structure only:
#!/bin/bash # show_usage() { echo "Usage: "`basename $0`" <host> <port> <user> <database>" echo "Example: "`basename $0`" 127.0.0.1 5432 postgres postgres" } if test -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" then show_usage >&2 exit 1 fi HOST="$1" PORT="$2" USER="$3" DB="$4" pg_dump --schema-only --no-owner --no-acl -h "${HOST}" -p "${PORT}" -U "${USER}" "${DB}" > "${DB}_structure_`date +%Y-%m-%d_%H-%M-%S`.sql" # End
- A database single table data only:
#!/bin/bash # show_usage() { echo "Usage: "`basename $0`" <host> <port> <user> <database> <table>" echo "Example: "`basename $0`" 127.0.0.1 5432 postgres postgres pg_catalog.pg_type" } if test -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" -o -z "$5" then show_usage >&2 exit 1 fi HOST="$1" PORT="$2" USER="$3" DB="$4" TABLE="$5" pg_dump --data-only --no-owner --no-acl -h "${HOST}" -p "${PORT}" -U "${USER}" --table="${TABLE}" "${DB}" > "${DB}_table_${TABLE}_`date +%Y-%m-%d_%H-%M-%S`.sql" # End
- You can restore such backups without database permission simply using psql tool, specifying the destination user and database (that should be empty or just created) like in the follow example:
# database creation postgres@host_dst:~$ psql -h localhost -U postgres postgres=# CREATE ROLE db_user LOGIN PASSWORD 'db_pass'; CREATE ROLE postgres=# CREATE DATABASE name_db WITH ENCODING 'UTF8' OWNER "db_user" TEMPLATE template0; CREATE DATABASE postgres=# \q # database restore postgres@host_dst:~$ psql -h localhost -U db_user name_db < dbname-dump.sql
A single database
- At host
root@host_src:~# su - postgres postgres@host_src:~# pg_dump -h localhost -U postgres name_db > dbname-dump.sql
- At destination
root@host_des:~# su - postgres postgres@host_des:~# psql -h localhost -U postgres -d name_db -E < dbname-dump.sql
The database name_db will not be created by this command, so you must create it yourself from template0 before executing psql (e.g., with createdb -T template0 name_db).
- Example:
on source host
postgres@host_src:~$ pg_dump -h localhost -U postgres name_db > dbname-dump.sql
on destination host
postgres@host_dst:~$ psql -h localhost -U postgres postgres=# CREATE ROLE db_user LOGIN PASSWORD 'db_pass'; CREATE ROLE postgres=# CREATE DATABASE name_db WITH ENCODING 'UTF8' OWNER "db_user" TEMPLATE template0; CREATE DATABASE postgres=# \q postgres@host_dst:~$ psql -h localhost -U postgres -E name_db < dbname-dump.sql
- Note: a better (secure) way is use the owner of database
on source host
postgres@host_src:~$ pg_dump -U postgres name_db --no-owner > dbname-dump.dump.sql
on destination host
postgres@host_dst:~$ psql -h localhost -U db_user -d name_db -E < dbname-dump.dump.sql
Just the database users/roles
on source host
postgres@host_src:~$ pg_dumpall -h localhost -U postgres name_db --globals-only --clean --if-exists > dumpall_globalsonly.sql
note: it contains the postgres user permission too, you can avoid changes to such user removing the related lines in the dump:
sed -i '/^DROP ROLE.*postgres/d;/^CREATE ROLE.*postgres/d;/^ALTER ROLE.*postgres/d' dumpall_globalsonly.sql
on destination host
postgres@host_dst:~$ psql -E --set ON_ERROR_STOP=on -h localhost -U postgres -d postgres -f dumpall_globalsonly.sql
Backup and Recovery all databases
From PostgreSQL: Documentation: Manuals: Backup and Restore
- Backup
root@host:~# su - postgres postgres@host:~$ pg_dumpall > all-dump.sql
or
root@host:~# su - postgres postgres@host:~$ pg_dumpall | gzip > all-dump.sql.gz
- Recovery
root@host:~# su - postgres postgres@host:~$ psql -E < all-dump.sql
A database table
- Dump
pg_dump -U db_user -t table_name -f dbname-tabledump.sql name_db
- Restore
psql -E -U db_user -d name_db < dbname-tabledump.sql
Maintenance
postgres@108:~$psql -U postgres psql (8.4.21) Type "help" for help. postgres=# SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS size FROM pg_database; datname | size --------------+--------- template0 | 5192 kB postgres | 5288 kB template1 | 5280 kB (3 rows) postgres=# \q postgres@108:~$ vacuumdb --analyze --verbose template1 [...] postgres@108:~$ reindexdb template1 [...] postgres@108:~$ psql -U postgres psql (8.4.21) Type "help" for help. postgres=# SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS size FROM pg_database; datname | size --------------+--------- template1 | 5280 kB template0 | 5192 kB postgres | 5288 kB (3 rows) postgres=# \q postgres@108:~$
Tools
Diff tools
Model tools
Pgmodeler
- Features:
- Extract diagram model from database connection
- Diff betweeen two databases
- Apply changes (and diff changes)
- Installation:
sudo apt-get install g++ pkg-config qt5-qmake qt5-default qtchooser libpq-dev libxml2-dev wget https://github.com/pgmodeler/pgmodeler/archive/v0.8.1.tar.gz tar -xzvf v0.8.1.tar.gz cd pgmodeler-0.8.1/ qmake PREFIX=$HOME/pgmodeler pgmodeler.pro make make install $HOME/pgmodeler/bin/pgmodeler