-- Program: pg2html.sql -- Info: PostgreSQL report in HTML -- Works with PostgreSQL 10 or sup. (tested and updated up to 15.x) -- Date: 2008-08-15 -- Version: 1.0.25 on 2023-02-14 -- Author: Bartolomeo Bogliolo (meo) mail@meo.bogliolo.name -- Usage: psql [-U USERNAME] [DBNAME] < pg2html.sql > /dev/null -- Notes: 1-APR-08 mail@meo.bogliolo.name -- 1.0.0 First version based on ora2html (Oracle report in HTML) -- 1.0.3 Minor changes (eg. formatting, alignment) -- 1.0.4 TOTAL for objects, space usage, roles -- 1.0.5 HTML5, function count -- 1.0.6 Pg 9.1 new features (if <9.1 gives an error on pg_available_extension) -- 1.0.7 Added poor password check (a) session summary -- 1.0.8 Pg 9.2 new features (NB pg_stat_activity is not compatible with previuos releases) -- 1.0.9 More performance statistics -- 1.0.10 Replication stats, (a) vacuum stats, (b) pg_stat_statement summary, (c) pg_buffercache -- 1.0.11 pg_stat_archiver (9.4), pg_stat_activity bkw changes (9.6), logical replication (10.1) -- (a) Schema/Function Matrix -- 1.0.12 HBA.conf file, datatypes usage, 10.x new wal function names, WAL list -- 1.0.13 Latest versions update -- 1.0.14 Latest versions update, relkind in pg_buffercache stat, bloat stats, HBA rules -- 1.0.15 Latest versions update, version 12 compliance -- 1.0.16 Latest versions update -- 1.0.17 Latest versions update -- 1.0.18 Per Host sessions, latest versions update -- 1.0.19 Both owner/schema in matrix, latest versions update -- 1.0.20 Latest versions update, (a) May 2021 updates, (b) SCRAM encryption in passwords -- 1.0.21 Latest versions update, fork for version 13 -- 1.0.22 Dynamic version: works for all PG supported releases and with more details for created extensions; some bigint casting -- 1.0.23 Latest versions update, (a) \if bug fixed (b) limit on the all index list (c,d,e,f) latest versions update -- 1.0.24 WAL bytes in pg_stat_statements, users/roles, (a) pg_stat_wal_receiver, pg_replication_slots (b) pg_stat_statements_info -- 1.0.25 Largest TOAST list, Stored Procedures count, Partitioning details, some graphical fixes, EnterpriseDB filter, -- PG16, estimated bloat, latest versions update, Postgis/Aurora/EDB additional statistics \pset tuples_only \pset fieldsep ' ' \pset footer off \a \o pg.htm select 'pg2html - PostgreSQL Statistics' as info; select '

PostgreSQL - '||current_database()||'

' as info; select '

' as info; select '

Table of contents:' as info; select '


' as info; select '

Statistics generated on: '|| now(); select 'on database: '||current_database()||'' as info; select 'by user: '||user as info; select 'using: pg2html.sql v.1.0.25-alfa6' as info; select '
Software by ' as info; select 'Meo

' as info; select '


' as info; select '

' as info; select '
Summary
Item', 'Value' as info; select '
'||' Database :', '', ''||current_database() union select '
'||' Version :', '', ''||substring(version() for position('on' in version())-1) union select '
'||' DB Size:', '', ''||pg_size_pretty(sum(pg_database_size(datname))) from pg_database; select '
'||' Created :', '', ''|| (pg_stat_file('base/'||oid ||'/PG_VERSION')).modification from pg_database where datname='template0'; select '
'||' Started :', '', ''||pg_postmaster_start_time() union select '
'||' Memory buffers (MB) :', '', ''||trunc(sum(setting::int*8)/1024) from pg_settings where name like '%buffers' union select '
'||' Work area (MB) :', '', ''||trunc(sum(setting::int)/1024) from pg_settings where name like '%mem' union select '
'||' Wal Archiving :', '', ''||setting from pg_settings where name like 'archive_mode'; select '
'||' Databases :', '', ''||count(*) from pg_database where not datistemplate union select '
'||' Defined Users/Roles :', '', ''||sum(case when rolcanlogin then 1 else 0 end)|| ' / '|| sum(case when rolcanlogin then 0 else 1 end) from pg_roles union select '
'||' Defined Schemata :', '', ''||count(distinct relowner) from pg_class union select '
'||' Defined Tables :', '', ''||count(*) from pg_class where relkind='r'; select '
'||' Sessions :', '', ''||count(*) from pg_stat_activity union select '
'||' Sessions (active) :', '', ''||count(*) from pg_stat_activity where state = 'active'; select '
'||' Host IP :', '', ''||inet_server_addr() union select '
'||' Port :', '', ''||inet_server_port() order by 2; select '


' as info; select '

' as info; select '

' as info; select ''; select '
Version
'||version()||'

' as info; select '

' ; select '
Version check
Version', ' Supported', ' Last major release (N or N-2)', ' Last minor release (N or N-1)', ' Notes'; SELECT '
'||substring(version() for position('on' in version())-1); SELECT '', CASE WHEN trunc(cast(current_setting('server_version_num') as integer)/100) in (1000, 1100, 1200, 1300, 1400, 1500) THEN 'YES' ELSE 'NO' END; SELECT '', CASE WHEN trunc(cast(current_setting('server_version_num') as integer)/100) in (1300, 1400, 1500) THEN 'YES' ELSE 'NO' END; -- last2 release SELECT '', CASE WHEN cast(current_setting('server_version_num')as integer) in (90623,90624, 100023,100022, 110019,110018,110020, 120014,120013,120015, 130010,130009,130011, 140007,140006,140008, 150000,150001,150003) THEN 'YES' ELSE 'NO' END; -- last2 update select 'Latest Releases: 15.2, 14.7, 13.10, 12.14, 11.19'; select '
Latest Unsupported: 10.23, 9.6.24, 9.5.25, 9.4.26, 9.3.25, 9.2.24, 9.1.24, 9.0.23,'; select ' 8.4.21, 8.3.23, 8.2.23, 8.1.23, 8.0.26, 7.4.30; 6.5.3, 1.0.9'; select '


'; select '

' as info; select '

' as info; select '
Databases
Name', 'OID', 'Owner', 'Size', 'HR Size' as info; select '
'||datname, '',oid, '',datdba::regrole::text, ''||pg_database_size(datname), ''||pg_size_pretty(pg_database_size(datname)) from pg_database where not datistemplate; select '
TOTAL (MB)','','', ''||trunc(sum(pg_database_size(datname))/(1024*1024)), ''||pg_size_pretty(sum(pg_database_size(datname))::int8) from pg_database; select '


' as info; select '

' as info; select '

' as info; select '
Tablespaces
Name', 'Owner', 'Location' as info; select '
'||spcname, '',pg_catalog.pg_get_userbyid(spcowner), '',pg_catalog.pg_tablespace_location(oid) from pg_tablespace order by oid; select '


' as info; select '

' as info; select '

' as info; select '
Schema/Object Matrix
SchemaOwner', ' Table', ' Index', ' View', ' Sequence', ' Composite type', ' Foreign table', ' TOAST table', ' Materialized view', ' Partitioned table', ' Partitioned index', ' Unlogged', ' Temporary', ' TOTAL' as info; select '
'||nspname, ''||rolname, ''||sum(case when relkind='r' THEN 1 ELSE 0 end), ''||sum(case when relkind='i' THEN 1 ELSE 0 end), ''||sum(case when relkind='v' THEN 1 ELSE 0 end), ''||sum(case when relkind='S' THEN 1 ELSE 0 end), ''||sum(case when relkind='c' THEN 1 ELSE 0 end), ''||sum(case when relkind='f' THEN 1 ELSE 0 end), ''||sum(case when relkind='t' THEN 1 ELSE 0 end), ''||sum(case when relkind='m' THEN 1 ELSE 0 end), ''||sum(case when relkind='p' THEN 1 ELSE 0 end), ''||sum(case when relkind='I' THEN 1 ELSE 0 end), ''||sum(case when relpersistence='u' THEN 1 ELSE 0 end), ''||sum(case when relpersistence='t' THEN 1 ELSE 0 end), ''||count(*) from pg_class, pg_roles, pg_namespace where relowner=pg_roles.oid and relnamespace=pg_namespace.oid group by rolname, nspname order by nspname, rolname; select '
TOTALTOTAL', ''||sum(case when relkind='r' THEN 1 ELSE 0 end), ''||sum(case when relkind='i' THEN 1 ELSE 0 end), ''||sum(case when relkind='v' THEN 1 ELSE 0 end), ''||sum(case when relkind='S' THEN 1 ELSE 0 end), ''||sum(case when relkind='c' THEN 1 ELSE 0 end), ''||sum(case when relkind='f' THEN 1 ELSE 0 end), ''||sum(case when relkind='t' THEN 1 ELSE 0 end), ''||sum(case when relkind='m' THEN 1 ELSE 0 end), ''||sum(case when relkind='p' THEN 1 ELSE 0 end), ''||sum(case when relkind='I' THEN 1 ELSE 0 end), ''||sum(case when relpersistence='u' THEN 1 ELSE 0 end), ''||sum(case when relpersistence='t' THEN 1 ELSE 0 end), ''||count(*) from pg_class; select '

' as info; select '

' as info; select '

' as info; select '
Partitions
SchemaOwner', ' Object Type', ' Partitioned Objects', ' Partitions'; select '
'||nspname, ''||rolname, ''||t.relkind, '', count(distinct t.relname), '', count(*) from pg_class t, pg_inherits i, pg_class p, pg_roles r, pg_namespace n where i.inhparent = t.oid and p.oid = i.inhrelid and t.relowner=r.oid and t.relnamespace=n.oid group by rolname, nspname, t.relkind order by t.relkind desc, nspname, rolname; select '

' as info; select '

' as info; select '

' as info; select '
Owner/Function Matrix
Owner', ' Functions', ' Procedures', ' TOTAL' as info; select '
'||rolname, ''||sum(case when prokind='p' THEN 0 ELSE 1 end), ''||sum(case when prokind='p' THEN 1 ELSE 0 end), ''||count(*) from pg_proc, pg_roles, pg_language where proowner=pg_roles.oid and prolang=pg_language.oid group by rolname order by rolname; select '
TOTAL', ''||sum(case when prokind='p' THEN 0 ELSE 1 end), ''||sum(case when prokind='p' THEN 1 ELSE 0 end), ''||count(*) from pg_proc, pg_language where prolang=pg_language.oid; select '


' as info; select '

' as info; select '

' as info; select '
Space Usage
Owner', 'Table#', 'Tables rows', 'Tables KBytes', 'Indexes KBytes', 'TOAST KBytes', 'Total KBytes' as info; select '
'||rolname, ''||to_char(sum(case when relkind='r' THEN 1 ELSE 0 end),'999G999G999G999G999G999G999'), ''||to_char(sum(case when relkind='r' THEN greatest(reltuples,0) ELSE 0 end),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(case when relkind='r' THEN cast(1 as bigint)* relpages *8 ELSE 0 end)),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(case when relkind='i' THEN cast(1 as bigint)* relpages *8 ELSE 0 end)),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(case when relkind='t' THEN cast(1 as bigint)* relpages *8 ELSE 0 end)),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(cast(1 as bigint)* relpages *8)),'999G999G999G999G999G999G999') from pg_class, pg_roles where relowner=pg_roles.oid group by rolname order by rolname; select '
TOTAL', ''||to_char(sum(case when relkind='r' THEN 1 ELSE 0 end),'999G999G999G999G999G999G999'), ''||to_char(sum(case when relkind='r' THEN reltuples ELSE 0 end),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(case when relkind='r' THEN cast(1 as bigint)* relpages *8 ELSE 0 end)),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(case when relkind='i' THEN cast(1 as bigint)* relpages *8 ELSE 0 end)),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(case when relkind='t' THEN cast(1 as bigint)* relpages *8 ELSE 0 end)),'999G999G999G999G999G999G999'), ''||to_char(trunc(sum(cast(1 as bigint)* relpages *8)),'999G999G999G999G999G999G999') from pg_class, pg_roles where relowner=pg_roles.oid; select '

' as info; select '

' as info; select '
Internals
Tables#', 'Rows', 'Relpages*8', 'Total Size', 'Main Fork', 'Free Space Map', 'Visibility Map', 'Initialization Fork' as info; select '
'||to_char(count(*),'999G999G999999G999G999G999') obj, ''||to_char(sum(reltuples),'999G999G999G999G999G999G999') as rowcount, ''||to_char(trunc(sum(relpages *8)),'999G999G999G999G999G999G999') relpages, ''||to_char(trunc(sum(pg_total_relation_size(oid))/1024),'999G999G999G999G999G999G999') total, ''||to_char(trunc(sum(pg_relation_size(oid, 'main'))/1024),'999G999G999G999G999G999G999') main, ''||to_char(trunc(sum(pg_relation_size(oid, 'fsm'))/1024),'999G999G999G999G999G999G999') fsm, ''||to_char(trunc(sum(pg_relation_size(oid, 'vm'))/1024),'999G999G999G999G999G999G999') vm, ''||to_char(trunc(sum(pg_relation_size(oid, 'init'))/1024),'999G999G999G999G999G999G999') init from pg_class where relkind='r'; select '

' as info; select '

' as info; select '
Vacuum and Analyze
# Tables', 'Last autoVACUUM', 'Last VACUUM', 'Last autoANALYZE', 'Last ANALYZE' as info; select '
'||count(*), ''||max(last_autovacuum), ''||max(last_vacuum), ''||max(last_autoanalyze), ''||max(last_analyze) from pg_stat_user_tables; select '

' as info; select '

' as info; select '
High dead tuples
Table', 'Tuples', 'Dead tuples', 'Dead%', 'Last autoVACUUM', 'Last VACUUM', 'Last autoANALYZE', 'Last ANALYZE' as info; select '
'||schemaname||'.'||relname, ''||n_live_tup, ''||n_dead_tup, ''||round(100*n_dead_tup/(n_live_tup+n_dead_tup)::float), ''||last_autovacuum, ''||last_vacuum, ''||last_autoanalyze, ''||last_analyze from pg_stat_all_tables where n_dead_tup>1000 and n_dead_tup>n_live_tup*0.05 order by n_dead_tup desc limit 20; select '

' as info; select '

' as info; select '' as info; select '
Bloated tables (estimated size)
Table', 'Fillfactor', 'Table Size', 'HR Size', 'Bloat', 'HR Bloat', 'Bloat%' as info; SELECT '
'||schemaname||'.'||tblname, ''||fillfactor, ''||bs*tblpages AS real_size, ''||pg_size_pretty(bs*tblpages) as HR_size, '', CASE WHEN tblpages - est_tblpages_ff > 0 THEN (tblpages-est_tblpages_ff)*bs ELSE 0 END AS bloat_size, '', CASE WHEN tblpages - est_tblpages_ff > 0 THEN pg_size_pretty( ((tblpages-est_tblpages_ff)*bs)::bigint) ELSE '0' END AS hr_bloat_size, '', CASE WHEN tblpages > 0 AND tblpages - est_tblpages_ff > 0 THEN round(100*(tblpages - est_tblpages_ff)/tblpages::float) ELSE 0 END, '%' FROM ( SELECT ceil( reltuples / ( (bs-page_hdr)*fillfactor/(tpl_size*100) ) ) + ceil( toasttuples / 4 ) AS est_tblpages_ff, tblpages, fillfactor, bs, tblid, schemaname, tblname, heappages, toastpages, is_na FROM ( SELECT ( 4 + tpl_hdr_size + tpl_data_size + (2*ma) - CASE WHEN tpl_hdr_size%ma = 0 THEN ma ELSE tpl_hdr_size%ma END - CASE WHEN ceil(tpl_data_size)::int%ma = 0 THEN ma ELSE ceil(tpl_data_size)::int%ma END ) AS tpl_size, bs - page_hdr AS size_per_block, (heappages + toastpages) AS tblpages, heappages, toastpages, reltuples, toasttuples, bs, page_hdr, tblid, schemaname, tblname, fillfactor, is_na FROM ( SELECT tbl.oid AS tblid, ns.nspname AS schemaname, tbl.relname AS tblname, tbl.reltuples, tbl.relpages AS heappages, coalesce(toast.relpages, 0) AS toastpages, coalesce(toast.reltuples, 0) AS toasttuples, coalesce(substring( array_to_string(tbl.reloptions, ' ') FROM 'fillfactor=([0-9]+)')::smallint, 100) AS fillfactor, current_setting('block_size')::numeric AS bs, CASE WHEN version()~'mingw32' OR version()~'64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS ma, 24 AS page_hdr, 23 + CASE WHEN MAX(coalesce(s.null_frac,0)) > 0 THEN ( 7 + count(s.attname) ) / 8 ELSE 0::int END + CASE WHEN bool_or(att.attname = 'oid' and att.attnum < 0) THEN 4 ELSE 0 END AS tpl_hdr_size, sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 0) ) AS tpl_data_size, bool_or(att.atttypid = 'pg_catalog.name'::regtype) OR sum(CASE WHEN att.attnum > 0 THEN 1 ELSE 0 END) <> count(s.attname) AS is_na FROM pg_attribute AS att JOIN pg_class AS tbl ON att.attrelid = tbl.oid JOIN pg_namespace AS ns ON ns.oid = tbl.relnamespace LEFT JOIN pg_stats AS s ON s.schemaname=ns.nspname AND s.tablename = tbl.relname AND s.inherited=false AND s.attname=att.attname LEFT JOIN pg_class AS toast ON tbl.reltoastrelid = toast.oid WHERE NOT att.attisdropped AND tbl.relkind in ('r','m') GROUP BY 1,2,3,4,5,6,7,8,9,10 ORDER BY 2,3 ) AS s ) AS s2 ) AS s3 where not is_na and tblpages-est_tblpages_ff>0 ORDER BY 6 desc limit 20; select '
Bloated tables (estimated percentage)
Table', 'Fillfactor', 'Table Size', 'HR Size', 'Bloat', 'HR Bloat', 'Bloat%' as info; SELECT '
'||schemaname||'.'||tblname, ''||fillfactor, ''||bs*tblpages AS real_size, ''||pg_size_pretty(bs*tblpages) as HR_size, '', CASE WHEN tblpages - est_tblpages_ff > 0 THEN (tblpages-est_tblpages_ff)*bs ELSE 0 END AS bloat_size, '', CASE WHEN tblpages - est_tblpages_ff > 0 THEN pg_size_pretty( ((tblpages-est_tblpages_ff)*bs)::bigint) ELSE '0' END AS hr_bloat_size, '', CASE WHEN tblpages > 0 AND tblpages - est_tblpages_ff > 0 THEN round(100*(tblpages - est_tblpages_ff)/tblpages::float) ELSE 0 END, '%' FROM ( SELECT ceil( reltuples / ( (bs-page_hdr)*fillfactor/(tpl_size*100) ) ) + ceil( toasttuples / 4 ) AS est_tblpages_ff, tblpages, fillfactor, bs, tblid, schemaname, tblname, heappages, toastpages, is_na FROM ( SELECT ( 4 + tpl_hdr_size + tpl_data_size + (2*ma) - CASE WHEN tpl_hdr_size%ma = 0 THEN ma ELSE tpl_hdr_size%ma END - CASE WHEN ceil(tpl_data_size)::int%ma = 0 THEN ma ELSE ceil(tpl_data_size)::int%ma END ) AS tpl_size, bs - page_hdr AS size_per_block, (heappages + toastpages) AS tblpages, heappages, toastpages, reltuples, toasttuples, bs, page_hdr, tblid, schemaname, tblname, fillfactor, is_na FROM ( SELECT tbl.oid AS tblid, ns.nspname AS schemaname, tbl.relname AS tblname, tbl.reltuples, tbl.relpages AS heappages, coalesce(toast.relpages, 0) AS toastpages, coalesce(toast.reltuples, 0) AS toasttuples, coalesce(substring( array_to_string(tbl.reloptions, ' ') FROM 'fillfactor=([0-9]+)')::smallint, 100) AS fillfactor, current_setting('block_size')::numeric AS bs, CASE WHEN version()~'mingw32' OR version()~'64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS ma, 24 AS page_hdr, 23 + CASE WHEN MAX(coalesce(s.null_frac,0)) > 0 THEN ( 7 + count(s.attname) ) / 8 ELSE 0::int END + CASE WHEN bool_or(att.attname = 'oid' and att.attnum < 0) THEN 4 ELSE 0 END AS tpl_hdr_size, sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 0) ) AS tpl_data_size, bool_or(att.atttypid = 'pg_catalog.name'::regtype) OR sum(CASE WHEN att.attnum > 0 THEN 1 ELSE 0 END) <> count(s.attname) AS is_na FROM pg_attribute AS att JOIN pg_class AS tbl ON att.attrelid = tbl.oid JOIN pg_namespace AS ns ON ns.oid = tbl.relnamespace LEFT JOIN pg_stats AS s ON s.schemaname=ns.nspname AND s.tablename = tbl.relname AND s.inherited=false AND s.attname=att.attname LEFT JOIN pg_class AS toast ON tbl.reltoastrelid = toast.oid WHERE NOT att.attisdropped AND tbl.relkind in ('r','m') GROUP BY 1,2,3,4,5,6,7,8,9,10 ORDER BY 2,3 ) AS s ) AS s2 ) AS s3 where not is_na and tblpages-est_tblpages_ff>0 and tblpages>2 ORDER BY 10 desc limit 5; select '

' as info; select '

' as info; select '
Database max age
Database', 'Max XID age', '% Wraparound' as info; SELECT '
'||datname||'', age(datfrozenxid), '', (age(datfrozenxid)::numeric/2000000000*100)::numeric(4,2) as "% Wraparound" FROM pg_database ORDER BY 2 DESC; select '

' as info; select '

' as info; select '
Relations too aged
Schema', 'Relation', 'XID age' as info; SELECT '
'|| nspname ||''|| relname ||'', age(relfrozenxid) FROM pg_class JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid WHERE relkind = 'r' AND age(relfrozenxid)> 2^28 ORDER by 2 DESC; select '


' as info; select '

' as info; select '

' as info; select '
Users/Roles
Role', 'Login', 'Inherit', 'Superuser', 'Expiry time', 'Config' as info; select '
'||rolname, ''||rolcanlogin, ''||rolinherit, ''||rolsuper, '',rolvaliduntil, ''||rolconfig::text from pg_roles order by rolcanlogin desc, rolname; select '
TOTAL Users', ''||count(*) from pg_roles where rolcanlogin; select '
TOTAL Roles', ''||count(*) from pg_roles where not rolcanlogin; select '

' as info; select '

' as defaultpw; select '

' as info; select '
Users with poor password
Username','Password', 'Note' ; select '
',usename, '', passwd, 'Weak password' from pg_shadow where substr(passwd,4) in ( md5('postgres'||usename), md5('mypass'|| usename), md5('admin'|| usename), md5('secret'|| usename), md5('root'|| usename), md5('password'|| usename), md5('public'|| usename), md5('private'|| usename), md5('1234'|| usename), md5('secure'|| usename), md5('pass'|| usename), md5('qwerty'|| usename), md5('pippo'|| usename), md5('manager'|| usename), md5('changeme'|| usename), md5('pwd'|| usename), md5('changeme'|| usename), md5('xxx'|| usename), md5('toor'|| usename), md5('supervisor'|| usename)) order by usename; select '
',usename, '', passwd, 'Same as user' from pg_shadow where substr(passwd,4) = md5(usename||usename) order by usename; select '
',usename, '', passwd, 'Empty password' from pg_shadow where passwd is null order by usename; select '
',usename, '', passwd, 'Unencrypted password' from pg_shadow where passwd not like 'md5%' and passwd not like 'SCRAM%' order by usename; select '

'; select '

'; select '

' as info; select '
HBA Rules
Type','Database', 'User', 'Address', 'Netmask', 'Auth', 'Options', 'Error'; select '
',type, '',database, '',user_name, '',address, '',netmask, '',auth_method, '',options, '',error from pg_hba_file_rules order by line_number; select '

'; select '

'; select '

' as info; select '
PGAudit logged Objects
Schema', 'Table', 'Privilege'; SELECT '
',table_schema, '',table_name, '',privilege_type FROM information_schema.role_table_grants WHERE grantee in ('rds_pgaudit', 'auditor', 'pgaudit') ORDER BY table_schema, table_name; select '


'; select '

' as info; select '

'; select '
' as info; select '
Per-User Sessions
User', 'Database', 'Count', 'Active' ; select '
',usename, '',datname, '', count(*), '', sum(case when state='active' then 1 else 0 end) from pg_stat_activity group by usename, datname order by 6 desc, 1; select '
TOTAL (', count(distinct usename), ' distinct users)'|| count(*) from pg_stat_activity; select '
' as info; select '
' as info; select '
Per-Host Sessions
User', 'Database', 'Count', 'Active' ; select '
', client_addr, '',datname, '', count(*), '', sum(case when state='active' then 1 else 0 end) from pg_stat_activity group by client_addr, datname order by 6 desc, 1 limit 20; select '
TOTAL (', count(distinct client_addr), ' distinct clients)'|| count(*) from pg_stat_activity; select '
' as info; select '

' as info; select '
Sessions
Pid', 'Database', 'User', 'Address', 'State', 'Query start', 'Backend', 'SQL' ; select '
',pid, '',datname, '',usename, '',client_addr, '',state, '',query_start, '',backend_type, '',query from pg_stat_activity where pid<>pg_backend_pid() order by state, pid; select '


' as info; select '

' as info; select '

' as info; select '
Locks
Pid', 'Type', 'Database', 'Relation', 'Mode', 'Granted' as info; select '
',pid, '',locktype, '', datname, '', relname, '',mode, '',granted from pg_locks l left join pg_catalog.pg_database d on d.oid = l.database left join pg_catalog.pg_class r on r.oid = l.relation order by granted, pid limit 50; select '
...'; select '

' as info; select '

' as info; select '

' as info; select '
Blocking Locks
Blocked Pid', 'Blocked User', 'Blocking Pid', 'Blocking User', ' Blocked Statement', ' Blocking Statement' as info; SELECT '
',blocked_locks.pid AS blocked_pid, '',blocked_activity.usename AS blocked_user, '',blocking_locks.pid AS blocking_pid, '',blocking_activity.usename AS blocking_user, '',blocked_activity.query AS blocked_statement, '',blocking_activity.query AS current_statement_in_blocking_process FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid != blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid WHERE NOT blocked_locks.GRANTED; select '


' as info; select '

' as info; select '

' as info; select '
Memory
Element', 'KB', 'Description' as info; select '
'||name, ''||setting::int*8, ''||short_desc from pg_settings where name like '%buffers'; select '
'||name, ''||setting::int, ''||short_desc from pg_settings where name like '%mem'; select '

' as info; select '

' as info; select '
Buffer Cache Contents
Class','Kind', 'Buffers', 'Buffered', 'Buffers%', 'Relation%', 'Avg. usage' as info; SELECT '
', c.relname, '', c.relkind, '', count(*), '', pg_size_pretty(count(*) * 8192), '', round(100.0 * count(*)/(SELECT setting FROM pg_settings WHERE name='shared_buffers')::integer,1), '', round(100.0 * count(*) * 8192 / pg_relation_size(c.oid),1), '', round(avg(usagecount),2) FROM pg_class c INNER JOIN pg_buffercache b ON b.relfilenode = c.relfilenode INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database()) WHERE pg_relation_size(c.oid) > 0 GROUP BY c.oid, c.relname, c.relkind ORDER BY 6 DESC LIMIT 20; select '

' as info; SELECT 'Buffer Cache size: ', pg_size_pretty(setting::bigint*8192::bigint) FROM pg_settings WHERE name='shared_buffers'; SELECT '
Estimated Minimal Buffer Cache size: ', pg_size_pretty(count(*) * 8192) FROM pg_buffercache WHERE usagecount >= 3; select '


' as info; select '

' as info; select '

Database Statistics' as info; select '
Database', 'Backends', 'Commit', 'TPS', 'Rollback', 'Read', 'Hit', 'Hit Ratio%', 'Return', 'Fetch', 'Insert', 'Update', 'Delete' as info; select '
'||datname, ''||numbackends, ''||xact_commit, ''||round(xact_commit/EXTRACT( EPOCH FROM (now()-stats_reset))::decimal,2), ''||xact_rollback, ''||blks_read, ''||blks_hit, ''||round((blks_hit)*100.0/nullif(blks_read+blks_hit, 0),2) hit_ratio, ''||tup_returned, ''||tup_fetched, ''||tup_inserted, ''||tup_updated, ''||tup_deleted from pg_stat_database where datname not like 'template%'; select '

' as info; select '

BG Writer statistics' as info; select '
checkpoints_timed', ' checkpoints_req ', ' buffers_checkpoint ', ' buffers_clean ', ' maxwritten_clean ', ' buffers_backend ', ' buffers_alloc ', ' Minutes between checkpoints '; select '
'||checkpoints_timed, ''|| checkpoints_req, ''|| buffers_checkpoint, ''|| buffers_clean, ''|| maxwritten_clean, ''|| buffers_backend, ''|| buffers_alloc from pg_stat_bgwriter; select ''|| seconds_since_start / total_checkpoints / 60 AS mbc from (SELECT EXTRACT(EPOCH FROM (now() - pg_postmaster_start_time())) AS seconds_since_start, (checkpoints_timed+checkpoints_req) AS total_checkpoints FROM pg_stat_bgwriter ) AS sub; select '

' as info; select '

Cache statistics' as info; select '
Object Type#Read', ' #Hit ', ' Hit Ratio% '; SELECT '
Table', ''||sum(heap_blks_read) as heap_read, ''||sum(heap_blks_hit) as heap_hit, ''||trunc(100*sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)),2) as ratio FROM pg_statio_user_tables; SELECT '
Index', ''||sum(idx_blks_read) as idx_read, ''||sum(idx_blks_hit) as idx_hit, ''||trunc(100*(sum(idx_blks_hit) - sum(idx_blks_read)) / nullif(sum(idx_blks_hit),0),2) as ratio FROM pg_statio_user_indexes; select '

' as info; SELECT cast(current_setting('server_version_num')as integer)>= 110000 as version_11p \gset var_ SELECT cast(current_setting('server_version_num')as integer)>= 120000 as version_12p \gset var_ SELECT cast(current_setting('server_version_num')as integer)>= 130000 as version_13p \gset var_ SELECT cast(current_setting('server_version_num')as integer)>= 140000 as version_14p \gset var_ SELECT cast(current_setting('server_version_num')as integer)>= 150000 as version_15p \gset var_ SELECT cast(current_setting('server_version_num')as integer)>= 160000 as version_16p \gset var_ select '

' as info; select '

Statement statistics' as info; select '
Query', 'User', 'Calls', 'Average (sec.)', 'Max (sec.)', 'Total Time', 'I/O Time', 'Rows', 'Hit Ratio%' as info; \if :var_version_13p SELECT 'WAL MB'; SELECT '
'||replace(query,',',', '), ' '||pg_get_userbyid(userid), ''||calls, ''||round((total_exec_time::numeric / calls::numeric)/1000,3), ''||round((max_exec_time::numeric::numeric)/1000,3), ''||round(total_exec_time), ''||round(blk_read_time+blk_write_time), ''||rows, ''||round((100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0)),2) AS hit_percent, ''||round((wal_bytes::numeric::numeric)/(1024*1024),0) FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20; \else SELECT '
'||replace(query,',',', '), ' '||pg_get_userbyid(userid), ''||calls, ''||round((total_time::numeric / calls::numeric)/1000,3), ''||round((max_time::numeric::numeric)/1000,3), ''||round(total_time), ''||round(blk_read_time+blk_write_time), ''||rows, ''||round((100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0)),2) AS hit_percent FROM pg_stat_statements ORDER BY total_time DESC LIMIT 20; \endif select '

' as info; \if :var_version_14p SELECT '

Database restart: '|| pg_postmaster_start_time(), '
Statistics reset: '|| stats_reset, ' Dealloc: '|| dealloc FROM pg_stat_statements_info; \endif select '

' as info; select '

Slowest Statements' as info; select '
Query', 'User', 'Calls', 'Average (sec.)', 'Total Time', 'I/O Time', 'Rows', 'Hit Ratio%' as info; \if :var_version_13p SELECT 'WAL MB'; SELECT '
'||replace(query,',',', '), ' '||pg_get_userbyid(userid), ''||calls, ''||round((total_exec_time::numeric / calls::numeric)/1000,3), ''||round(total_exec_time), ''||round(blk_read_time+blk_write_time), ''||rows, ''||round((100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0)),2) AS hit_percent, ''||round((wal_bytes::numeric::numeric)/(1024*1024),0) FROM pg_stat_statements WHERE pg_get_userbyid(userid) not in ('enterprisedb') -- Comment if needed ORDER BY (total_exec_time::numeric/calls::numeric) DESC LIMIT 10; \else SELECT '
'||replace(query,',',', '), ' '||pg_get_userbyid(userid), ''||calls, ''||round((total_time::numeric / calls::numeric)/1000,3), ''||round(total_time), ''||round(blk_read_time+blk_write_time), ''||rows, ''||round((100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0)),2) AS hit_percent FROM pg_stat_statements WHERE pg_get_userbyid(userid) not in ('enterprisedb') -- Comment if needed ORDER BY (total_time::numeric/calls::numeric) DESC LIMIT 10; \endif select '

' as info; select '

' as info; select '
Database', 'DBcpu', 'IOcpu' as info; \if :var_version_13p \if :var_version_14p select '
', datname, ' ', round(sum( (total_exec_time)/(EXTRACT(EPOCH FROM (now()-stats_reset))*1000) )::numeric,5) DBcpu, ' ', round(sum( (blk_read_time+blk_write_time)/(EXTRACT(EPOCH FROM (now()-stats_reset))*1000) )::numeric,5) IOcpu from pg_stat_statements, pg_database, pg_stat_statements_info where pg_stat_statements.dbid=pg_database.oid group by datname; select '
TOTAL', round(sum( (total_exec_time)/(EXTRACT(EPOCH FROM (now()-stats_reset))*1000) )::numeric,5) DBcpu, ' ', round(sum( (blk_read_time+blk_write_time)/(EXTRACT(EPOCH FROM (now()-stats_reset))*1000) )::numeric,5) IOcpu from pg_stat_statements, pg_stat_statements_info; \else select '
', datname, ' ', round(sum( (total_exec_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) DBcpu, ' ', round(sum( (blk_read_time+blk_write_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) IOcpu from pg_stat_statements, pg_database where pg_stat_statements.dbid=pg_database.oid group by datname; select '
TOTAL', round(sum( (total_exec_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) DBcpu, ' ', round(sum( (blk_read_time+blk_write_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) IOcpu from pg_stat_statements; \endif \else select '
', datname, ' ', round(sum( (total_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) DBcpu, ' ', round(sum( (blk_read_time+blk_write_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) IOcpu from pg_stat_statements, pg_database where pg_stat_statements.dbid=pg_database.oid group by datname; select '
TOTAL', round(sum( (total_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) DBcpu, ' ', round(sum( (blk_read_time+blk_write_time)/(EXTRACT(EPOCH FROM (now()-pg_postmaster_start_time()))*1000) )::numeric,5) IOcpu from pg_stat_statements; \endif select '

' as info; select '

Tables statistics' as info; select '
SchemaTable', 'Seq. Readed Tuples', 'Idx. Readed Tuples', 'Sequential Scan', 'Index Scan', 'Insert', 'Update', 'Hot Update', 'Delete', 'Index Usage Ratio%', 'HOT Update Ratio%'; select '
'||schemaname, ''||relname, ''||coalesce(seq_tup_read, 0), ''||coalesce(idx_tup_fetch, 0), ''||coalesce(seq_scan, 0), ''||coalesce(idx_scan, 0), ''||coalesce(n_tup_ins, 0), ''||coalesce(n_tup_upd, 0), ''||coalesce(n_tup_hot_upd, 0), ''||coalesce(n_tup_del, 0), ''||coalesce(idx_scan*100/nullif(idx_scan+seq_scan,0), -1) as idx_hit_ratio, ''||coalesce(n_tup_hot_upd*100/nullif(n_tup_upd,0), -1) as hot_hit_ratio from pg_stat_user_tables order by (coalesce(seq_tup_read,0) +coalesce(idx_tup_fetch,0) +coalesce(n_tup_ins,0) + coalesce(n_tup_upd,0) +coalesce(n_tup_del,0)) desc limit 20; select '

' as info; select '

Tables caching' as info; select '
SchemaTable', 'Heap Reads', 'Index Reads', 'TOAST Reads', 'Heap Hit Ratio%', 'Index Hit Ratio%', 'TOAST Hit Ratio%'; select '
'||schemaname, ''||relname, ''||coalesce(heap_blks_read, 0), ''||coalesce(idx_blks_read, 0), ''||coalesce(toast_blks_read, 0), ''||coalesce(heap_blks_hit*100/nullif(heap_blks_read+heap_blks_hit,0), -1) as tb_hit_ratio, ''||coalesce(idx_blks_hit*100/nullif(idx_blks_read+idx_blks_hit,0), -1) as idx_hit_ratio, ''||coalesce(toast_blks_hit*100/nullif(toast_blks_read+toast_blks_hit,0), -1) as toast_hit_ratio from pg_statio_user_tables where heap_blks_read>0 order by heap_blks_read desc limit 20; select '

' as info; select '

' as info;
select '

' as info; select '
Partitioning Details
SchemaOwner', ' Partitioned Object', ' Partition', ' Expression', ' Tuples'; select '
'||nspname, ''||rolname, '', t.relname, '', p.relname, '', pg_get_expr(p.relpartbound, p.oid, true), '', p.reltuples, ' / ', t.reltuples from pg_class t, pg_inherits i, pg_class p, pg_roles r, pg_namespace n where i.inhparent = t.oid and p.oid = i.inhrelid and t.relowner=r.oid and t.relnamespace=n.oid and p.relkind='r'; select '

' as info; select '

Index Usage - Details
' ; select '

Per-table index usage' as info; select '
RelationIndex Usage%', ' #Rows ', ' #Scan ', ' #Seq. tuples '; SELECT '
', relname, '', 100 * idx_scan / (seq_scan + idx_scan) index_used_pct, '', n_live_tup, '', seq_scan, '', seq_tup_read FROM pg_stat_user_tables WHERE seq_scan + idx_scan > 1 AND 100 * idx_scan / (seq_scan + idx_scan) < 95 ORDER BY n_live_tup DESC LIMIT 32; select '

' as info; select '

Invalid indexes' as info; select '
Schema', 'Index'; SELECT '
'|| nspname ||''|| relname FROM pg_class, pg_index, pg_namespace WHERE pg_index.indisvalid = false AND pg_class.relnamespace = pg_namespace.oid AND pg_index.indexrelid = pg_class.oid; select '

' as info; select '

Missing indexes' as info; select '
SchemaRelation', 'ContraintIssue', '#Table ScanParent', 'Columns'; WITH fk_actions ( code, action ) AS ( VALUES ( 'a', 'error' ), ( 'r', 'restrict' ), ( 'c', 'cascade' ), ( 'n', 'set null' ), ( 'd', 'set default' ) ), fk_list AS ( SELECT pg_constraint.oid as fkoid, conrelid, confrelid as parentid, conname, relname, nspname, fk_actions_update.action as update_action, fk_actions_delete.action as delete_action, conkey as key_cols FROM pg_constraint JOIN pg_class ON conrelid = pg_class.oid JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid JOIN fk_actions AS fk_actions_update ON confupdtype = fk_actions_update.code JOIN fk_actions AS fk_actions_delete ON confdeltype = fk_actions_delete.code WHERE contype = 'f' ), fk_attributes AS ( SELECT fkoid, conrelid, attname, attnum FROM fk_list JOIN pg_attribute ON conrelid = attrelid AND attnum = ANY( key_cols ) ORDER BY fkoid, attnum ), fk_cols_list AS ( SELECT fkoid, array_agg(attname) as cols_list FROM fk_attributes GROUP BY fkoid ), index_list AS ( SELECT indexrelid as indexid, pg_class.relname as indexname, indrelid, indkey, indpred is not null as has_predicate, pg_get_indexdef(indexrelid) as indexdef FROM pg_index JOIN pg_class ON indexrelid = pg_class.oid WHERE indisvalid ), fk_index_match AS ( SELECT fk_list.*, indexid, indexname, indkey::int[] as indexatts, has_predicate, indexdef, array_length(key_cols, 1) as fk_colcount, array_length(indkey,1) as index_colcount, round(pg_relation_size(conrelid)/(1024^2)::numeric) as table_mb, cols_list FROM fk_list JOIN fk_cols_list USING (fkoid) LEFT OUTER JOIN index_list ON conrelid = indrelid AND (indkey::int2[])[0:(array_length(key_cols,1) -1)] @> key_cols ), fk_perfect_match AS ( SELECT fkoid FROM fk_index_match WHERE (index_colcount - 1) <= fk_colcount AND NOT has_predicate AND indexdef LIKE '%USING btree%' ), fk_index_check AS ( SELECT 'no index' as issue, *, 1 as issue_sort FROM fk_index_match WHERE indexid IS NULL UNION ALL SELECT 'questionable index' as issue, *, 2 FROM fk_index_match WHERE indexid IS NOT NULL AND fkoid NOT IN ( SELECT fkoid FROM fk_perfect_match) ), parent_table_stats AS ( SELECT fkoid, tabstats.relname as parent_name, (n_tup_ins + n_tup_upd + n_tup_del + n_tup_hot_upd) as parent_writes, round(pg_relation_size(parentid)/(1024^2)::numeric) as parent_mb FROM pg_stat_user_tables AS tabstats JOIN fk_list ON relid = parentid ), fk_table_stats AS ( SELECT fkoid, (n_tup_ins + n_tup_upd + n_tup_del + n_tup_hot_upd) as writes, seq_scan as table_scans FROM pg_stat_user_tables AS tabstats JOIN fk_list ON relid = conrelid ) SELECT '
', nspname as schema_name, '', relname as table_name, '', conname as fk_name, '', issue, '', table_scans, '', parent_name, '', cols_list FROM fk_index_check JOIN parent_table_stats USING (fkoid) JOIN fk_table_stats USING (fkoid) WHERE table_mb > 5 AND ( writes > 1000 OR parent_writes > 1000 OR parent_mb > 10 ) ORDER BY table_scans DESC, table_mb DESC, table_name, fk_name LIMIT 64; select '
' as info; select '

Unused indexes' as info; select '
SchemaTable', 'Index', 'Size'; SELECT '
',s.schemaname, '',s.relname, '',s.indexrelname, '',pg_relation_size(s.indexrelid) FROM pg_catalog.pg_stat_user_indexes s JOIN pg_catalog.pg_index i ON s.indexrelid = i.indexrelid WHERE s.idx_scan = 0 AND 0 <>ALL (i.indkey) AND NOT i.indisunique AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_constraint c WHERE c.conindid = s.indexrelid) ORDER BY pg_relation_size(s.indexrelid) DESC LIMIT 64; select '

' as info; select '

All indexes DDLs (up to 1000)' as info; select '
SchemaRelation', 'IndexDDL'; SELECT '
',schemaname, '',tablename, '',indexname, '',indexdef FROM pg_indexes WHERE schemaname not in ('pg_catalog') AND tablename not like 'pgstatspack%' ORDER BY schemaname, tablename, indexname LIMIT 1000; select '


' as info; select '

' as info; select '
Tuning Parameters
Parameter', 'Value', 'Min', 'Max', 'Unit', 'Description' as info; select '
',name,'',replace(replace(setting,'<','<'),'>','>'), '',min_val,'',max_val, '',unit, '',short_desc from pg_settings where name in ('max_connections','shared_buffers','effective_cache_size','work_mem', 'wal_buffers', 'checkpoint_completion_target', 'checkpoint_segments', 'synchronous_commit', 'wal_writer_delay', 'max_fsm_pages','fsync','commit_delay','commit_siblings','random_page_cost', 'checkpoint_timeout', 'max_wal_size', 'bgwriter_lru_maxpages', 'bgwriter_lru_multiplier', ' bgwriter_delay', 'autovacuum_vacuum_cost_limit', 'autovacuum_vacuum_cost_delay') order by name; select '


' as info; select '

' as info; select '

' as info; select '
Biggest Objects
Object', 'Type', 'Owner', 'Schema', 'Bytes', 'Rows' as info; select '
'||relname, ''||case WHEN relkind='r' THEN 'Table' WHEN relkind='i' THEN 'Index' WHEN relkind='t' THEN 'TOAST Table' ELSE relkind||'' end, ''||rolname, ''||n.nspname, ''||to_char(relpages::INT8*8*1024,'999G999G999G999G999G999G999'), ''||to_char(reltuples,'999G999G999G999G999G999G999') from pg_class, pg_roles, pg_catalog.pg_namespace n where relowner=pg_roles.oid and n.oid=pg_class.relnamespace order by relpages desc, reltuples desc limit 32; select '

' as info; select '

' as info; select '

' as info; select '
Largest TOASTs
TOAST', 'Owner', 'Table', 'Bytes', 'Chunks' as info; select '
'||t.relname, ''||rolname, ''||n.nspname||'.'||r.relname, ''||to_char(pg_relation_size(t.oid),'999G999G999G999G999G999G999'), ''||to_char(t.reltuples,'999G999G999G999G999G999G999') from pg_class t, pg_roles, pg_catalog.pg_namespace n, pg_class r where t.relowner=pg_roles.oid and n.oid=r.relnamespace and r.reltoastrelid = t.oid and t.relkind='t' and t.reltuples>0 order by pg_relation_size(t.oid) desc limit 10; select '


' as info; select '

' as info; select '

' as info; select '
Procedural Languages
Available languages' as info; select '
'||lanname from pg_language; select '

'; select '
PL Objects
Owner', 'Kind', 'Language', 'Count', 'Source size' as info; select '
'||o.rolname, ''||case when f.prokind='f' then 'Function' when f.prokind='a' then 'Aggregate func.' when f.prokind='w' then 'Window func.' when f.prokind='p' then 'Procedure' else 'Other' end, ''||l.lanname, ''||count(*), ''||sum(char_length(prosrc)) from pg_proc f, pg_roles o, pg_language l where f.proowner=o.oid and f.prolang=l.oid group by o.rolname, l.lanname, prokind order by o.rolname, prokind, l.lanname; select '

' as info; -- regexp_split_to_table(prosrc, E'\n') select '

' as info; select '

Data Types - Details
' ; select '

' as info; select '
Data Types
Owner', 'Data type', 'Count' as info; select '
'||o.rolname, ''||t.typname, ''||count(*) from pg_attribute a, pg_class r, pg_roles o, pg_type t where a.attrelid=r.oid and a.atttypid=t.oid and r.relowner=o.oid and o.rolname not in ('enterprisedb', 'postgres') group by o.rolname, t.typname order by o.rolname, t.typname; select '


' as info; select '

' as info; select '

' as info; select '
Physical Backup
Parameter', 'Value' as info; select '
',name,'',setting from pg_settings where name in ('archive_mode', 'archive_timeout') order by name; select '
Write xlog location', ''||pg_current_wal_lsn(); select '
Insert xlog location', ''||pg_current_wal_insert_lsn(); select '

' as info; select '

' as info; select '

' as info; select '
Archiver Statistics
Archived Count ', ' Last Archived WAL ', ' Last Archived Time ', ' Failed Count ', ' Last Failed WAL ', ' Last Failed Time ', ' Statistics Reset ', ' Archiving ', ' WALS ps ' as info; select '
',archived_count, '',last_archived_wal, '',last_archived_time, '',failed_count, '',last_failed_wal, '',last_failed_time, '',stats_reset, '', current_setting('archive_mode')::BOOLEAN AND (last_failed_wal IS NULL OR last_failed_wal <= last_archived_wal), '', CAST (archived_count AS NUMERIC) / EXTRACT (EPOCH FROM age(now(), stats_reset)) from pg_stat_archiver; select '


' as info; select '

' as info; select '

' as info; select '
Replication
Parameter', 'Value' as info; select '
In Recovery Mode', ''||pg_is_in_recovery()||''; select '
',name,'',replace(replace(setting,'<','<'),'>','>') from pg_settings where name in ('wal_level', 'archive_command', 'hot_standby', 'max_wal_senders', 'checkpoint_segments', 'max_wal_size', 'archive_mode', 'max_standby_archive_delay', 'max_standby_streaming_delay', 'hot_standby_feedback', 'wal_keep_segments', 'wal_keep_size', 'synchronous_standby_names', 'recovery_target_timeline', 'wal_receiver_create_temp_slot', 'max_slot_wal_keep_size', 'ignore_invalid_pages', 'primary_slot_name', 'primary_conninfo', 'max_slot_wal_keep_size', 'vacuum_defer_cleanup_age') order by name; select '

' as info; select '

' ; select '
Master Statistics
Client', 'State', 'Sync', 'Current Snapshot', 'Sent loc.', 'Write loc.', 'Flush loc.', 'Replay loc.', 'Backend Start'; select 'Write lag', 'Flush lag', 'Replay lag'; select '
',client_addr, '', state, '', sync_state, '', txid_current_snapshot(), '', sent_lsn, '',write_lsn, '',flush_lsn, '',replay_lsn, '', backend_start, '',write_lag, '',flush_lag, '',replay_lag from pg_stat_replication; select '
' as info; select '

' ; select '
Replication Slots
Name', 'Type', 'Active', 'XMIN', 'Catalog XMIN', 'Restart LSN'; select '
',slot_name, '', slot_type, '', active, '', xmin, '', catalog_xmin, '', restart_lsn from pg_replication_slots; select '
' as info; select '

' ; select '
Slave Statistics
Last Replication','Replication Delay','Current Snapshot', 'Receive loc.','Replay loc.'; select '
', now() - pg_last_xact_replay_timestamp(), '', CASE WHEN pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn() THEN 0 ELSE EXTRACT (EPOCH FROM now() - pg_last_xact_replay_timestamp()) END, '', case when pg_is_in_recovery() then txid_current_snapshot() else null end, '', pg_last_wal_receive_lsn(), '', pg_last_wal_replay_lsn(); select '

' as info; select '

' ; select '
WAL Receiver
PID','Status','Connection', 'Latest LSN','Latest time'; select '
',pid, '', status, '', conninfo, '', latest_end_lsn, '', latest_end_time from pg_stat_wal_receiver; select '

' as info; select '

' ; select '
Logical Replication - Subscriptions
Subscription Name','Pid','Relation OID', 'Received','Last Message Send','Last Message Receipt', 'Latest location','Latest time'; select '
',subname, '',pid, '',relid, '',received_lsn, '',last_msg_send_time, '',last_msg_receipt_time, '',latest_end_lsn, '',latest_end_time from pg_stat_subscription order by subname; select '

' as info; select '

Logical Replication - Details
' ; select '

' ; select '
Publications
Publication Name','Owner', 'All tables', 'Insert', 'Update', 'Delete'; select '
',pubname, '',rolname, '',puballtables, '', pubinsert, '', pubupdate, '', pubdelete from pg_publication p, pg_roles a where a.oid=p.pubowner order by pubname; select '
Publication Name','Schema','Table'; select '
',pubname, '',schemaname, '',tablename from pg_publication_tables order by pubname, tablename; select '

'; select '

' ; select '
Subscriptions
Subscription Name','Database','Owner', 'Enabled', 'Sync. Commit', ' Slot ', ' Connection '; select '
',subname, '',datname, '',rolname, '',subenabled, '', subsynccommit, '', subslotname, '', subconninfo from pg_subscription s, pg_database d, pg_roles a where d.oid=s.subdbid and a.oid=s.subowner order by subname; select '
Subscription Name','Schema','Table', 'State', 'LSN'; select '
',subname, '', '',relname, '',srsubstate, '', srsublsn from pg_subscription_rel r, pg_subscription s, pg_class c where s.oid=r.srsubid and c.oid=r.srrelid order by subname, relname; select '

'; select '


'; select '

' as info; select '

' as info; select '
Extensions
Name', 'Default Version', 'Installed Version', 'Description' as info; select '
',name,'',default_version,'',installed_version,'',comment from pg_available_extensions order by case when installed_version is null then 1 else 0 end, name; select '
postgisPostGIS installed (pre-extensions check)' pg from pg_proc where proname='postgis_version'; select '


' as info; select '

' as info; select '

' as info; select '
NLS Settings
Parameter', 'Value', 'Description' as info; select '
',name,'',setting, '',short_desc from pg_settings where name like 'lc%' order by name; select '


' as info; select '

' as info; select '

' as info; select '
Configured Parameters
Parameter', 'Value', 'Description', 'Source' as info; select '
',name,'', replace(replace( case when unit='kB' then pg_size_pretty(setting::bigint*1024) when unit='8kB' then pg_size_pretty(setting::bigint*1024*8) when unit='B' then pg_size_pretty(setting::bigint) when unit='MB' then pg_size_pretty(setting::bigint*1024*1024) else coalesce(setting||' '||unit,setting) end, '<','<'),'>','>'), '',short_desc, '',source from pg_settings where source not in ('default', 'override', 'client') order by name; select '

' as info; select '

' as info; select '

' as info; select '
PostgreSQL Parameters
Parameter', 'Value', 'Min', 'Max', 'Description', 'Category', 'Context', 'Source' as info; select '
',name,'', replace(replace( case when unit='kB' then pg_size_pretty(setting::bigint*1024) when unit='8kB' then pg_size_pretty(setting::bigint*1024*8) when unit='B' then pg_size_pretty(setting::bigint) when unit='MB' then pg_size_pretty(setting::bigint*1024*1024) else coalesce(setting||' '||unit,setting) end, '<','<'),'>','>'), '',min_val,'',max_val, '',short_desc, '',category, '',context, '',source from pg_settings order by name; select '


' as info; select '

' as info; select '

'; select '
HBA file

' as info;
select pg_read_file('pg_hba.conf',1,10240);
select '


' as info; -- SELECT * from pg_catalog.pg_read_file('pg_hba.conf'); -- WITH f(name) AS (VALUES('pg_hba.conf')) -- SELECT pg_catalog.pg_read_file(name, 0, (pg_catalog.pg_stat_file(name)).size) FROM f; select '

' as info; select '

'; select '
WAL files (20)

' as info;
\pset tuples_only
\a
select * from pg_ls_waldir() order by modification desc limit 20;

select pg_size_pretty(sum(size)) as WAL_total_size from pg_ls_waldir();
\pset tuples_only
\a
select '


' as info; /* Extensions info dynamic part */ select '

' as info; select '

Optional informations

' as info; SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pg_stat_statements' and installed_version is not null) as pg_stat_statements, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pg_buffercache' and installed_version is not null) as pg_buffercache, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pgstattuple' and installed_version is not null) as pgstattuple, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pg_freespacemap' and installed_version is not null) as pg_freespacemap, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='sslinfo' and installed_version is not null) as sslinfo, EXISTS (SELECT 1 FROM pg_stat_ssl WHERE ssl limit 1) as ssl_active, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pgaudit' and installed_version is not null) as pgaudit, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='pgrowlocks' and installed_version is not null) as pgrowlocks, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='postgis' and installed_version is not null) as postgis, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='edbspl') as edb, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='aurora_stat_utils') as aurora, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='aurora_stat_utils') as aurora_stat, EXISTS (SELECT 1 FROM pg_available_extensions WHERE name='apg_plan_mgmt') as qpm \gset opt_ -- pg_stat_statements are pg_buffercache are too important to be "optional" \if :opt_pgstattuple select '

' as info; select '

'; select '
Bloat detailed informations for biggest tables (can be time expensive: uncomment if needed)

' as info;
\pset tuples_only
\a

\if 0
select relname Relation, (pgstattuple(pg_class.oid::regclass)).*
  from pg_class, pg_roles, pg_catalog.pg_namespace n
 where relowner=pg_roles.oid
   and n.oid=pg_class.relnamespace
   and relkind = 'r'
 order by relpages desc, reltuples desc
 limit 50;
\endif

\pset tuples_only
\a
select '


' as info; \endif \if :opt_sslinfo select '

' as info; select '

'; select '
SSL Informations on current connection
SSL Usage','SSL Version', 'SSL Cipher', 'Client Certificate' as info; SELECT '
', ssl_is_used(), '', ssl_version(), '', ssl_cipher(), '', ssl_client_cert_present(); select '


' as info; \endif \if :opt_ssl_active select '

' as info; select '

'; select '
SSL Informations on all connections
' as info;
select * from pg_stat_ssl;
select '


' as info; \endif \if :var_version_12p select '

' as info; select '

'; select '
Additional PG12+ Statistics
' as info;
\pset tuples_only
\a
select *
  from pg_stats_ext;

SELECT *
  from pg_stat_gssapi;

-- pg_stat_progress_cluster pg_stat_progress_create_index
\pset tuples_only
\a
select '

' as info; \endif \if :var_version_13p select '

' as info; select '

'; select '
Additional PG13+ Statistics
' as info;
\pset tuples_only
\a
select replace(replace(name, '<', '-'), '>', '-') as name, off, size, allocated_size
  from pg_shmem_allocations
 order by allocated_size desc;

SELECT *
  from pg_stat_slru;

--  pg_stat_progress_analyze pg_stat_progress_basebackup
\pset tuples_only
\a
select '

' as info; \endif \if :var_version_14p select '

' as info; select '

'; select '
Additional PG14+ Statistics
' as info;
\pset tuples_only
\a
select *, 
       pg_size_pretty(wal_bytes/extract(epoch from (now()-stats_reset)/3600)) wal_hour
  from pg_stat_wal;

SELECT *
  from pg_backend_memory_contexts
 ORDER BY used_bytes DESC LIMIT 10;

SELECT *
  from pg_stat_replication_slots;

SELECT *
  from pg_stat_statements_info;

SELECT *
  from pg_stats_ext_exprs;

\pset tuples_only
\a
select '


' as info; \endif \if :var_version_15p select '

' as info; select '

'; select '
Additional PG15+ Statistics
' as info;
\pset tuples_only
\a
select *
  from pg_parameter_acl;

--  pg_publication_namespace
\pset tuples_only
\a
select '

' as info; \endif \if :var_version_16p select '

' as info; select '

'; select '
Additional PG16+ Statistics
' as info;
\pset tuples_only
\a
select 'No new system catalog views';
 
\pset tuples_only
\a
select '

' as info; \endif \if :opt_postgis select '

' as info; select '

'; select '
Postgis Statistics

' as info;
\pset tuples_only
\a

select 'PostgreSQL Version: ', version();
select 'PostGIS Version: ', PostGIS_version();
select 'PostGIS Full Version: ', PostGIS_full_version();

SELECT 'Extension: ', name, installed_version 
  FROM pg_available_extensions WHERE name like 'postgis%'
 ORDER BY name;

select 'GIS Objects: ', count(*)
  from geometry_columns;
select 'GiST Indexes: ', count(*)
  from pg_index, pg_class, pg_roles
 where pg_index.indrelid=pg_class.oid
   and relowner=pg_roles.oid
   and upper(pg_get_indexdef(indexrelid)) like '%GIST%';

select 'Projection: ', substr(proj4text, 1,10), count(*)
  from spatial_ref_sys
 group by substr(proj4text, 1,10);

\pset tuples_only
\a
select '

' as info; \endif \if :opt_edb select '

' as info; select '

'; select '
Additional EnterpriseDB Advanced Server Statistics

' as info;
\pset tuples_only
\a

select *
  from all_policies;
select *
  from all_directories;
select *
  from all_db_links;
select *
  from product_component_version;

\if :var_version_14p
select *
  from all_objects
 where schema_name not in ('SYS')
 order by last_ddl_time desc
 limit 20;

 select *
  from all_users
 order by creation_date desc
 limit 20;
\endif

select *
  from session_waits;
select *
  from system_waits;

select *
  from session_waits_history
 limit 50;
select *
  from system_waits_history
 limit 50;

\pset tuples_only
\a
select '


' as info; \endif \if :opt_aurora select '

' as info; select '

'; select '
Additional Aurora Postgres-compatible Statistics

' as info;
\pset tuples_only
\a

select 'Latest Aurora PostgreSQL Releases: 14.6 13.9, 12.13, 11.18; (10.21, 1.11 9.6.22)';
select 'Aurora: '||AURORA_VERSION();

\if :opt_aurora_stat
SELECT *
  FROM aurora_wait_report();
\endif

\if :opt_qpm
SELECT *
  FROM apg_plan_mgmt.dba_plans;
\endif

\pset tuples_only
\a
select '


' as info; \endif select '


' as info; select '

Statistics generated on: '|| current_date || ' ' ||localtime as info; select '
More info on' as info; select 'this site' as info; select 'or contact' as info; select 'Meo.

' as info; \pset tuples_only \a \o