Commit 528ac10c authored by Bruce Momjian's avatar Bruce Momjian

The current implementation of dbsize doesn't handle tables in

tablespaces correctly, and is quite restricted on objects covered (only
tables and databases, but not tablespaces and indexes).

The attached patch contributes:

- database_size(name)
- relation_size(text)
These are the well-known functions, tablespace-aware.

- pg_tablespace_size(oid)
- pg_database_size(oid)
- pg_relation_size(oid)
Tablespace-aware implementations, used by the upper functions.
pg_relation_size will report sizes of indexes as well.

- pg_size_pretty(bigint)
Formatting of sizes, to display '146MB' instead of '152885668'

Andreas Pflug
parent e814e4bf
/*
* dbsize.c
* object size functions
*
* Copyright (c) 2004, PostgreSQL Global Development Group
*
* Author: Andreas Pflug <pgadmin@pse-consulting.de>
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/dbsize/dbsize.c,v 1.13 2004/09/02 00:55:22 momjian Exp $
*
*/
#include "postgres.h" #include "postgres.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "storage/fd.h"
#include "catalog/catname.h" #include "utils/syscache.h"
#include "utils/builtins.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_tablespace.h" #include "catalog/pg_tablespace.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "fmgr.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "utils/builtins.h"
static int64 extern DLLIMPORT char *DataDir;
get_tablespace_size(Oid dbid, Oid spcid, bool baddirOK);
static char * Datum pg_tablespace_size(PG_FUNCTION_ARGS);
psnprintf(size_t len, const char *fmt,...) Datum pg_database_size(PG_FUNCTION_ARGS);
{ Datum pg_relation_size(PG_FUNCTION_ARGS);
va_list ap; Datum pg_size_pretty(PG_FUNCTION_ARGS);
char *buf;
buf = palloc(len); Datum database_size(PG_FUNCTION_ARGS);
Datum relation_size(PG_FUNCTION_ARGS);
va_start(ap, fmt); PG_FUNCTION_INFO_V1(pg_tablespace_size);
vsnprintf(buf, len, fmt, ap); PG_FUNCTION_INFO_V1(pg_database_size);
va_end(ap); PG_FUNCTION_INFO_V1(pg_relation_size);
PG_FUNCTION_INFO_V1(pg_size_pretty);
return buf; PG_FUNCTION_INFO_V1(database_size);
} PG_FUNCTION_INFO_V1(relation_size);
/* static int64
* SQL function: database_size(name) returns bigint db_dir_size(char *path)
*/ {
int64 dirsize=0;
struct dirent *direntry;
DIR *dirdesc;
char filename[MAXPGPATH];
PG_FUNCTION_INFO_V1(database_size); dirdesc=AllocateDir(path);
Datum database_size(PG_FUNCTION_ARGS); if (!dirdesc)
return 0;
Datum while ((direntry = readdir(dirdesc)) != 0)
database_size(PG_FUNCTION_ARGS) {
{ struct stat fst;
Name dbname = PG_GETARG_NAME(0);
Oid dbid; if (!strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, ".."))
int64 totalsize; continue;
#ifdef SYMLINK snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
Relation dbrel;
HeapScanDesc scan;
HeapTuple tuple;
#endif
dbid = get_database_oid(NameStr(*dbname)); if (stat(filename, &fst) < 0)
if (!OidIsValid(dbid)) ereport(ERROR,
ereport(ERROR, (errcode_for_file_access(),
(errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("could not stat \"%s\": %m", filename)));
errmsg("database \"%s\" does not exist", NameStr(*dbname)))); dirsize += fst.st_size;
}
FreeDir(dirdesc);
return dirsize;
}
static int64
calculate_database_size(Oid dbOid)
{
int64 totalsize=0;
DIR *dirdesc;
struct dirent *direntry;
char pathname[MAXPGPATH];
#ifdef SYMLINK snprintf(pathname, MAXPGPATH, "%s/global/%u", DataDir, (unsigned)dbOid);
totalsize += db_dir_size(pathname);
snprintf(pathname, MAXPGPATH, "%s/base/%u", DataDir, (unsigned)dbOid);
totalsize += db_dir_size(pathname);
dbrel = heap_openr(TableSpaceRelationName, AccessShareLock); snprintf(pathname, MAXPGPATH, "%s/pg_tblspc", DataDir);
scan = heap_beginscan(dbrel, SnapshotNow, 0, (ScanKey) NULL); dirdesc = AllocateDir(pathname);
totalsize = 0; if (!dirdesc)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open tablespace directory: %m")));
while ((tuple = heap_getnext(scan, ForwardScanDirection))) while ((direntry = readdir(dirdesc)) != 0)
{ {
Oid spcid = HeapTupleGetOid(tuple); if (!strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, ".."))
continue;
if (spcid != GLOBALTABLESPACE_OID) snprintf(pathname, MAXPGPATH, "%s/pg_tblspc/%s/%u", DataDir, direntry->d_name, (unsigned)dbOid);
totalsize += get_tablespace_size(dbid, spcid, true); totalsize += db_dir_size(pathname);
} }
heap_endscan(scan);
heap_close(dbrel, AccessShareLock);
#else
/* Same as always */
totalsize = get_tablespace_size(dbid, DEFAULTTABLESPACE_OID, false);
#endif
/*
* We need to keep in mind that we may not be called from the database
* whose size we're reporting so, we need to look in every tablespace
* to see if our database has data in there
*/
PG_RETURN_INT64(totalsize); FreeDir(dirdesc);
if (!totalsize)
ereport(ERROR,
(ERRCODE_UNDEFINED_DATABASE,
errmsg("Database OID %u unknown.", (unsigned)dbOid)));
return totalsize;
} }
static int64 /*
get_tablespace_size(Oid dbid, Oid spcid, bool baddirOK) * calculate total size of tablespace
*/
Datum
pg_tablespace_size(PG_FUNCTION_ARGS)
{ {
char *dbpath; Oid tblspcOid = PG_GETARG_OID(0);
DIR *dirdesc;
struct dirent *direntry; char tblspcPath[MAXPGPATH];
int64 totalsize; char pathname[MAXPGPATH];
int64 totalsize=0;
DIR *dirdesc;
struct dirent *direntry;
dbpath = GetDatabasePath(dbid, spcid); 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, (unsigned)tblspcOid);
dirdesc = AllocateDir(tblspcPath);
dirdesc = AllocateDir(dbpath);
if (!dirdesc) if (!dirdesc)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("No such tablespace OID: %u: %m", (unsigned)tblspcOid)));
while ((direntry = readdir(dirdesc)) != 0)
{ {
if (baddirOK) struct stat fst;
return 0;
else if (!strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, ".."))
continue;
snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
if (stat(pathname, &fst) < 0)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not open directory \"%s\": %m", dbpath))); errmsg("could not stat \"%s\": %m", pathname)));
totalsize += fst.st_size;
if (fst.st_mode & S_IFDIR)
totalsize += db_dir_size(pathname);
} }
totalsize = 0;
for (;;) FreeDir(dirdesc);
PG_RETURN_INT64(totalsize);
}
/*
* calculate size of databases in all tablespaces
*/
Datum
pg_database_size(PG_FUNCTION_ARGS)
{
Oid dbOid = PG_GETARG_OID(0);
PG_RETURN_INT64(calculate_database_size(dbOid));
}
Datum
database_size(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));
}
static int64
calculate_relation_size(Oid tblspcOid, Oid relnodeOid)
{
int64 totalsize=0;
unsigned int segcount=0;
char dirpath[MAXPGPATH];
char pathname[MAXPGPATH];
if (tblspcOid == 0 || tblspcOid == DEFAULTTABLESPACE_OID)
snprintf(dirpath, MAXPGPATH, "%s/base/%u", DataDir, (unsigned)MyDatabaseId);
else if (tblspcOid == GLOBALTABLESPACE_OID)
snprintf(dirpath, MAXPGPATH, "%s/global", DataDir);
else
snprintf(dirpath, MAXPGPATH, "%s/pg_tblspc/%u/%u", DataDir, (unsigned)tblspcOid, (unsigned)MyDatabaseId);
for (segcount = 0 ;; segcount++)
{ {
char *fullname; struct stat fst;
struct stat statbuf;
if (segcount == 0)
snprintf(pathname, MAXPGPATH, "%s/%u", dirpath, (unsigned) relnodeOid);
else
snprintf(pathname, MAXPGPATH, "%s/%u.%u", dirpath, (unsigned) relnodeOid, segcount);
errno = 0; if (stat(pathname, &fst) < 0)
direntry = readdir(dirdesc);
if (!direntry)
{ {
if (errno) if (errno == ENOENT)
break;
else
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("error reading directory: %m"))); errmsg("could not stat \"%s\": %m", pathname)));
else
break;
} }
totalsize += fst.st_size;
fullname = psnprintf(strlen(dbpath) + 1 + strlen(direntry->d_name) + 1,
"%s/%s", dbpath, direntry->d_name);
if (stat(fullname, &statbuf) == -1)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat \"%s\": %m", fullname)));
totalsize += statbuf.st_size;
pfree(fullname);
} }
FreeDir(dirdesc); return totalsize;
return (totalsize);
} }
/* /*
* SQL function: relation_size(text) returns bigint * calculate size of relation
*/ */
Datum
pg_relation_size(PG_FUNCTION_ARGS)
{
Oid relOid=PG_GETARG_OID(0);
PG_FUNCTION_INFO_V1(relation_size); HeapTuple tuple;
Form_pg_class pg_class;
Oid relnodeOid;
Oid tblspcOid;
char relkind;
tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relOid), 0, 0, 0);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(ERRCODE_UNDEFINED_TABLE,
errmsg("Relation OID %u does not exist", relOid)));
pg_class = (Form_pg_class) GETSTRUCT(tuple);
relnodeOid = pg_class->relfilenode;
tblspcOid = pg_class->reltablespace;
relkind = pg_class->relkind;
ReleaseSysCache(tuple);
switch(relkind)
{
case RELKIND_INDEX:
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
break;
default:
ereport(ERROR,
(ERRCODE_WRONG_OBJECT_TYPE,
errmsg("Relation kind %d not supported", relkind)));
}
PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid));
}
Datum relation_size(PG_FUNCTION_ARGS);
Datum Datum
relation_size(PG_FUNCTION_ARGS) relation_size(PG_FUNCTION_ARGS)
...@@ -160,43 +288,58 @@ relation_size(PG_FUNCTION_ARGS) ...@@ -160,43 +288,58 @@ relation_size(PG_FUNCTION_ARGS)
RangeVar *relrv; RangeVar *relrv;
Relation relation; Relation relation;
Oid relnode; Oid relnodeOid;
int64 totalsize; Oid tblspcOid;
unsigned int segcount;
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname, relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
"relation_size")); "relation_size"));
relation = heap_openrv(relrv, AccessShareLock); relation = heap_openrv(relrv, AccessShareLock);
relnode = relation->rd_rel->relfilenode; tblspcOid = relation->rd_rel->reltablespace;
relnodeOid = relation->rd_rel->relfilenode;
totalsize = 0; heap_close(relation, AccessShareLock);
segcount = 0;
for (;;)
{
char *fullname;
struct stat statbuf;
if (segcount == 0) PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid));
fullname = psnprintf(25, "%u", (unsigned) relnode); }
else
fullname = psnprintf(50, "%u.%u", (unsigned) relnode, segcount);
if (stat(fullname, &statbuf) == -1) /*
* 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
{ {
if (errno == ENOENT) mult *= 1024;
break; if (size < limit*mult)
snprintf(VARDATA(result), 50, INT64_FORMAT " MB", (size+mult/2) / mult);
else else
ereport(ERROR, {
(errcode_for_file_access(), mult *= 1024;
errmsg("could not stat \"%s\": %m", fullname))); 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);
}
}
} }
totalsize += statbuf.st_size;
pfree(fullname);
segcount++;
} }
VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
heap_close(relation, AccessShareLock); PG_RETURN_TEXT_P(result);
PG_RETURN_INT64(totalsize);
} }
...@@ -5,3 +5,19 @@ CREATE FUNCTION database_size (name) RETURNS bigint ...@@ -5,3 +5,19 @@ CREATE FUNCTION database_size (name) RETURNS bigint
CREATE FUNCTION relation_size (text) RETURNS bigint CREATE FUNCTION relation_size (text) RETURNS bigint
AS 'MODULE_PATHNAME', 'relation_size' AS 'MODULE_PATHNAME', 'relation_size'
LANGUAGE C WITH (isstrict); LANGUAGE C WITH (isstrict);
CREATE FUNCTION pg_tablespace_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_tablespace_size'
LANGUAGE C STABLE STRICT;
CREATE FUNCTION pg_database_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_database_size'
LANGUAGE C STABLE STRICT;
CREATE FUNCTION pg_relation_size(oid) RETURNS bigint
AS 'MODULE_PATHNAME', 'pg_relation_size'
LANGUAGE C STABLE STRICT;
CREATE FUNCTION pg_size_pretty(bigint) RETURNS text
AS 'MODULE_PATHNAME', 'pg_size_pretty'
LANGUAGE C STABLE STRICT;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment