If the C code for the prior dbsize patch is not acceptable for whatever
reason, here's a SQL-based patch to replace it. It's not a drop-in for
7.3/7.4 as I'd hoped, only an 8.1 patch. I believe it is functionally
equivalent to the C patch, but simpler, shorter, and probably a tad slower.
I also removed the README section on how to aggregate since it was
incomplete/incorrect (it didn't count toasted indices) and added a SQL
function that itemizes the size for a relation's table and index data
(helpful to us in identifying bloat, measuring performance, capacity
estimation, etc).
Ed
? contrib/dbsize/README.dbsize.aggregate_relation_components
? contrib/dbsize/dbsize.sql
? contrib/dbsize/dbsize.sql.in.aggregate_relation_components
? contrib/dbsize/dbsize.sql.in.using_pg_relation_size
Index: contrib/dbsize/README.dbsize
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/dbsize/README.dbsize,v
retrieving revision 1.4
diff -C1 -r1.4 README.dbsize
*** contrib/dbsize/README.dbsize 28 Sep 2004 19:35:43 -0000 1.4
--- contrib/dbsize/README.dbsize 28 Jan 2005 17:10:31 -0000
***************
*** 1,3 ****
! This module contains several functions that report the size of a given
! database object:
--- 1,3 ----
! This module contains several functions that report the on-disk size of a
! given database object in bytes:
***************
*** 5,6 ****
--- 5,10 ----
int8 relation_size(text)
+ int8 indexes_size(text)
+ int8 aggregate_relation_size(text)
+
+ setof record relation_size_components(text)
***************
*** 12,14 ****
! The first two functions:
--- 16,20 ----
! The first four functions take the name of the object (possibly
! schema-qualified for the latter three) and returns the size of the
! on-disk files in bytes.
***************
*** 16,20 ****
SELECT relation_size('pg_class');
! take the name of the object (possibly schema-qualified, for relation_size),
! while these functions take object OIDs:
--- 22,32 ----
SELECT relation_size('pg_class');
+ SELECT indexes_size('pg_class');
+ SELECT aggregate_relation_size('pg_class');
+
+ The next function, relation_size_components(), returns a set of rows
+ showing the sizes of the relations constituting the input relation.
+
+ SELECT relation_size_components('pg_class');
! These functions take object OIDs:
***************
*** 24,49 ****
Please note that relation_size and pg_relation_size report only the size of
the selected relation itself; any subsidiary indexes or toast tables are not
! counted. To obtain the total size of a table including all helper files
! you'd have to do something like:
!
! SELECT *,
! pg_size_pretty(tablesize+indexsize+toastsize+toastindexsize) AS totalsize
! FROM
! (SELECT pg_relation_size(cl.oid) AS tablesize,
! COALESCE((SELECT SUM(pg_relation_size(indexrelid))::bigint
! FROM pg_index WHERE cl.oid=indrelid), 0) AS indexsize,
! CASE WHEN reltoastrelid=0 THEN 0
! ELSE pg_relation_size(reltoastrelid)
! END AS toastsize,
! CASE WHEN reltoastrelid=0 THEN 0
! ELSE pg_relation_size((SELECT reltoastidxid FROM pg_class ct
! WHERE ct.oid = cl.reltoastrelid))
! END AS toastindexsize
! FROM pg_class cl
! WHERE relname = 'foo') ss;
!
! This sample query utilizes the helper function pg_size_pretty(int8),
! which formats the number of bytes into a convenient string using KB, MB,
! GB. It is also contained in this module.
--- 36,47 ----
+ The indexes_size() function returns the total size of the indices for a
+ relation, including any toasted indices.
+
+ The aggregate_relation_size() function returns the total size of the relation,
+ all its indices, and any toasted data.
+
Please note that relation_size and pg_relation_size report only the size of
the selected relation itself; any subsidiary indexes or toast tables are not
! counted. To obtain the total size of a table including all helper files,
! use aggregate_relation_size().
Index: contrib/dbsize/dbsize.sql.in
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/dbsize/dbsize.sql.in,v
retrieving revision 1.4
diff -C1 -r1.4 dbsize.sql.in
*** contrib/dbsize/dbsize.sql.in 28 Sep 2004 19:35:43 -0000 1.4
--- contrib/dbsize/dbsize.sql.in 28 Jan 2005 17:10:32 -0000
***************
*** 1,2 ****
! CREATE FUNCTION database_size (name) RETURNS bigint
AS 'MODULE_PATHNAME', 'database_size'
--- 1,2 ----
! CREATE OR REPLACE FUNCTION database_size (name) RETURNS bigint
AS 'MODULE_PATHNAME', 'database_size'
***************
*** 4,10 ****
! CREATE FUNCTION relation_size (text) RETURNS bigint
! AS 'MODULE_PATHNAME', 'relation_size'
! LANGUAGE C STRICT;
!
! CREATE FUNCTION pg_tablespace_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_tablespace_size'
--- 4,6 ----
! CREATE OR REPLACE FUNCTION pg_tablespace_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_tablespace_size'
***************
*** 12,14 ****
! CREATE FUNCTION pg_database_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_database_size'
--- 8,10 ----
! CREATE OR REPLACE FUNCTION pg_database_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_database_size'
***************
*** 16,18 ****
! CREATE FUNCTION pg_relation_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_relation_size'
--- 12,14 ----
! CREATE OR REPLACE FUNCTION pg_relation_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_relation_size'
***************
*** 20,23 ****
! CREATE FUNCTION pg_size_pretty(bigint) RETURNS text
AS 'MODULE_PATHNAME', 'pg_size_pretty'
LANGUAGE C STRICT;
--- 16,132 ----
! CREATE OR REPLACE FUNCTION pg_size_pretty(bigint) RETURNS text
AS 'MODULE_PATHNAME', 'pg_size_pretty'
LANGUAGE C STRICT;
+
+ CREATE OR REPLACE FUNCTION relation_size (text) RETURNS bigint
+ AS 'MODULE_PATHNAME', 'relation_size'
+ LANGUAGE C STRICT;
+
+ CREATE OR REPLACE FUNCTION aggregate_relation_size (text) RETURNS bigint AS '
+ SELECT pg_relation_size(r.oid)
+ + COALESCE(pg_relation_size(t.oid), 0)::bigint
+ + COALESCE(pg_relation_size(ti.oid), 0)::bigint
+ + COALESCE(SUM(pg_relation_size(i.indexrelid)), 0)::bigint
+ + COALESCE(SUM(pg_relation_size(it.oid)), 0)::bigint
+ + COALESCE(SUM(pg_relation_size(iti.oid)), 0)::bigint AS bytes
+ FROM pg_class r
+ LEFT JOIN pg_class t ON (r.reltoastrelid = t.oid)
+ LEFT JOIN pg_class ti ON (t.reltoastidxid = ti.oid)
+ LEFT JOIN pg_index i ON (r.oid = i.indrelid)
+ LEFT JOIN pg_class ir ON (ir.oid = i.indexrelid)
+ LEFT JOIN pg_class it ON (ir.reltoastrelid = it.oid)
+ LEFT JOIN pg_class iti ON (it.reltoastidxid = iti.oid)
+ WHERE r.relname = \$1
+ GROUP BY r.oid, t.oid, ti.oid
+ ' LANGUAGE SQL;
+
+ CREATE OR REPLACE FUNCTION indexes_size (text) RETURNS bigint
+ AS '
+ SELECT COALESCE(SUM(pg_relation_size(ir.oid)), 0)::bigint
+ + COALESCE(SUM(pg_relation_size(it.oid)), 0)::bigint
+ + COALESCE(SUM(pg_relation_size(iti.oid)), 0)::bigint AS bytes
+ FROM pg_class r
+ LEFT JOIN pg_index i ON (r.oid = i.indrelid)
+ LEFT JOIN pg_class ir ON (ir.oid = i.indexrelid)
+ LEFT JOIN pg_class it ON (ir.reltoastrelid = it.oid)
+ LEFT JOIN pg_class iti ON (it.reltoastidxid = iti.oid)
+ WHERE r.relname = \$1
+ ' LANGUAGE SQL;
+
+ CREATE OR REPLACE FUNCTION relation_size_components (text) RETURNS SETOF RECORD
+ AS '
+ -- relation size
+ SELECT indexes_size(r.relname) AS indexes_size,
+ relation_size(r.relname) AS data_size,
+ aggregate_relation_size(r.relname) AS total_size,
+ r.relname, r.relkind, r.oid AS relid, r.relfilenode
+ FROM pg_class r
+ WHERE r.relname = \$1
+
+ UNION ALL
+
+ -- relation toast size
+ SELECT indexes_size(toast.relname) AS indexes_size,
+ relation_size(''pg_toast.''||toast.relname) AS data_size,
+ aggregate_relation_size(toast.relname) AS total_size,
+ toast.relname, toast.relkind, toast.oid AS relid, toast.relfilenode
+ FROM pg_class r, pg_class toast
+ WHERE r.reltoastrelid = toast.oid
+ AND r.relname = \$1
+
+ UNION ALL
+
+ -- relation toast index size
+ SELECT indexes_size(toastidxr.relname) AS indexes_size,
+ relation_size(''pg_toast.''||toastidxr.relname) AS data_size,
+ aggregate_relation_size(toastidxr.relname) AS total_size,
+ toastidxr.relname, toastidxr.relkind,
+ toastidxr.oid AS relid, toastidxr.relfilenode
+ FROM pg_class r, pg_index toastidx, pg_class toastidxr
+ WHERE r.relname = \$1
+ AND r.reltoastrelid = toastidx.indrelid
+ AND toastidx.indexrelid = toastidxr.oid
+
+ UNION ALL
+
+ -- relation indices size
+ SELECT indexes_size(idxr.relname) AS indexes_size,
+ relation_size(idxr.relname) AS data_size,
+ aggregate_relation_size(idxr.relname) AS total_size,
+ idxr.relname, idxr.relkind, idxr.oid AS relid, idxr.relfilenode
+ FROM pg_class r, pg_class idxr, pg_index idx
+ WHERE r.relname = \$1
+ AND r.oid = idx.indrelid
+ AND idx.indexrelid = idxr.oid
+
+ UNION ALL
+
+ -- relation indices toast size
+ SELECT indexes_size(idxtoastr.relname) AS indexes_size,
+ relation_size(''pg_toast.''||idxtoastr.relname) AS data_size,
+ aggregate_relation_size(idxtoastr.relname) AS total_size,
+ idxtoastr.relname, idxtoastr.relkind, idxtoastr.oid AS relid,
+ idxtoastr.relfilenode
+ FROM pg_class r, pg_class idxr, pg_index idx, pg_class idxtoastr
+ WHERE r.relname = \$1
+ AND r.oid = idx.indrelid
+ AND idx.indexrelid = idxr.oid
+ AND idxr.reltoastrelid = idxtoastr.oid
+
+ UNION ALL
+
+ -- relation indices toast index size
+ SELECT indexes_size(idxtoastidxr.relname) AS indexes_size,
+ relation_size(''pg_toast.''||idxtoastidxr.relname) AS data_size,
+ aggregate_relation_size(idxtoastidxr.relname) AS total_size,
+ idxtoastidxr.relname, idxtoastidxr.relkind,
+ idxtoastidxr.oid AS relid, idxtoastidxr.relfilenode
+ FROM pg_class r, pg_class idxr, pg_index idx, pg_class idxtoast,
+ pg_class idxtoastidxr
+ WHERE r.relname = \$1
+ AND r.oid = idx.indrelid
+ AND idx.indexrelid = idxr.oid
+ AND idxr.reltoastrelid = idxtoast.oid
+ AND idxtoast.reltoastrelid = idxtoastidxr.oid
+ ' LANGUAGE SQL;
+
---------------------------(end of broadcast)---------------------------
TIP 9: the planner will ignore your desire to choose an index scan if your
joining column's datatypes do not match