3 * Database object size functions, and related inquiries
5 * Copyright (c) 2002-2013, PostgreSQL Global Development Group
8 * src/backend/utils/adt/dbsize.c
14 #include <sys/types.h>
17 #include "access/heapam.h"
18 #include "access/htup_details.h"
19 #include "catalog/catalog.h"
20 #include "catalog/namespace.h"
21 #include "catalog/pg_tablespace.h"
22 #include "commands/dbcommands.h"
23 #include "commands/tablespace.h"
24 #include "common/relpath.h"
25 #include "miscadmin.h"
26 #include "storage/fd.h"
27 #include "utils/acl.h"
28 #include "utils/builtins.h"
29 #include "utils/numeric.h"
30 #include "utils/rel.h"
31 #include "utils/relfilenodemap.h"
32 #include "utils/relmapper.h"
33 #include "utils/syscache.h"
36 /* Return physical size of directory contents, or 0 if dir doesn't exist */
38 db_dir_size(const char *path)
41 struct dirent *direntry;
43 char filename[MAXPGPATH];
45 dirdesc = AllocateDir(path);
50 while ((direntry = ReadDir(dirdesc, path)) != NULL)
54 CHECK_FOR_INTERRUPTS();
56 if (strcmp(direntry->d_name, ".") == 0 ||
57 strcmp(direntry->d_name, "..") == 0)
60 snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
62 if (stat(filename, &fst) < 0)
68 (errcode_for_file_access(),
69 errmsg("could not stat file \"%s\": %m", filename)));
71 dirsize += fst.st_size;
79 * calculate size of database in all tablespaces
82 calculate_database_size(Oid dbOid)
86 struct dirent *direntry;
87 char dirpath[MAXPGPATH];
88 char pathname[MAXPGPATH];
91 /* User must have connect privilege for target database */
92 aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT);
93 if (aclresult != ACLCHECK_OK)
94 aclcheck_error(aclresult, ACL_KIND_DATABASE,
95 get_database_name(dbOid));
97 /* Shared storage in pg_global is not counted */
99 /* Include pg_default storage */
100 snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
101 totalsize = db_dir_size(pathname);
103 /* Scan the non-default tablespaces */
104 snprintf(dirpath, MAXPGPATH, "pg_tblspc");
105 dirdesc = AllocateDir(dirpath);
108 (errcode_for_file_access(),
109 errmsg("could not open tablespace directory \"%s\": %m",
112 while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
114 CHECK_FOR_INTERRUPTS();
116 if (strcmp(direntry->d_name, ".") == 0 ||
117 strcmp(direntry->d_name, "..") == 0)
120 snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%s/%u",
121 direntry->d_name, TABLESPACE_VERSION_DIRECTORY, dbOid);
122 totalsize += db_dir_size(pathname);
131 pg_database_size_oid(PG_FUNCTION_ARGS)
133 Oid dbOid = PG_GETARG_OID(0);
136 size = calculate_database_size(dbOid);
141 PG_RETURN_INT64(size);
145 pg_database_size_name(PG_FUNCTION_ARGS)
147 Name dbName = PG_GETARG_NAME(0);
148 Oid dbOid = get_database_oid(NameStr(*dbName), false);
151 size = calculate_database_size(dbOid);
156 PG_RETURN_INT64(size);
161 * Calculate total size of tablespace. Returns -1 if the tablespace directory
165 calculate_tablespace_size(Oid tblspcOid)
167 char tblspcPath[MAXPGPATH];
168 char pathname[MAXPGPATH];
171 struct dirent *direntry;
175 * User must have CREATE privilege for target tablespace, either
176 * explicitly granted or implicitly because it is default for current
179 if (tblspcOid != MyDatabaseTableSpace)
181 aclresult = pg_tablespace_aclcheck(tblspcOid, GetUserId(), ACL_CREATE);
182 if (aclresult != ACLCHECK_OK)
183 aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
184 get_tablespace_name(tblspcOid));
187 if (tblspcOid == DEFAULTTABLESPACE_OID)
188 snprintf(tblspcPath, MAXPGPATH, "base");
189 else if (tblspcOid == GLOBALTABLESPACE_OID)
190 snprintf(tblspcPath, MAXPGPATH, "global");
192 snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u/%s", tblspcOid,
193 TABLESPACE_VERSION_DIRECTORY);
195 dirdesc = AllocateDir(tblspcPath);
200 while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
204 CHECK_FOR_INTERRUPTS();
206 if (strcmp(direntry->d_name, ".") == 0 ||
207 strcmp(direntry->d_name, "..") == 0)
210 snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
212 if (stat(pathname, &fst) < 0)
218 (errcode_for_file_access(),
219 errmsg("could not stat file \"%s\": %m", pathname)));
222 if (S_ISDIR(fst.st_mode))
223 totalsize += db_dir_size(pathname);
225 totalsize += fst.st_size;
234 pg_tablespace_size_oid(PG_FUNCTION_ARGS)
236 Oid tblspcOid = PG_GETARG_OID(0);
239 size = calculate_tablespace_size(tblspcOid);
244 PG_RETURN_INT64(size);
248 pg_tablespace_size_name(PG_FUNCTION_ARGS)
250 Name tblspcName = PG_GETARG_NAME(0);
251 Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
254 size = calculate_tablespace_size(tblspcOid);
259 PG_RETURN_INT64(size);
264 * calculate size of (one fork of) a relation
266 * Note: we can safely apply this to temp tables of other sessions, so there
267 * is no check here or at the call sites for that.
270 calculate_relation_size(RelFileNode *rfn, BackendId backend, ForkNumber forknum)
274 char pathname[MAXPGPATH];
275 unsigned int segcount = 0;
277 relationpath = relpathbackend(*rfn, backend, forknum);
279 for (segcount = 0;; segcount++)
283 CHECK_FOR_INTERRUPTS();
286 snprintf(pathname, MAXPGPATH, "%s",
289 snprintf(pathname, MAXPGPATH, "%s.%u",
290 relationpath, segcount);
292 if (stat(pathname, &fst) < 0)
298 (errcode_for_file_access(),
299 errmsg("could not stat file \"%s\": %m", pathname)));
301 totalsize += fst.st_size;
308 pg_relation_size(PG_FUNCTION_ARGS)
310 Oid relOid = PG_GETARG_OID(0);
311 text *forkName = PG_GETARG_TEXT_P(1);
315 rel = try_relation_open(relOid, AccessShareLock);
318 * Before 9.2, we used to throw an error if the relation didn't exist, but
319 * that makes queries like "SELECT pg_relation_size(oid) FROM pg_class"
320 * less robust, because while we scan pg_class with an MVCC snapshot,
321 * someone else might drop the table. It's better to return NULL for
322 * already-dropped tables than throw an error and abort the whole query.
327 size = calculate_relation_size(&(rel->rd_node), rel->rd_backend,
328 forkname_to_number(text_to_cstring(forkName)));
330 relation_close(rel, AccessShareLock);
332 PG_RETURN_INT64(size);
336 * Calculate total on-disk size of a TOAST relation, including its indexes.
337 * Must not be applied to non-TOAST relations.
340 calculate_toast_table_size(Oid toastrelid)
348 toastRel = relation_open(toastrelid, AccessShareLock);
350 /* toast heap size, including FSM and VM size */
351 for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
352 size += calculate_relation_size(&(toastRel->rd_node),
353 toastRel->rd_backend, forkNum);
355 /* toast index size, including FSM and VM size */
356 indexlist = RelationGetIndexList(toastRel);
358 /* Size is calculated using all the indexes available */
359 foreach(lc, indexlist)
361 Relation toastIdxRel;
362 toastIdxRel = relation_open(lfirst_oid(lc),
364 for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
365 size += calculate_relation_size(&(toastIdxRel->rd_node),
366 toastIdxRel->rd_backend, forkNum);
368 relation_close(toastIdxRel, AccessShareLock);
370 list_free(indexlist);
371 relation_close(toastRel, AccessShareLock);
377 * Calculate total on-disk size of a given table,
378 * including FSM and VM, plus TOAST table if any.
379 * Indexes other than the TOAST table's index are not included.
381 * Note that this also behaves sanely if applied to an index or toast table;
382 * those won't have attached toast tables, but they can have multiple forks.
385 calculate_table_size(Relation rel)
391 * heap size, including FSM and VM
393 for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
394 size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
398 * Size of toast relation
400 if (OidIsValid(rel->rd_rel->reltoastrelid))
401 size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
407 * Calculate total on-disk size of all indexes attached to the given table.
409 * Can be applied safely to an index, but you'll just get zero.
412 calculate_indexes_size(Relation rel)
417 * Aggregate all indexes on the given relation
419 if (rel->rd_rel->relhasindex)
421 List *index_oids = RelationGetIndexList(rel);
424 foreach(cell, index_oids)
426 Oid idxOid = lfirst_oid(cell);
430 idxRel = relation_open(idxOid, AccessShareLock);
432 for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
433 size += calculate_relation_size(&(idxRel->rd_node),
437 relation_close(idxRel, AccessShareLock);
440 list_free(index_oids);
447 pg_table_size(PG_FUNCTION_ARGS)
449 Oid relOid = PG_GETARG_OID(0);
453 rel = try_relation_open(relOid, AccessShareLock);
458 size = calculate_table_size(rel);
460 relation_close(rel, AccessShareLock);
462 PG_RETURN_INT64(size);
466 pg_indexes_size(PG_FUNCTION_ARGS)
468 Oid relOid = PG_GETARG_OID(0);
472 rel = try_relation_open(relOid, AccessShareLock);
477 size = calculate_indexes_size(rel);
479 relation_close(rel, AccessShareLock);
481 PG_RETURN_INT64(size);
485 * Compute the on-disk size of all files for the relation,
486 * including heap data, index data, toast data, FSM, VM.
489 calculate_total_relation_size(Relation rel)
494 * Aggregate the table size, this includes size of the heap, toast and
495 * toast index with free space and visibility map
497 size = calculate_table_size(rel);
500 * Add size of all attached indexes as well
502 size += calculate_indexes_size(rel);
508 pg_total_relation_size(PG_FUNCTION_ARGS)
510 Oid relOid = PG_GETARG_OID(0);
514 rel = try_relation_open(relOid, AccessShareLock);
519 size = calculate_total_relation_size(rel);
521 relation_close(rel, AccessShareLock);
523 PG_RETURN_INT64(size);
527 * formatting with size units
530 pg_size_pretty(PG_FUNCTION_ARGS)
532 int64 size = PG_GETARG_INT64(0);
534 int64 limit = 10 * 1024;
535 int64 limit2 = limit * 2 - 1;
538 snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size);
541 size >>= 9; /* keep one extra bit for rounding */
543 snprintf(buf, sizeof(buf), INT64_FORMAT " kB",
549 snprintf(buf, sizeof(buf), INT64_FORMAT " MB",
555 snprintf(buf, sizeof(buf), INT64_FORMAT " GB",
560 snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
567 PG_RETURN_TEXT_P(cstring_to_text(buf));
571 numeric_to_cstring(Numeric n)
573 Datum d = NumericGetDatum(n);
575 return DatumGetCString(DirectFunctionCall1(numeric_out, d));
579 int64_to_numeric(int64 v)
581 Datum d = Int64GetDatum(v);
583 return DatumGetNumeric(DirectFunctionCall1(int8_numeric, d));
587 numeric_is_less(Numeric a, Numeric b)
589 Datum da = NumericGetDatum(a);
590 Datum db = NumericGetDatum(b);
592 return DatumGetBool(DirectFunctionCall2(numeric_lt, da, db));
596 numeric_plus_one_over_two(Numeric n)
598 Datum d = NumericGetDatum(n);
603 one = DirectFunctionCall1(int8_numeric, Int64GetDatum(1));
604 two = DirectFunctionCall1(int8_numeric, Int64GetDatum(2));
605 result = DirectFunctionCall2(numeric_add, d, one);
606 result = DirectFunctionCall2(numeric_div_trunc, result, two);
607 return DatumGetNumeric(result);
611 numeric_shift_right(Numeric n, unsigned count)
613 Datum d = NumericGetDatum(n);
615 Datum divisor_numeric;
618 divisor_int64 = Int64GetDatum((int64) (1 << count));
619 divisor_numeric = DirectFunctionCall1(int8_numeric, divisor_int64);
620 result = DirectFunctionCall2(numeric_div_trunc, d, divisor_numeric);
621 return DatumGetNumeric(result);
625 pg_size_pretty_numeric(PG_FUNCTION_ARGS)
627 Numeric size = PG_GETARG_NUMERIC(0);
633 limit = int64_to_numeric(10 * 1024);
634 limit2 = int64_to_numeric(10 * 1024 * 2 - 1);
636 if (numeric_is_less(size, limit))
638 buf = numeric_to_cstring(size);
639 result = palloc(strlen(buf) + 7);
641 strcat(result, " bytes");
645 /* keep one extra bit for rounding */
647 size = numeric_shift_right(size, 9);
649 if (numeric_is_less(size, limit2))
651 /* size = (size + 1) / 2 */
652 size = numeric_plus_one_over_two(size);
653 buf = numeric_to_cstring(size);
654 result = palloc(strlen(buf) + 4);
656 strcat(result, " kB");
661 size = numeric_shift_right(size, 10);
662 if (numeric_is_less(size, limit2))
664 /* size = (size + 1) / 2 */
665 size = numeric_plus_one_over_two(size);
666 buf = numeric_to_cstring(size);
667 result = palloc(strlen(buf) + 4);
669 strcat(result, " MB");
674 size = numeric_shift_right(size, 10);
676 if (numeric_is_less(size, limit2))
678 /* size = (size + 1) / 2 */
679 size = numeric_plus_one_over_two(size);
680 buf = numeric_to_cstring(size);
681 result = palloc(strlen(buf) + 4);
683 strcat(result, " GB");
688 size = numeric_shift_right(size, 10);
689 /* size = (size + 1) / 2 */
690 size = numeric_plus_one_over_two(size);
691 buf = numeric_to_cstring(size);
692 result = palloc(strlen(buf) + 4);
694 strcat(result, " TB");
700 PG_RETURN_TEXT_P(cstring_to_text(result));
704 * Get the filenode of a relation
706 * This is expected to be used in queries like
707 * SELECT pg_relation_filenode(oid) FROM pg_class;
708 * That leads to a couple of choices. We work from the pg_class row alone
709 * rather than actually opening each relation, for efficiency. We don't
710 * fail if we can't find the relation --- some rows might be visible in
711 * the query's MVCC snapshot even though the relations have been dropped.
712 * (Note: we could avoid using the catcache, but there's little point
713 * because the relation mapper also works "in the now".) We also don't
714 * fail if the relation doesn't have storage. In all these cases it
715 * seems better to quietly return NULL.
718 pg_relation_filenode(PG_FUNCTION_ARGS)
720 Oid relid = PG_GETARG_OID(0);
723 Form_pg_class relform;
725 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
726 if (!HeapTupleIsValid(tuple))
728 relform = (Form_pg_class) GETSTRUCT(tuple);
730 switch (relform->relkind)
732 case RELKIND_RELATION:
733 case RELKIND_MATVIEW:
735 case RELKIND_SEQUENCE:
736 case RELKIND_TOASTVALUE:
737 /* okay, these have storage */
738 if (relform->relfilenode)
739 result = relform->relfilenode;
740 else /* Consult the relation mapper */
741 result = RelationMapOidToFilenode(relid,
742 relform->relisshared);
746 /* no storage, return NULL */
751 ReleaseSysCache(tuple);
753 if (!OidIsValid(result))
756 PG_RETURN_OID(result);
760 * Get the relation via (reltablespace, relfilenode)
762 * This is expected to be used when somebody wants to match an individual file
763 * on the filesystem back to its table. That's not trivially possible via
764 * pg_class, because that doesn't contain the relfilenodes of shared and nailed
767 * We don't fail but return NULL if we cannot find a mapping.
769 * InvalidOid can be passed instead of the current database's default
773 pg_filenode_relation(PG_FUNCTION_ARGS)
775 Oid reltablespace = PG_GETARG_OID(0);
776 Oid relfilenode = PG_GETARG_OID(1);
777 Oid heaprel = InvalidOid;
779 heaprel = RelidByRelfilenode(reltablespace, relfilenode);
781 if (!OidIsValid(heaprel))
784 PG_RETURN_OID(heaprel);
788 * Get the pathname (relative to $PGDATA) of a relation
790 * See comments for pg_relation_filenode.
793 pg_relation_filepath(PG_FUNCTION_ARGS)
795 Oid relid = PG_GETARG_OID(0);
797 Form_pg_class relform;
802 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
803 if (!HeapTupleIsValid(tuple))
805 relform = (Form_pg_class) GETSTRUCT(tuple);
807 switch (relform->relkind)
809 case RELKIND_RELATION:
810 case RELKIND_MATVIEW:
812 case RELKIND_SEQUENCE:
813 case RELKIND_TOASTVALUE:
814 /* okay, these have storage */
816 /* This logic should match RelationInitPhysicalAddr */
817 if (relform->reltablespace)
818 rnode.spcNode = relform->reltablespace;
820 rnode.spcNode = MyDatabaseTableSpace;
821 if (rnode.spcNode == GLOBALTABLESPACE_OID)
822 rnode.dbNode = InvalidOid;
824 rnode.dbNode = MyDatabaseId;
825 if (relform->relfilenode)
826 rnode.relNode = relform->relfilenode;
827 else /* Consult the relation mapper */
828 rnode.relNode = RelationMapOidToFilenode(relid,
829 relform->relisshared);
833 /* no storage, return NULL */
834 rnode.relNode = InvalidOid;
835 /* some compilers generate warnings without these next two lines */
836 rnode.dbNode = InvalidOid;
837 rnode.spcNode = InvalidOid;
841 if (!OidIsValid(rnode.relNode))
843 ReleaseSysCache(tuple);
847 /* Determine owning backend. */
848 switch (relform->relpersistence)
850 case RELPERSISTENCE_UNLOGGED:
851 case RELPERSISTENCE_PERMANENT:
852 backend = InvalidBackendId;
854 case RELPERSISTENCE_TEMP:
855 if (isTempOrToastNamespace(relform->relnamespace))
856 backend = MyBackendId;
859 /* Do it the hard way. */
860 backend = GetTempNamespaceBackendId(relform->relnamespace);
861 Assert(backend != InvalidBackendId);
865 elog(ERROR, "invalid relpersistence: %c", relform->relpersistence);
866 backend = InvalidBackendId; /* placate compiler */
870 ReleaseSysCache(tuple);
872 path = relpathbackend(rnode, backend, MAIN_FORKNUM);
874 PG_RETURN_TEXT_P(cstring_to_text(path));