[ About | Licence | Contacts ]
Written by Oleksandr Gavenko (AKA gavenkoa), compiled on 2024-04-01 from rev 052223c22317.

Postgres

Installing on Debian

Install and create new user and database:

$ sudo apt-get install postgresql postgresql-client
$ sudo su - postgres
% psql
postgres=# CREATE USER myuser WITH PASSWORD 'mypass';
postgres=# CREATE DATABASE mydb OWNER myuser;
postgres=# \q

Connect as user mypguser to new database:

$ su - mypguser
$ psql mypgdatabase

In order to create local host superuser:

$ sudo su - postgres
$ createuser --superuser USER
$ exit
$ sudo -u USER psql
https://wiki.debian.org/PostgreSql
Debian wiki instructions.

Check version

By SQL query:

SELECT version();
SHOW server_version;
SHOW server_version_num;

From executable:

$ postgres -V

Check locations

SHOW hba_file;
SHOW config_file;

Check settings

SELECT name, setting FROM pg_settings;
SELECT name, current_setting(name), source FROM pg_settings;

Check extensions

SELECT * FROM pg_available_extensions;

Set application name

application_name is displayed in pg_stat_activity.application_name.

It can help identify clients of DB. To alter it in current session:

SET application_name = 'some name';

To show current value:

SHOW application_name;

Limitations

https://wiki.postgresql.org/wiki/TOAST
TOAST is a mechanism PostgreSQL uses to keep physical data rows from exceeding the size of a data block (typically 8KB).

List privileges

List databases with privileges:

\l

List schemas with privileges:

\dn+

List users with privileges:

\du

List privileges to objects (tables, views, sequences):

\dp

SELECT * FROM information_schema.role_table_grants
WHERE table_name = 'table_name';

List default privileges:

\ddp

SELECT * FROM pg_default_acl;

Grant role options

Show role options and group membership:

\du

Grant role options:

ALTER USER me WITH SUPERUSER;
ALTER USER me WITH CREATEDB CREATEROLE LOGIN;

Revoke role options:

ALTER USER me WITH NOSUPERUSER;
https://www.postgresql.org/docs/current/sql-createrole.html
CREATE ROLE.

Granting selects:

GRANT SELECT ON ALL TABLES IN SCHEMA public TO ro_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO rw_user;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO rw_user;

Making read-only role:

GRANT CONNECT ON DATABASE mydb TO ro;
GRANT USAGE ON SCHEMA myschema TO ro;
GRANT SELECT ON TABLE myschema.myt1, myschema.myt2 TO ro;

Granting default privileges for new objects:

ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO rw_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT ALL ON SEQUENCES TO rw_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT EXECUTE ON FUNCTIONS TO rw_user;

Revoke privileges and drop role

List roles:

SELECT * FROM pg_roles;

SELECT oid, rolname FROM pg_roles
WHERE pg_has_role('xuser', oid, 'member');

Revoke privileges:

REVOKE ALL PRIVILEGES ON DATABASE mydb FROM xuser;
REVOKE ALL PRIVILEGES ON SCHEMA myschem FROM xuser;
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA myschem FROM xuser;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA myschem FROM xuser;

do $$
DECLARE r record;
begin
    for r in select * from pg_views where schemaname = 'myschem'
    loop
      execute 'revoke all on ' || quote_ident(r.schemaname) ||'.'|| quote_ident(r.viewname) || ' from "xuser"';
    end loop;
end $$;

To forbid any operations (read/write) without dealing with individual privileges (any pending user transactions will fail!):

REVOKE USAGE ON SCHEMA myschem FROM xuser;

To forbid connection remove privilege from public role as it inherited by all users and then from user:

REVOKE CONNECT ON DATABASE mydb FROM public;
REVOKE CONNECT ON DATABASE mydb FROM xuser;

Role can't be dropped until it owns objects, need to reassign owner:

REASSIGN OWNED BY xuser TO myuser;

Some objects may left behind role (like default privileges), they can be dropped via:

DROP OWNED BY xuser;

Remove role:

DROP ROLE xuser;

Change password

ALTER USER me WITH PASSWORD 'PassWord';

Monitor connections

Current user:

SELECT current_user;
SELECT session_user;

Active sessions and queries:

select * from pg_stat_activity;

select * from pg_stat_activity
order by usename;
https://www.postgresql.org/docs/current/monitoring-stats.html
The Statistics Collector.

Profile queries

Stats on query execution time:

SELECT * FROM pg_stat_statements;

SELECT * FROM pg_stat_statements
ORDER BY calls DESC;

SELECT * FROM pg_stat_statements
WHERE query LIKE '%some%'
ORDER BY calls DESC;
https://www.postgresql.org/docs/current/pgstatstatements.html
pg_stat_statements.
https://www.postgresql.org/docs/current/indexes-examine.html
Examining Index Usage.

Reveal execution details:

EXPLAIN ANALYSE SELECT 1;
EXPLAIN (TIMING false, COSTS false, ANALYSE) SELECT 1;
EXPLAIN (TIMING false, COSTS false, BUFFERS false, VERBOSE false, ANALYSE) SELECT 1;

EXPLAIN ANALYSE actually executes a query, if it is destructive enclose the query into a transaction:

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;
https://www.postgresql.org/docs/11/sql-explain.html
Syntax of EXPLAIN.
https://www.postgresql.org/docs/11/using-explain.html
Interpreting results of EXPLAIN.

List databases, schemas and tables

Default database is postgres.

To list databases and database locales:

$ psql -U pgadmin -l

or:

=> SELECT datname FROM pg_database WHERE datistemplate = false;
=> \l

To switch databases:

=> \connect NAME

Schemas:

\dn *
select schema_name from information_schema.schemata;
select nspname from pg_catalog.pg_namespace;

To list all tables in the current database:

\dt
SELECT table_schema, table_name FROM information_schema.tables ORDER BY table_schema, table_name;
SELECT * FROM pg_class;

or in all schemas:

=> \dt *.*

List views:

\dv

List users:

\du
\du+

List schemas:

\dn

List indexes:

\di
select * from pg_indexes;
select * from pg_indexes where schemaname = '...';
select * from pg_indexes where schemaname = '...' and tablename = '...';
select * from pg_indexes where schemaname = '...' and indexname = '...';

select
  t.relname as table_name,
  i.relname as index_name,
  a.attname as column_name
from
  pg_class t,
  pg_class i,
  pg_index ix,
  pg_attribute a
where
  t.oid = ix.indrelid
  and i.oid = ix.indexrelid
  and a.attrelid = t.oid
  and a.attnum = ANY(ix.indkey)
  and t.relkind = 'r'
  and t.relname like 'test%'
order by
  t.relname,
  i.relname;

select
  t.relname as table_name,
  i.relname as index_name,
  array_to_string(array_agg(a.attname), ', ') as column_names
from
  pg_class t,
  pg_class i,
  pg_index ix,
  pg_attribute a
where
  t.oid = ix.indrelid
  and i.oid = ix.indexrelid
  and a.attrelid = t.oid
  and a.attnum = ANY(ix.indkey)
  and t.relkind = 'r'
  and t.relname like 'test%'
group by
  t.relname,
  i.relname
order by
  t.relname,
  i.relname;

List index usage:

SELECT * FROM pg_stat_all_indexes;
SELECT * FROM pg_stat_all_tables;
SELECT * FROM pg_stat_user_indexes;
SELECT * FROM pg_stat_user_tables;

List constraints:

SELECT * FROM pg_constraint;
SELECT * FROM pg_constraint WHERE conrelid = (SELECT oid FROM pg_class WHERE relname LIKE 'tblName');
select * from pg_constraint where conname like '%conName%';

Show constraints and columns to given table:

select
  c.constraint_name,
  x.table_schema as schema_name, x.table_name, x.column_name,
  y.table_schema as foreign_schema_name, y.table_name as foreign_table_name, y.column_name as foreign_column_name
from information_schema.referential_constraints c
join information_schema.key_column_usage x
    on x.constraint_name = c.constraint_name
join information_schema.key_column_usage y
    on y.ordinal_position = x.position_in_unique_constraint
    and y.constraint_name = c.unique_constraint_name
where y.table_name = 'sa_pnr'
order by c.constraint_name, x.ordinal_position
https://www.postgresql.org/docs/current/catalog-pg-constraint.html
pg_constraint.
https://stackoverflow.com/questions/1152260/postgres-sql-to-list-table-foreign-keys
List table foreign keys.

List functions:

\df

List triggers:

\dy

List misc info:

select current_database();
select current_schema();
select current_user;
select session_user;
select current_date;
select version();
https://www.postgresql.org/docs/current/functions-info.html
System Information Functions.

Set default schema.

set search_path to NAME;
set schema 'NAME';

Database, table, index size

Database size:

SELECT pg_database_size('geekdb');  -- in bytes
SELECT pg_size_pretty(pg_database_size('dbname'));

SELECT datname, pg_database_size(datname) FROM pg_database;

List of databases sizes:

\l+

SELECT pg_database_size('mydb');

List tables sizes (and constraints):

\d+

All tables row count:

SELECT relname, n_live_tup FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY n_live_tup DESC;

Table total size (with indexes):

SELECT pg_size_pretty(pg_total_relation_size('schemaname.tablename'));

Table size with TOAST (without indexes):

SELECT pg_table_size('schemaname.tablename');

Sole table size (without indexes and TOAST):

SELECT pg_size_pretty(pg_relation_size('schemaname.tablename'));

SELECT pg_relation_size(t.schemaname ||'.'|| t.tablename), t.* FROM pg_tables t ORDER BY 1 DESC;

Largest table in the PostgreSQL database:

SELECT relname, relpages FROM pg_class ORDER BY relpages DESC;
SELECT relname, pg_size_pretty(pg_relation_size(p.oid)) FROM pg_class ORDER BY relpages DESC;

SELECT pg_table_size(t.schemaname ||'.'|| t.tablename), pg_relation_size(t.schemaname ||'.'|| t.tablename), t.*
FROM pg_tables t
ORDER BY 1 DESC;

Largest tables with TOAST:

SELECT p.relname, t.relname, pg_size_pretty(pg_relation_size(p.oid)), p.relpages
FROM pg_class p
LEFT JOIN pg_class t ON t.reltoastrelid = p.oid
ORDER BY p.relpages DESC;

Largest TOAST:

select tbl.relname, toast.relname, toast.relpages
from pg_class tbl, pg_class toast
where tbl.reltoastrelid = toast.oid
order by toast.relpages desc;

Index size:

SELECT pg_indexes_size('my_ix');

Largest index:

select pg_relation_size(oid), * from pg_class
where relkind = 'i'
order by pg_relation_size(oid) desc;

select pg_relation_size(i.oid), t.relname as tname, i.relname as iname, *
from pg_class i
join pg_index ix on ix.indexrelid = i.oid
join pg_class t on t.oid = ix.indrelid
where i.relkind = 'i'
order by pg_relation_size(i.oid) desc;

Size of types:

SELECT pg_column_size(5::smallint);
SELECT pg_column_size(5::int);
SELECT pg_column_size(5::bigint);

Size of column:

SELECT sum(pg_column_size(col)) FROM tbl;

Total of all pg_column_size is less then pg_relation_size because table is using pages to store rows and we might have gaps inside pages.

https://www.postgresql.org/docs/9.6/functions-admin.html
Database Object Management Functions.
https://wiki.postgresql.org/wiki/Disk_Usage
Finding the size of various object in your database.
https://wiki.postgresql.org/wiki/Count_estimate
Count estimate.
https://wiki.postgresql.org/wiki/Index_Maintenance
Index size/usage statistics.

MVCC and transaction id

As being MVCC Postgres doesn't delete or update existing rows. Row visibility is controled by transaction id (monotonous incremented number): xmin - first transaction that can see row, xmax - last transaction that can see row:

SELECT xmin, xmax, * FROM foo WHERE id < 5 ORDER BY id;

Vacuum

Stats on vacuum oprtations per table:

select * from pg_stat_user_tables;

Table statistics

Full table statistics is in pg_statistics table which is accessible only by superuser.

Human readable stats are in pg_stats view.

Using psql client

Using password file ~/.pgpass:

# comment
hostname:port:database:username:password
hostname:port:*:username:password
hostname:*:*:username:password

Connect by:

$ psql -U $USER -h $HOST -p $PORT  $SCHEMA

Switch to long lines format:

\x

How to view execution plan:

EXPLAIN query;
EXPLAIN ANALYZE query;

Collect statistics:

ANALYZE table;

How to redirect the output of query to a file:

\o output_file
SELECT * FROM pg_class;

Switch database:

\connect DBNAME
\c DBNAME

Load external SQL file:

\i my.sql

Execute external file:

psql -f my.sql

Export data

Export from psql:

\copy (SELECT * FROM foo) TO '/tmp/my.csv' WITH CSV

Import data

Import CSV file:

\copy tbl_name from 'my.csv' csv;
\copy tbl_name from 'my.csv' delimiter ':' csv;

$ psql -U $USER -h $HOST $DB -c "\\copy tbl_name from 'my.csv' csv"

Copy tables

Create table with data copying using another table (no constraints & indices are recreated, including PK):

CREATE TABLE mycopy AS TABLE myorig;

Create table without data copying using another table:

CREATE TABLE mycopy AS TABLE myorig WITH NO DATA;

Create table from select and copy data (no constraints & indices are recreated, including PK):

CREATE TABLE mycopy AS (SELECT * FROM myorig);

Create table from select (without copying data):

CREATE TABLE mycopy AS (SELECT * FROM myorig) WITH NO DATA;

JDBC driver

https://jdbc.postgresql.org/about/about.html
The current version of the driver should be compatible with PostgreSQL 8.2 and higher, and Java 6 (JDBC 4.0), Java 7 (JDBC 4.1), Java 8 (JDBC 4.2) and Java 9.
https://jdbc.postgresql.org/download.html
Java/JDBC/Postgres compatibility table.

plpgsql

Simple function:

CREATE OR REPLACE FUNCTION incx(x INTEGER)
RETURNS INTEGER STABLE AS $$
  BEGIN
    RETURN x + 1;
  END;
$$ LANGUAGE plpgsql;

Support for procedures is added only in Postgres v11.

https://www.postgresql.org/docs/current/sql-createfunction.html
CREATE FUNCTION.
https://www.postgresql.org/docs/current/plpgsql-control-structures.html
plpgsql control structures.
https://www.postgresql.org/docs/current/errcodes-appendix.html
PostgreSQL Error Codes.

Anonymous block:

do $$
begin
    null;
end $$;

You cannot perform select without assigning resultset, special keyword perform is used insted of select in PLPQGSL:

do $$
begin
    perform 1 union all select 2;
end$$;

Temporary constant table

Standard SQL syntax:

SELECT 1 AS num, 'a' AS str
UNION ALL SELECT 2, 'b'
UNION ALL SELECT 3, 'c';

Postgre extension:

SELECT num, str FROM (
  VALUES (1, 'a'), (2, 'b'), (3, 'c')) AS tbl(num, str);

WITH tbl(num, str) AS (VALUES (1, 'a'), (2, 'b'), (3, 'c'))
SELECT num, str FROM tbl;

To generate table:

SELECT val FROM generate_series(1, 10) val;

WITH t AS (SELECT val FROM generate_series(1, 10) val)
SELECT * FROM t;

WITH
  seq1(num) AS (VALUES (1), (2), (3)),
  seq2(str) AS (VALUES ('a'), ('b'), ('c'))
SELECT * FROM seq1
CROSS JOIN seq2;