mirror of https://github.com/postgres/postgres
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 PflugREL8_0_STABLE
parent
e814e4bfe5
commit
528ac10c7c
@ -1,202 +1,345 @@ |
|||||||
|
/*
|
||||||
|
* 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(ERRCODE_UNDEFINED_DATABASE), |
(errcode_for_file_access(), |
||||||
errmsg("database \"%s\" does not exist", NameStr(*dbname)))); |
errmsg("could not stat \"%s\": %m", filename))); |
||||||
|
dirsize += fst.st_size; |
||||||
|
} |
||||||
|
|
||||||
|
FreeDir(dirdesc); |
||||||
|
return dirsize; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
#ifdef SYMLINK |
static int64 |
||||||
|
calculate_database_size(Oid dbOid) |
||||||
|
{ |
||||||
|
int64 totalsize=0; |
||||||
|
DIR *dirdesc; |
||||||
|
struct dirent *direntry; |
||||||
|
char pathname[MAXPGPATH]; |
||||||
|
|
||||||
|
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); |
||||||
|
|
||||||
|
char tblspcPath[MAXPGPATH]; |
||||||
|
char pathname[MAXPGPATH]; |
||||||
|
int64 totalsize=0; |
||||||
DIR *dirdesc; |
DIR *dirdesc; |
||||||
struct dirent *direntry; |
struct dirent *direntry; |
||||||
int64 totalsize; |
|
||||||
|
|
||||||
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) |
||||||
{ |
|
||||||
if (baddirOK) |
|
||||||
return 0; |
|
||||||
else |
|
||||||
ereport(ERROR, |
ereport(ERROR, |
||||||
(errcode_for_file_access(), |
(errcode_for_file_access(), |
||||||
errmsg("could not open directory \"%s\": %m", dbpath))); |
errmsg("No such tablespace OID: %u: %m", (unsigned)tblspcOid))); |
||||||
} |
|
||||||
totalsize = 0; |
|
||||||
for (;;) |
|
||||||
{ |
|
||||||
char *fullname; |
|
||||||
struct stat statbuf; |
|
||||||
|
|
||||||
errno = 0; |
while ((direntry = readdir(dirdesc)) != 0) |
||||||
direntry = readdir(dirdesc); |
|
||||||
if (!direntry) |
|
||||||
{ |
{ |
||||||
if (errno) |
struct stat fst; |
||||||
ereport(ERROR, |
|
||||||
(errcode_for_file_access(), |
|
||||||
errmsg("error reading directory: %m"))); |
|
||||||
else |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
fullname = psnprintf(strlen(dbpath) + 1 + strlen(direntry->d_name) + 1, |
if (!strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, "..")) |
||||||
"%s/%s", dbpath, direntry->d_name); |
continue; |
||||||
if (stat(fullname, &statbuf) == -1) |
|
||||||
|
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 stat \"%s\": %m", fullname))); |
errmsg("could not stat \"%s\": %m", pathname))); |
||||||
totalsize += statbuf.st_size; |
totalsize += fst.st_size; |
||||||
pfree(fullname); |
|
||||||
|
if (fst.st_mode & S_IFDIR) |
||||||
|
totalsize += db_dir_size(pathname); |
||||||
} |
} |
||||||
|
|
||||||
FreeDir(dirdesc); |
FreeDir(dirdesc); |
||||||
return (totalsize); |
|
||||||
|
PG_RETURN_INT64(totalsize); |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL function: relation_size(text) returns bigint |
* calculate size of databases in all tablespaces |
||||||
*/ |
*/ |
||||||
|
Datum |
||||||
|
pg_database_size(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
Oid dbOid = PG_GETARG_OID(0); |
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(relation_size); |
PG_RETURN_INT64(calculate_database_size(dbOid)); |
||||||
|
} |
||||||
|
|
||||||
Datum relation_size(PG_FUNCTION_ARGS); |
|
||||||
|
|
||||||
Datum |
Datum |
||||||
relation_size(PG_FUNCTION_ARGS) |
database_size(PG_FUNCTION_ARGS) |
||||||
{ |
{ |
||||||
text *relname = PG_GETARG_TEXT_P(0); |
Name dbName = PG_GETARG_NAME(0); |
||||||
|
Oid dbOid = get_database_oid(NameStr(*dbName)); |
||||||
|
|
||||||
RangeVar *relrv; |
if (!OidIsValid(dbOid)) |
||||||
Relation relation; |
ereport(ERROR, |
||||||
Oid relnode; |
(errcode(ERRCODE_UNDEFINED_DATABASE), |
||||||
int64 totalsize; |
errmsg("database \"%s\" does not exist", NameStr(*dbName)))); |
||||||
unsigned int segcount; |
|
||||||
|
|
||||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname, |
PG_RETURN_INT64(calculate_database_size(dbOid)); |
||||||
"relation_size")); |
} |
||||||
relation = heap_openrv(relrv, AccessShareLock); |
|
||||||
|
|
||||||
relnode = relation->rd_rel->relfilenode; |
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); |
||||||
|
|
||||||
totalsize = 0; |
for (segcount = 0 ;; segcount++) |
||||||
segcount = 0; |
|
||||||
for (;;) |
|
||||||
{ |
{ |
||||||
char *fullname; |
struct stat fst; |
||||||
struct stat statbuf; |
|
||||||
|
|
||||||
if (segcount == 0) |
if (segcount == 0) |
||||||
fullname = psnprintf(25, "%u", (unsigned) relnode); |
snprintf(pathname, MAXPGPATH, "%s/%u", dirpath, (unsigned) relnodeOid); |
||||||
else |
else |
||||||
fullname = psnprintf(50, "%u.%u", (unsigned) relnode, segcount); |
snprintf(pathname, MAXPGPATH, "%s/%u.%u", dirpath, (unsigned) relnodeOid, segcount); |
||||||
|
|
||||||
if (stat(fullname, &statbuf) == -1) |
if (stat(pathname, &fst) < 0) |
||||||
{ |
{ |
||||||
if (errno == ENOENT) |
if (errno == ENOENT) |
||||||
break; |
break; |
||||||
else |
else |
||||||
ereport(ERROR, |
ereport(ERROR, |
||||||
(errcode_for_file_access(), |
(errcode_for_file_access(), |
||||||
errmsg("could not stat \"%s\": %m", fullname))); |
errmsg("could not stat \"%s\": %m", pathname))); |
||||||
} |
} |
||||||
totalsize += statbuf.st_size; |
totalsize += fst.st_size; |
||||||
pfree(fullname); |
|
||||||
segcount++; |
|
||||||
} |
} |
||||||
|
|
||||||
|
return totalsize; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate size of relation |
||||||
|
*/ |
||||||
|
Datum |
||||||
|
pg_relation_size(PG_FUNCTION_ARGS) |
||||||
|
{ |
||||||
|
Oid relOid=PG_GETARG_OID(0); |
||||||
|
|
||||||
|
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) |
||||||
|
{ |
||||||
|
text *relname = PG_GETARG_TEXT_P(0); |
||||||
|
|
||||||
|
RangeVar *relrv; |
||||||
|
Relation relation; |
||||||
|
Oid relnodeOid; |
||||||
|
Oid tblspcOid; |
||||||
|
|
||||||
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname, |
||||||
|
"relation_size")); |
||||||
|
relation = heap_openrv(relrv, AccessShareLock); |
||||||
|
|
||||||
|
tblspcOid = relation->rd_rel->reltablespace; |
||||||
|
relnodeOid = relation->rd_rel->relfilenode; |
||||||
|
|
||||||
heap_close(relation, AccessShareLock); |
heap_close(relation, AccessShareLock); |
||||||
|
|
||||||
PG_RETURN_INT64(totalsize); |
PG_RETURN_INT64(calculate_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); |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue