dbsize.c 9.6 KB
Newer Older
T
Tom Lane 已提交
1 2
/*
 * dbsize.c
3
 *		object size functions
T
Tom Lane 已提交
4
 *
5
 * Copyright (c) 2002-2007, PostgreSQL Global Development Group
T
Tom Lane 已提交
6 7
 *
 * IDENTIFICATION
8
 *	  $PostgreSQL: pgsql/src/backend/utils/adt/dbsize.c,v 1.13 2007/08/27 01:19:14 tgl Exp $
T
Tom Lane 已提交
9 10 11 12 13 14 15 16 17
 *
 */

#include "postgres.h"

#include <sys/types.h>
#include <sys/stat.h>

#include "access/heapam.h"
18
#include "catalog/catalog.h"
T
Tom Lane 已提交
19 20 21 22 23 24
#include "catalog/namespace.h"
#include "catalog/pg_tablespace.h"
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "storage/fd.h"
25
#include "utils/acl.h"
T
Tom Lane 已提交
26 27 28 29 30 31 32 33 34 35
#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;
B
Bruce Momjian 已提交
36 37 38
	struct dirent *direntry;
	DIR		   *dirdesc;
	char		filename[MAXPGPATH];
T
Tom Lane 已提交
39 40 41 42

	dirdesc = AllocateDir(path);

	if (!dirdesc)
B
Bruce Momjian 已提交
43
		return 0;
T
Tom Lane 已提交
44

45
	while ((direntry = ReadDir(dirdesc, path)) != NULL)
T
Tom Lane 已提交
46
	{
B
Bruce Momjian 已提交
47
		struct stat fst;
T
Tom Lane 已提交
48

B
Bruce Momjian 已提交
49
		if (strcmp(direntry->d_name, ".") == 0 ||
T
Tom Lane 已提交
50
			strcmp(direntry->d_name, "..") == 0)
B
Bruce Momjian 已提交
51
			continue;
T
Tom Lane 已提交
52 53 54 55

		snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);

		if (stat(filename, &fst) < 0)
56 57 58 59 60 61 62 63
		{
			if (errno == ENOENT)
				continue;
			else
				ereport(ERROR,
						(errcode_for_file_access(),
						 errmsg("could not stat file \"%s\": %m", filename)));
		}
B
Bruce Momjian 已提交
64
		dirsize += fst.st_size;
T
Tom Lane 已提交
65 66 67 68 69 70 71 72 73 74 75 76
	}

	FreeDir(dirdesc);
	return dirsize;
}

/*
 * calculate size of database in all tablespaces
 */
static int64
calculate_database_size(Oid dbOid)
{
77
	int64		totalsize;
B
Bruce Momjian 已提交
78 79 80 81
	DIR		   *dirdesc;
	struct dirent *direntry;
	char		dirpath[MAXPGPATH];
	char		pathname[MAXPGPATH];
T
Tom Lane 已提交
82 83 84 85

	/* Shared storage in pg_global is not counted */

	/* Include pg_default storage */
86
	snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
87
	totalsize = db_dir_size(pathname);
T
Tom Lane 已提交
88 89

	/* Scan the non-default tablespaces */
90
	snprintf(dirpath, MAXPGPATH, "pg_tblspc");
91
	dirdesc = AllocateDir(dirpath);
T
Tom Lane 已提交
92
	if (!dirdesc)
B
Bruce Momjian 已提交
93
		ereport(ERROR,
T
Tom Lane 已提交
94 95
				(errcode_for_file_access(),
				 errmsg("could not open tablespace directory \"%s\": %m",
96
						dirpath)));
T
Tom Lane 已提交
97

98
	while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
T
Tom Lane 已提交
99
	{
B
Bruce Momjian 已提交
100
		if (strcmp(direntry->d_name, ".") == 0 ||
T
Tom Lane 已提交
101
			strcmp(direntry->d_name, "..") == 0)
B
Bruce Momjian 已提交
102
			continue;
T
Tom Lane 已提交
103

104 105
		snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%u",
				 direntry->d_name, dbOid);
T
Tom Lane 已提交
106 107 108 109 110 111 112
		totalsize += db_dir_size(pathname);
	}

	FreeDir(dirdesc);

	/* Complain if we found no trace of the DB at all */
	if (!totalsize)
B
Bruce Momjian 已提交
113
		ereport(ERROR,
T
Tom Lane 已提交
114 115 116 117 118 119 120 121 122
				(ERRCODE_UNDEFINED_DATABASE,
				 errmsg("database with OID %u does not exist", dbOid)));

	return totalsize;
}

Datum
pg_database_size_oid(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
123
	Oid			dbOid = PG_GETARG_OID(0);
T
Tom Lane 已提交
124

125 126 127 128
	if (!pg_database_ownercheck(dbOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
					   get_database_name(dbOid));

T
Tom Lane 已提交
129 130 131 132 133 134
	PG_RETURN_INT64(calculate_database_size(dbOid));
}

Datum
pg_database_size_name(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
135 136
	Name		dbName = PG_GETARG_NAME(0);
	Oid			dbOid = get_database_oid(NameStr(*dbName));
T
Tom Lane 已提交
137 138 139 140 141 142 143

	if (!OidIsValid(dbOid))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_DATABASE),
				 errmsg("database \"%s\" does not exist",
						NameStr(*dbName))));

144 145 146 147
	if (!pg_database_ownercheck(dbOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
					   NameStr(*dbName));

T
Tom Lane 已提交
148 149 150 151 152 153 154 155 156 157
	PG_RETURN_INT64(calculate_database_size(dbOid));
}


/*
 * calculate total size of tablespace
 */
static int64
calculate_tablespace_size(Oid tblspcOid)
{
B
Bruce Momjian 已提交
158 159 160 161 162
	char		tblspcPath[MAXPGPATH];
	char		pathname[MAXPGPATH];
	int64		totalsize = 0;
	DIR		   *dirdesc;
	struct dirent *direntry;
T
Tom Lane 已提交
163 164

	if (tblspcOid == DEFAULTTABLESPACE_OID)
165
		snprintf(tblspcPath, MAXPGPATH, "base");
T
Tom Lane 已提交
166
	else if (tblspcOid == GLOBALTABLESPACE_OID)
167
		snprintf(tblspcPath, MAXPGPATH, "global");
T
Tom Lane 已提交
168
	else
169
		snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u", tblspcOid);
T
Tom Lane 已提交
170 171 172 173 174 175 176 177 178

	dirdesc = AllocateDir(tblspcPath);

	if (!dirdesc)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open tablespace directory \"%s\": %m",
						tblspcPath)));

179
	while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
T
Tom Lane 已提交
180
	{
B
Bruce Momjian 已提交
181
		struct stat fst;
T
Tom Lane 已提交
182

B
Bruce Momjian 已提交
183
		if (strcmp(direntry->d_name, ".") == 0 ||
T
Tom Lane 已提交
184
			strcmp(direntry->d_name, "..") == 0)
B
Bruce Momjian 已提交
185
			continue;
T
Tom Lane 已提交
186 187 188 189

		snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);

		if (stat(pathname, &fst) < 0)
190 191 192 193 194 195 196 197
		{
			if (errno == ENOENT)
				continue;
			else
				ereport(ERROR,
						(errcode_for_file_access(),
						 errmsg("could not stat file \"%s\": %m", pathname)));
		}
T
Tom Lane 已提交
198 199

		if (fst.st_mode & S_IFDIR)
B
Bruce Momjian 已提交
200 201 202
			totalsize += db_dir_size(pathname);

		totalsize += fst.st_size;
T
Tom Lane 已提交
203 204 205
	}

	FreeDir(dirdesc);
B
Bruce Momjian 已提交
206

T
Tom Lane 已提交
207 208 209 210 211 212
	return totalsize;
}

Datum
pg_tablespace_size_oid(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
213 214
	Oid			tblspcOid = PG_GETARG_OID(0);

215 216 217 218 219
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 (errmsg("must be superuser to use pg_tablespace_size"))));

T
Tom Lane 已提交
220 221 222 223 224 225
	PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
}

Datum
pg_tablespace_size_name(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
226 227
	Name		tblspcName = PG_GETARG_NAME(0);
	Oid			tblspcOid = get_tablespace_oid(NameStr(*tblspcName));
T
Tom Lane 已提交
228

229 230 231 232 233
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 (errmsg("must be superuser to use pg_tablespace_size"))));

T
Tom Lane 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246 247
	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
248
calculate_relation_size(RelFileNode *rfn)
T
Tom Lane 已提交
249
{
250
	int64		totalsize = 0;
251
	char	   *relationpath;
252 253
	char		pathname[MAXPGPATH];
	unsigned int segcount = 0;
T
Tom Lane 已提交
254

255
	relationpath = relpath(*rfn);
T
Tom Lane 已提交
256

B
Bruce Momjian 已提交
257
	for (segcount = 0;; segcount++)
T
Tom Lane 已提交
258 259 260 261
	{
		struct stat fst;

		if (segcount == 0)
262 263
			snprintf(pathname, MAXPGPATH, "%s",
					 relationpath);
T
Tom Lane 已提交
264
		else
265 266
			snprintf(pathname, MAXPGPATH, "%s.%u",
					 relationpath, segcount);
T
Tom Lane 已提交
267 268 269 270 271 272 273 274

		if (stat(pathname, &fst) < 0)
		{
			if (errno == ENOENT)
				break;
			else
				ereport(ERROR,
						(errcode_for_file_access(),
P
Peter Eisentraut 已提交
275
						 errmsg("could not stat file \"%s\": %m", pathname)));
T
Tom Lane 已提交
276 277 278 279 280 281 282 283 284 285
		}
		totalsize += fst.st_size;
	}

	return totalsize;
}

Datum
pg_relation_size_oid(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
286
	Oid			relOid = PG_GETARG_OID(0);
287 288
	Relation	rel;
	int64		size;
T
Tom Lane 已提交
289

290
	rel = relation_open(relOid, AccessShareLock);
T
Tom Lane 已提交
291

292 293 294 295
	if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
					   RelationGetRelationName(rel));

296
	size = calculate_relation_size(&(rel->rd_node));
T
Tom Lane 已提交
297

298
	relation_close(rel, AccessShareLock);
T
Tom Lane 已提交
299

300
	PG_RETURN_INT64(size);
T
Tom Lane 已提交
301 302 303 304 305 306 307
}

Datum
pg_relation_size_name(PG_FUNCTION_ARGS)
{
	text	   *relname = PG_GETARG_TEXT_P(0);
	RangeVar   *relrv;
308 309
	Relation	rel;
	int64		size;
B
Bruce Momjian 已提交
310 311

	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
312
	rel = relation_openrv(relrv, AccessShareLock);
B
Bruce Momjian 已提交
313

314 315 316 317
	if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
					   RelationGetRelationName(rel));

318
	size = calculate_relation_size(&(rel->rd_node));
B
Bruce Momjian 已提交
319

320
	relation_close(rel, AccessShareLock);
T
Tom Lane 已提交
321

322
	PG_RETURN_INT64(size);
T
Tom Lane 已提交
323 324 325 326
}


/*
B
Bruce Momjian 已提交
327
 *	Compute the on-disk size of files for the relation according to the
328
 *	stat function, including heap data, index data, and toast data.
T
Tom Lane 已提交
329 330
 */
static int64
331
calculate_total_relation_size(Oid Relid)
T
Tom Lane 已提交
332
{
333 334 335 336 337 338
	Relation	heapRel;
	Oid			toastOid;
	int64		size;
	ListCell   *cell;

	heapRel = relation_open(Relid, AccessShareLock);
339 340 341 342 343

	if (!pg_class_ownercheck(RelationGetRelid(heapRel), GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
					   RelationGetRelationName(heapRel));

344 345 346 347 348
	toastOid = heapRel->rd_rel->reltoastrelid;

	/* Get the heap size */
	size = calculate_relation_size(&(heapRel->rd_node));

349
	/* Include any dependent indexes */
350
	if (heapRel->rd_rel->relhasindex)
351
	{
B
Bruce Momjian 已提交
352
		List	   *index_oids = RelationGetIndexList(heapRel);
353

354
		foreach(cell, index_oids)
355
		{
356 357 358 359 360 361 362 363
			Oid			idxOid = lfirst_oid(cell);
			Relation	iRel;

			iRel = relation_open(idxOid, AccessShareLock);

			size += calculate_relation_size(&(iRel->rd_node));

			relation_close(iRel, AccessShareLock);
T
Tom Lane 已提交
364
		}
365 366

		list_free(index_oids);
T
Tom Lane 已提交
367
	}
368

369
	/* Recursively include toast table (and index) size */
370 371
	if (OidIsValid(toastOid))
		size += calculate_total_relation_size(toastOid);
T
Tom Lane 已提交
372

373
	relation_close(heapRel, AccessShareLock);
T
Tom Lane 已提交
374 375 376 377 378

	return size;
}

Datum
379
pg_total_relation_size_oid(PG_FUNCTION_ARGS)
T
Tom Lane 已提交
380
{
B
Bruce Momjian 已提交
381
	Oid			relid = PG_GETARG_OID(0);
T
Tom Lane 已提交
382

383 384
	/* permission check is inside calculate_total_relation_size */

385
	PG_RETURN_INT64(calculate_total_relation_size(relid));
T
Tom Lane 已提交
386 387 388
}

Datum
389
pg_total_relation_size_name(PG_FUNCTION_ARGS)
T
Tom Lane 已提交
390
{
391 392 393
	text	   *relname = PG_GETARG_TEXT_P(0);
	RangeVar   *relrv;
	Oid			relid;
B
Bruce Momjian 已提交
394 395

	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
396
	relid = RangeVarGetRelid(relrv, false);
B
Bruce Momjian 已提交
397

398 399
	/* permission check is inside calculate_total_relation_size */

400
	PG_RETURN_INT64(calculate_total_relation_size(relid));
T
Tom Lane 已提交
401 402 403 404 405 406 407 408
}

/*
 * formatting with size units
 */
Datum
pg_size_pretty(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
409 410 411 412
	int64		size = PG_GETARG_INT64(0);
	char	   *result = palloc(50 + VARHDRSZ);
	int64		limit = 10 * 1024;
	int64		mult = 1;
413 414

	if (size < limit * mult)
B
Bruce Momjian 已提交
415
		snprintf(VARDATA(result), 50, INT64_FORMAT " bytes", size);
T
Tom Lane 已提交
416 417 418
	else
	{
		mult *= 1024;
419
		if (size < limit * mult)
B
Bruce Momjian 已提交
420 421
			snprintf(VARDATA(result), 50, INT64_FORMAT " kB",
					 (size + mult / 2) / mult);
T
Tom Lane 已提交
422 423 424
		else
		{
			mult *= 1024;
425
			if (size < limit * mult)
B
Bruce Momjian 已提交
426
				snprintf(VARDATA(result), 50, INT64_FORMAT " MB",
427
						 (size + mult / 2) / mult);
T
Tom Lane 已提交
428 429 430
			else
			{
				mult *= 1024;
431
				if (size < limit * mult)
B
Bruce Momjian 已提交
432
					snprintf(VARDATA(result), 50, INT64_FORMAT " GB",
433
							 (size + mult / 2) / mult);
T
Tom Lane 已提交
434 435
				else
				{
B
Bruce Momjian 已提交
436 437
					mult *= 1024;
					snprintf(VARDATA(result), 50, INT64_FORMAT " TB",
438
							 (size + mult / 2) / mult);
T
Tom Lane 已提交
439 440 441 442 443
				}
			}
		}
	}

444
	SET_VARSIZE(result, strlen(VARDATA(result)) + VARHDRSZ);
T
Tom Lane 已提交
445 446 447

	PG_RETURN_TEXT_P(result);
}