mirror of
https://github.com/postgres/postgres.git
synced 2025-08-06 18:42:54 +03:00
The places that did, eg, (statbuf.st_mode & S_IFMT) == S_IFDIR were correct, but there is no good reason not to use S_ISDIR() instead, especially when that's what the other 90% of our code does. The places that did, eg, (statbuf.st_mode & S_IFDIR) were flat out *wrong* and would fail in various platform-specific ways, eg a symlink could be mistaken for a regular file on most Unixen. The actual impact of this is probably small, since the problem cases seem to always involve symlinks or sockets, which are unlikely to be found in the directories that PG code might be scanning. But it's clearly trouble waiting to happen, so patch all the way back anyway. (There seem to be no occurrences of the mistake in 7.4.)
351 lines
7.8 KiB
C
351 lines
7.8 KiB
C
/*
|
|
* dbsize.c
|
|
* object size functions
|
|
*
|
|
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/contrib/dbsize/dbsize.c,v 1.16.4.1 2008/03/31 01:33:13 tgl Exp $
|
|
*
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "access/heapam.h"
|
|
#include "catalog/namespace.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/fd.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
/* hack to make it compile under Win32 */
|
|
extern DLLIMPORT char *DataDir;
|
|
|
|
Datum pg_tablespace_size(PG_FUNCTION_ARGS);
|
|
Datum pg_database_size(PG_FUNCTION_ARGS);
|
|
Datum pg_relation_size(PG_FUNCTION_ARGS);
|
|
Datum pg_size_pretty(PG_FUNCTION_ARGS);
|
|
|
|
Datum database_size(PG_FUNCTION_ARGS);
|
|
Datum relation_size(PG_FUNCTION_ARGS);
|
|
|
|
PG_FUNCTION_INFO_V1(pg_tablespace_size);
|
|
PG_FUNCTION_INFO_V1(pg_database_size);
|
|
PG_FUNCTION_INFO_V1(pg_relation_size);
|
|
PG_FUNCTION_INFO_V1(pg_size_pretty);
|
|
|
|
PG_FUNCTION_INFO_V1(database_size);
|
|
PG_FUNCTION_INFO_V1(relation_size);
|
|
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* calculate total size of tablespace
|
|
*/
|
|
Datum
|
|
pg_tablespace_size(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid tblspcOid = PG_GETARG_OID(0);
|
|
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)));
|
|
totalsize += fst.st_size;
|
|
|
|
if (S_ISDIR(fst.st_mode))
|
|
totalsize += db_dir_size(pathname);
|
|
}
|
|
|
|
FreeDir(dirdesc);
|
|
|
|
PG_RETURN_INT64(totalsize);
|
|
}
|
|
|
|
|
|
/*
|
|
* calculate size of database 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));
|
|
}
|
|
|
|
|
|
/* Calculate relation size given tablespace and relation OIDs */
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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
|
|
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 = 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));
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|