From 358a897fa1d7ff49c350f2cb7a0372c0f67dfb88 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 29 Jul 2005 14:47:04 +0000 Subject: Move dbsize functions into the backend. New functions: pg_tablespace_size pg_database_size pg_relation_size pg_complete_relation_size pg_size_pretty Remove /contrib/dbsize. Dave Page --- src/backend/utils/adt/Makefile | 4 +- src/backend/utils/adt/dbsize.c | 461 +++++++++++++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.h | 25 ++- src/include/utils/builtins.h | 13 +- 4 files changed, 498 insertions(+), 5 deletions(-) create mode 100644 src/backend/utils/adt/dbsize.c (limited to 'src') diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 427d6334cd4..a0b4bf7dfd4 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -1,7 +1,7 @@ # # Makefile for utils/adt # -# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.57 2004/04/01 21:28:45 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.58 2005/07/29 14:46:57 momjian Exp $ # subdir = src/backend/utils/adt @@ -24,7 +24,7 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \ tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \ network.o mac.o inet_net_ntop.o inet_net_pton.o \ ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \ - ascii.o quote.o pgstatfuncs.o encode.o + ascii.o quote.o pgstatfuncs.o encode.o dbsize.o like.o: like.c like_match.c diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c new file mode 100644 index 00000000000..158d2cf4026 --- /dev/null +++ b/src/backend/utils/adt/dbsize.c @@ -0,0 +1,461 @@ +/* + * dbsize.c + * object size functions + * + * Copyright (c) 2002-2005, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/utils/adt/dbsize.c,v 1.1 2005/07/29 14:46:57 momjian Exp $ + * + */ + +#include "postgres.h" + +#include +#include + +#include "access/heapam.h" +#include "catalog/namespace.h" +#include "catalog/pg_tablespace.h" +#include "commands/dbcommands.h" +#include "commands/tablespace.h" +#include "miscadmin.h" +#include "storage/fd.h" +#include "utils/builtins.h" +#include "utils/syscache.h" +#include "utils/relcache.h" + + +/* Return physical size of directory contents, or 0 if dir doesn't exist */ +static int64 +db_dir_size(const char *path) +{ + int64 dirsize = 0; + struct dirent *direntry; + DIR *dirdesc; + char filename[MAXPGPATH]; + + dirdesc = AllocateDir(path); + + if (!dirdesc) + return 0; + + while ((direntry = readdir(dirdesc)) != NULL) + { + struct stat fst; + + if (strcmp(direntry->d_name, ".") == 0 || + strcmp(direntry->d_name, "..") == 0) + continue; + + snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name); + + if (stat(filename, &fst) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat \"%s\": %m", filename))); + + dirsize += fst.st_size; + } + + FreeDir(dirdesc); + return dirsize; +} + +/* + * calculate size of database in all tablespaces + */ +static int64 +calculate_database_size(Oid dbOid) +{ + int64 totalsize = 0; + DIR *dirdesc; + struct dirent *direntry; + char pathname[MAXPGPATH]; + + /* Shared storage in pg_global is not counted */ + + /* Include pg_default storage */ + snprintf(pathname, MAXPGPATH, "%s/base/%u", DataDir, dbOid); + totalsize += db_dir_size(pathname); + + /* Scan the non-default tablespaces */ + snprintf(pathname, MAXPGPATH, "%s/pg_tblspc", DataDir); + dirdesc = AllocateDir(pathname); + if (!dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open tablespace directory \"%s\": %m", + pathname))); + + while ((direntry = readdir(dirdesc)) != NULL) + { + if (strcmp(direntry->d_name, ".") == 0 || + strcmp(direntry->d_name, "..") == 0) + continue; + + snprintf(pathname, MAXPGPATH, "%s/pg_tblspc/%s/%u", + DataDir, direntry->d_name, dbOid); + totalsize += db_dir_size(pathname); + } + + FreeDir(dirdesc); + + /* Complain if we found no trace of the DB at all */ + if (!totalsize) + ereport(ERROR, + (ERRCODE_UNDEFINED_DATABASE, + errmsg("database with OID %u does not exist", dbOid))); + + return totalsize; +} + +Datum +pg_database_size_oid(PG_FUNCTION_ARGS) +{ + Oid dbOid = PG_GETARG_OID(0); + + PG_RETURN_INT64(calculate_database_size(dbOid)); +} + +Datum +pg_database_size_name(PG_FUNCTION_ARGS) +{ + Name dbName = PG_GETARG_NAME(0); + Oid dbOid = get_database_oid(NameStr(*dbName)); + + if (!OidIsValid(dbOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", + NameStr(*dbName)))); + + PG_RETURN_INT64(calculate_database_size(dbOid)); +} + + +/* + * calculate total size of tablespace + */ +static int64 +calculate_tablespace_size(Oid tblspcOid) +{ + char tblspcPath[MAXPGPATH]; + char pathname[MAXPGPATH]; + int64 totalsize=0; + DIR *dirdesc; + struct dirent *direntry; + + if (tblspcOid == DEFAULTTABLESPACE_OID) + snprintf(tblspcPath, MAXPGPATH, "%s/base", DataDir); + else if (tblspcOid == GLOBALTABLESPACE_OID) + snprintf(tblspcPath, MAXPGPATH, "%s/global", DataDir); + else + snprintf(tblspcPath, MAXPGPATH, "%s/pg_tblspc/%u", DataDir, tblspcOid); + + dirdesc = AllocateDir(tblspcPath); + + if (!dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open tablespace directory \"%s\": %m", + tblspcPath))); + + while ((direntry = readdir(dirdesc)) != NULL) + { + struct stat fst; + + if (strcmp(direntry->d_name, ".") == 0 || + strcmp(direntry->d_name, "..") == 0) + continue; + + snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name); + + if (stat(pathname, &fst) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat \"%s\": %m", pathname))); + + if (fst.st_mode & S_IFDIR) + totalsize += db_dir_size(pathname); + + totalsize += fst.st_size; + } + + FreeDir(dirdesc); + + return totalsize; +} + +Datum +pg_tablespace_size_oid(PG_FUNCTION_ARGS) +{ + Oid tblspcOid = PG_GETARG_OID(0); + + PG_RETURN_INT64(calculate_tablespace_size(tblspcOid)); +} + +Datum +pg_tablespace_size_name(PG_FUNCTION_ARGS) +{ + Name tblspcName = PG_GETARG_NAME(0); + Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName)); + + if (!OidIsValid(tblspcOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace \"%s\" does not exist", + NameStr(*tblspcName)))); + + PG_RETURN_INT64(calculate_tablespace_size(tblspcOid)); +} + + +/* + * calculate size of a relation + */ +static int64 +calculate_relation_size(Oid tblspcOid, Oid relnodeOid) +{ + int64 totalsize=0; + unsigned int segcount=0; + char dirpath[MAXPGPATH]; + char pathname[MAXPGPATH]; + + if (!tblspcOid) + tblspcOid = MyDatabaseTableSpace; + + if (tblspcOid == DEFAULTTABLESPACE_OID) + snprintf(dirpath, MAXPGPATH, "%s/base/%u", DataDir, MyDatabaseId); + else if (tblspcOid == GLOBALTABLESPACE_OID) + snprintf(dirpath, MAXPGPATH, "%s/global", DataDir); + else + snprintf(dirpath, MAXPGPATH, "%s/pg_tblspc/%u/%u", + DataDir, tblspcOid, MyDatabaseId); + + for (segcount = 0 ;; segcount++) + { + struct stat fst; + + if (segcount == 0) + snprintf(pathname, MAXPGPATH, "%s/%u", + dirpath, relnodeOid); + else + snprintf(pathname, MAXPGPATH, "%s/%u.%u", + dirpath, relnodeOid, segcount); + + if (stat(pathname, &fst) < 0) + { + if (errno == ENOENT) + break; + else + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat \"%s\": %m", pathname))); + } + totalsize += fst.st_size; + } + + return totalsize; +} + +Datum +pg_relation_size_oid(PG_FUNCTION_ARGS) +{ + Oid relOid=PG_GETARG_OID(0); + HeapTuple tuple; + Form_pg_class pg_class; + Oid relnodeOid; + Oid tblspcOid; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (ERRCODE_UNDEFINED_TABLE, + errmsg("relation with OID %u does not exist", relOid))); + + pg_class = (Form_pg_class) GETSTRUCT(tuple); + relnodeOid = pg_class->relfilenode; + tblspcOid = pg_class->reltablespace; + + ReleaseSysCache(tuple); + + PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid)); +} + +Datum +pg_relation_size_name(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_P(0); + RangeVar *relrv; + Relation relation; + Oid relnodeOid; + Oid tblspcOid; + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + relation = relation_openrv(relrv, AccessShareLock); + + tblspcOid = relation->rd_rel->reltablespace; + relnodeOid = relation->rd_rel->relfilenode; + + relation_close(relation, AccessShareLock); + + PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid)); +} + + +/* + * Compute on-disk size of files for 'relation' according to the stat function, + * optionally including heap data, index data, and/or toast data. + */ +static int64 +calculate_complete_relation_size(Oid tblspcOid, Oid relnodeOid) +{ + Relation heapRelation; + Relation idxRelation; + Relation toastRelation; + Oid idxOid; + Oid idxTblspcOid; + Oid toastOid; + Oid toastTblspcOid; + bool hasIndices; + int64 size = 0; + List *indexoidlist; + ListCell *idx; + + heapRelation = relation_open(relnodeOid, AccessShareLock); + toastOid = heapRelation->rd_rel->reltoastrelid; + hasIndices = heapRelation->rd_rel->relhasindex; + + /* Get the heap size */ + size += calculate_relation_size(tblspcOid, relnodeOid); + + /* Get Index size */ + if ( hasIndices ) { + /* recursively include any dependent indexes ... */ + indexoidlist = RelationGetIndexList(heapRelation); + + foreach(idx, indexoidlist) { + idxOid = lfirst_oid(idx); + idxRelation = relation_open(idxOid, AccessShareLock); + idxTblspcOid = idxRelation->rd_rel->reltablespace; + size += calculate_relation_size(idxTblspcOid, idxOid); + relation_close(idxRelation, AccessShareLock); + } + list_free(indexoidlist); + } + + /* Close heapReleation now we no longer need it */ + relation_close(heapRelation, AccessShareLock); + + /* Get toast table size */ + if ( toastOid != 0 ) { + + /* recursively include any toast relations ... */ + toastRelation = relation_open(toastOid, AccessShareLock); + toastTblspcOid = toastRelation->rd_rel->reltablespace; + size += calculate_relation_size(toastTblspcOid, toastOid); + relation_close(toastRelation, AccessShareLock); + } + + return size; +} + +/* + * Compute on-disk size of files for 'relation' including + * heap data, index data, and toasted data. + */ +Datum +pg_complete_relation_size_oid(PG_FUNCTION_ARGS) +{ + Oid relOid=PG_GETARG_OID(0); + HeapTuple tuple; + Form_pg_class pg_class; + Oid relnodeOid; + Oid tblspcOid; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (ERRCODE_UNDEFINED_TABLE, + errmsg("relation with OID %u does not exist", relOid))); + + pg_class = (Form_pg_class) GETSTRUCT(tuple); + relnodeOid = pg_class->relfilenode; + tblspcOid = pg_class->reltablespace; + + ReleaseSysCache(tuple); + + PG_RETURN_INT64(calculate_complete_relation_size(tblspcOid, relnodeOid)); +} + +Datum +pg_complete_relation_size_name(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_P(0); + RangeVar *relrv; + Relation relation; + Oid relnodeOid; + Oid tblspcOid; + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + relation = relation_openrv(relrv, AccessShareLock); + + tblspcOid = relation->rd_rel->reltablespace; + relnodeOid = relation->rd_rel->relfilenode; + + relation_close(relation, AccessShareLock); + + PG_RETURN_INT64(calculate_complete_relation_size(tblspcOid, relnodeOid)); +} + +/* + * formatting with size units + */ +Datum +pg_size_pretty(PG_FUNCTION_ARGS) +{ + int64 size=PG_GETARG_INT64(0); + char *result=palloc(50+VARHDRSZ); + int64 limit = 10*1024; + int64 mult=1; + + if (size < limit*mult) + snprintf(VARDATA(result), 50, INT64_FORMAT" bytes", + size); + else + { + mult *= 1024; + if (size < limit*mult) + snprintf(VARDATA(result), 50, INT64_FORMAT " kB", + (size+mult/2) / mult); + else + { + mult *= 1024; + if (size < limit*mult) + snprintf(VARDATA(result), 50, INT64_FORMAT " MB", + (size+mult/2) / mult); + else + { + mult *= 1024; + if (size < limit*mult) + snprintf(VARDATA(result), 50, INT64_FORMAT " GB", + (size+mult/2) / mult); + else + { + mult *= 1024; + snprintf(VARDATA(result), 50, INT64_FORMAT " TB", + (size+mult/2) / mult); + } + } + } + } + + VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ; + + PG_RETURN_TEXT_P(result); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index d69cdc122dd..72e23c3f0b5 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.378 2005/07/26 00:04:19 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.379 2005/07/29 14:47:01 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1573,6 +1573,11 @@ DESCR("matches regex., case-insensitive"); DATA(insert OID = 1241 ( nameicregexne PGNSP PGUID 12 f f t f i 2 16 "19 25" _null_ _null_ _null_ nameicregexne - _null_ )); DESCR("does not match regex., case-insensitive"); +DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_ pg_tablespace_size_oid - _null_ )); +DESCR("Calculate total disk space usage for the specified tablespace"); +DATA(insert OID = 2323 ( pg_tablespace_size PGNSP PGUID 12 f f t f v 1 20 "19" _null_ _null_ _null_ pg_tablespace_size_name - _null_ )); +DESCR("Calculate total disk space usage for the specified tablespace"); + DATA(insert OID = 1251 ( int4abs PGNSP PGUID 12 f f t f i 1 23 "23" _null_ _null_ _null_ int4abs - _null_ )); DESCR("absolute value"); DATA(insert OID = 1253 ( int2abs PGNSP PGUID 12 f f t f i 1 21 "21" _null_ _null_ _null_ int2abs - _null_ )); @@ -1581,6 +1586,9 @@ DESCR("absolute value"); DATA(insert OID = 1263 ( interval PGNSP PGUID 12 f f t f s 1 1186 "25" _null_ _null_ _null_ text_interval - _null_ )); DESCR("convert text to interval"); +DATA(insert OID = 2324 ( pg_database_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_ pg_database_size_oid - _null_ )); +DESCR("Calculate total disk space usage for the specified database"); + DATA(insert OID = 1271 ( overlaps PGNSP PGUID 12 f f f f i 4 16 "1266 1266 1266 1266" _null_ _null_ _null_ overlaps_timetz - _null_ )); DESCR("SQL92 interval comparison"); DATA(insert OID = 1272 ( datetime_pl PGNSP PGUID 12 f f t f i 2 1114 "1082 1083" _null_ _null_ _null_ datetime_timestamp - _null_ )); @@ -1624,6 +1632,9 @@ DESCR("latest tid of a tuple"); DATA(insert OID = 1294 ( currtid2 PGNSP PGUID 12 f f t f v 2 27 "25 27" _null_ _null_ _null_ currtid_byrelname - _null_ )); DESCR("latest tid of a tuple"); +DATA(insert OID = 2168 ( pg_database_size PGNSP PGUID 12 f f t f v 1 20 "19" _null_ _null_ _null_ pg_database_size_name - _null_ )); +DESCR("Calculate total disk space usage for the specified database"); + DATA(insert OID = 1296 ( timedate_pl PGNSP PGUID 14 f f t f i 2 1114 "1083 1082" _null_ _null_ _null_ "select ($2 + $1)" - _null_ )); DESCR("convert time and date to timestamp"); DATA(insert OID = 1297 ( datetimetz_pl PGNSP PGUID 12 f f t f i 2 1184 "1082 1266" _null_ _null_ _null_ datetimetz_timestamptz - _null_ )); @@ -3038,7 +3049,6 @@ DESCR("Prepare for taking an online backup"); DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 f f t f v 0 25 "" _null_ _null_ _null_ pg_stop_backup - _null_ )); DESCR("Finish taking an online backup"); - /* Aggregates (moved here from pg_aggregate for 7.3) */ DATA(insert OID = 2100 ( avg PGNSP PGUID 12 t f f f i 1 1700 "20" _null_ _null_ _null_ aggregate_dummy - _null_ )); @@ -3227,6 +3237,17 @@ DESCR("current user privilege on schema by schema name"); DATA(insert OID = 2273 ( has_schema_privilege PGNSP PGUID 12 f f t f s 2 16 "26 25" _null_ _null_ _null_ has_schema_privilege_id - _null_ )); DESCR("current user privilege on schema by schema oid"); +DATA(insert OID = 2325 ( pg_relation_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_ pg_relation_size_oid - _null_ )); +DESCR("Calculate disk space usage for the specified table or index"); +DATA(insert OID = 2289 ( pg_relation_size PGNSP PGUID 12 f f t f v 1 20 "25" _null_ _null_ _null_ pg_relation_size_name - _null_ )); +DESCR("Calculate disk space usage for the specified table or index"); +DATA(insert OID = 2286 ( pg_complete_relation_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_ pg_complete_relation_size_oid - _null_ )); +DESCR("Calculate total disk space usage for the specified table and associated indexes and toast tables"); +DATA(insert OID = 2287 ( pg_complete_relation_size PGNSP PGUID 12 f f t f v 1 20 "25" _null_ _null_ _null_ pg_complete_relation_size_name - _null_ )); +DESCR("Calculate total disk space usage for the specified table and associated indexes and toast tables"); +DATA(insert OID = 2288 ( pg_size_pretty PGNSP PGUID 12 f f t f v 1 25 "20" _null_ _null_ _null_ pg_size_pretty - _null_ )); +DESCR("Convert a long int to a human readable text using size units"); + DATA(insert OID = 2390 ( has_tablespace_privilege PGNSP PGUID 12 f f t f s 3 16 "19 25 25" _null_ _null_ _null_ has_tablespace_privilege_name_name - _null_ )); DESCR("user privilege on tablespace by username, tablespace name"); DATA(insert OID = 2391 ( has_tablespace_privilege PGNSP PGUID 12 f f t f s 3 16 "19 26 25" _null_ _null_ _null_ has_tablespace_privilege_name_id - _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 4f2ac7aae31..96681e08509 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.261 2005/07/26 00:04:19 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.262 2005/07/29 14:47:04 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -363,6 +363,17 @@ extern Datum float84le(PG_FUNCTION_ARGS); extern Datum float84gt(PG_FUNCTION_ARGS); extern Datum float84ge(PG_FUNCTION_ARGS); +/* dbsize.c */ +extern Datum pg_tablespace_size_oid(PG_FUNCTION_ARGS); +extern Datum pg_tablespace_size_name(PG_FUNCTION_ARGS); +extern Datum pg_database_size_oid(PG_FUNCTION_ARGS); +extern Datum pg_database_size_name(PG_FUNCTION_ARGS); +extern Datum pg_relation_size_oid(PG_FUNCTION_ARGS); +extern Datum pg_relation_size_name(PG_FUNCTION_ARGS); +extern Datum pg_complete_relation_size_oid(PG_FUNCTION_ARGS); +extern Datum pg_complete_relation_size_name(PG_FUNCTION_ARGS); +extern Datum pg_size_pretty(PG_FUNCTION_ARGS); + /* misc.c */ extern Datum nullvalue(PG_FUNCTION_ARGS); extern Datum nonnullvalue(PG_FUNCTION_ARGS); -- cgit v1.2.3