PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
dbsize.c
Go to the documentation of this file.
1 /*
2  * dbsize.c
3  * Database object size functions, and related inquiries
4  *
5  * Copyright (c) 2002-2017, PostgreSQL Global Development Group
6  *
7  * IDENTIFICATION
8  * src/backend/utils/adt/dbsize.c
9  *
10  */
11 
12 #include "postgres.h"
13 
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 
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 "miscadmin.h"
25 #include "storage/fd.h"
26 #include "utils/acl.h"
27 #include "utils/builtins.h"
28 #include "utils/numeric.h"
29 #include "utils/rel.h"
30 #include "utils/relfilenodemap.h"
31 #include "utils/relmapper.h"
32 #include "utils/syscache.h"
33 
34 /* Divide by two and round towards positive infinity. */
35 #define half_rounded(x) (((x) + ((x) < 0 ? 0 : 1)) / 2)
36 
37 /* Return physical size of directory contents, or 0 if dir doesn't exist */
38 static int64
39 db_dir_size(const char *path)
40 {
41  int64 dirsize = 0;
42  struct dirent *direntry;
43  DIR *dirdesc;
44  char filename[MAXPGPATH];
45 
46  dirdesc = AllocateDir(path);
47 
48  if (!dirdesc)
49  return 0;
50 
51  while ((direntry = ReadDir(dirdesc, path)) != NULL)
52  {
53  struct stat fst;
54 
56 
57  if (strcmp(direntry->d_name, ".") == 0 ||
58  strcmp(direntry->d_name, "..") == 0)
59  continue;
60 
61  snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
62 
63  if (stat(filename, &fst) < 0)
64  {
65  if (errno == ENOENT)
66  continue;
67  else
68  ereport(ERROR,
70  errmsg("could not stat file \"%s\": %m", filename)));
71  }
72  dirsize += fst.st_size;
73  }
74 
75  FreeDir(dirdesc);
76  return dirsize;
77 }
78 
79 /*
80  * calculate size of database in all tablespaces
81  */
82 static int64
84 {
85  int64 totalsize;
86  DIR *dirdesc;
87  struct dirent *direntry;
88  char dirpath[MAXPGPATH];
89  char pathname[MAXPGPATH];
90  AclResult aclresult;
91 
92  /* User must have connect privilege for target database */
93  aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT);
94  if (aclresult != ACLCHECK_OK)
96  get_database_name(dbOid));
97 
98  /* Shared storage in pg_global is not counted */
99 
100  /* Include pg_default storage */
101  snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
102  totalsize = db_dir_size(pathname);
103 
104  /* Scan the non-default tablespaces */
105  snprintf(dirpath, MAXPGPATH, "pg_tblspc");
106  dirdesc = AllocateDir(dirpath);
107  if (!dirdesc)
108  ereport(ERROR,
110  errmsg("could not open tablespace directory \"%s\": %m",
111  dirpath)));
112 
113  while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
114  {
116 
117  if (strcmp(direntry->d_name, ".") == 0 ||
118  strcmp(direntry->d_name, "..") == 0)
119  continue;
120 
121  snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%s/%u",
122  direntry->d_name, TABLESPACE_VERSION_DIRECTORY, dbOid);
123  totalsize += db_dir_size(pathname);
124  }
125 
126  FreeDir(dirdesc);
127 
128  return totalsize;
129 }
130 
131 Datum
133 {
134  Oid dbOid = PG_GETARG_OID(0);
135  int64 size;
136 
137  size = calculate_database_size(dbOid);
138 
139  if (size == 0)
140  PG_RETURN_NULL();
141 
142  PG_RETURN_INT64(size);
143 }
144 
145 Datum
147 {
149  Oid dbOid = get_database_oid(NameStr(*dbName), false);
150  int64 size;
151 
152  size = calculate_database_size(dbOid);
153 
154  if (size == 0)
155  PG_RETURN_NULL();
156 
157  PG_RETURN_INT64(size);
158 }
159 
160 
161 /*
162  * Calculate total size of tablespace. Returns -1 if the tablespace directory
163  * cannot be found.
164  */
165 static int64
167 {
168  char tblspcPath[MAXPGPATH];
169  char pathname[MAXPGPATH];
170  int64 totalsize = 0;
171  DIR *dirdesc;
172  struct dirent *direntry;
173  AclResult aclresult;
174 
175  /*
176  * User must have CREATE privilege for target tablespace, either
177  * explicitly granted or implicitly because it is default for current
178  * database.
179  */
180  if (tblspcOid != MyDatabaseTableSpace)
181  {
182  aclresult = pg_tablespace_aclcheck(tblspcOid, GetUserId(), ACL_CREATE);
183  if (aclresult != ACLCHECK_OK)
185  get_tablespace_name(tblspcOid));
186  }
187 
188  if (tblspcOid == DEFAULTTABLESPACE_OID)
189  snprintf(tblspcPath, MAXPGPATH, "base");
190  else if (tblspcOid == GLOBALTABLESPACE_OID)
191  snprintf(tblspcPath, MAXPGPATH, "global");
192  else
193  snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u/%s", tblspcOid,
195 
196  dirdesc = AllocateDir(tblspcPath);
197 
198  if (!dirdesc)
199  return -1;
200 
201  while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
202  {
203  struct stat fst;
204 
206 
207  if (strcmp(direntry->d_name, ".") == 0 ||
208  strcmp(direntry->d_name, "..") == 0)
209  continue;
210 
211  snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
212 
213  if (stat(pathname, &fst) < 0)
214  {
215  if (errno == ENOENT)
216  continue;
217  else
218  ereport(ERROR,
220  errmsg("could not stat file \"%s\": %m", pathname)));
221  }
222 
223  if (S_ISDIR(fst.st_mode))
224  totalsize += db_dir_size(pathname);
225 
226  totalsize += fst.st_size;
227  }
228 
229  FreeDir(dirdesc);
230 
231  return totalsize;
232 }
233 
234 Datum
236 {
237  Oid tblspcOid = PG_GETARG_OID(0);
238  int64 size;
239 
240  size = calculate_tablespace_size(tblspcOid);
241 
242  if (size < 0)
243  PG_RETURN_NULL();
244 
245  PG_RETURN_INT64(size);
246 }
247 
248 Datum
250 {
251  Name tblspcName = PG_GETARG_NAME(0);
252  Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
253  int64 size;
254 
255  size = calculate_tablespace_size(tblspcOid);
256 
257  if (size < 0)
258  PG_RETURN_NULL();
259 
260  PG_RETURN_INT64(size);
261 }
262 
263 
264 /*
265  * calculate size of (one fork of) a relation
266  *
267  * Note: we can safely apply this to temp tables of other sessions, so there
268  * is no check here or at the call sites for that.
269  */
270 static int64
272 {
273  int64 totalsize = 0;
274  char *relationpath;
275  char pathname[MAXPGPATH];
276  unsigned int segcount = 0;
277 
278  relationpath = relpathbackend(*rfn, backend, forknum);
279 
280  for (segcount = 0;; segcount++)
281  {
282  struct stat fst;
283 
285 
286  if (segcount == 0)
287  snprintf(pathname, MAXPGPATH, "%s",
288  relationpath);
289  else
290  snprintf(pathname, MAXPGPATH, "%s.%u",
291  relationpath, segcount);
292 
293  if (stat(pathname, &fst) < 0)
294  {
295  if (errno == ENOENT)
296  break;
297  else
298  ereport(ERROR,
300  errmsg("could not stat file \"%s\": %m", pathname)));
301  }
302  totalsize += fst.st_size;
303  }
304 
305  return totalsize;
306 }
307 
308 Datum
310 {
311  Oid relOid = PG_GETARG_OID(0);
312  text *forkName = PG_GETARG_TEXT_P(1);
313  Relation rel;
314  int64 size;
315 
316  rel = try_relation_open(relOid, AccessShareLock);
317 
318  /*
319  * Before 9.2, we used to throw an error if the relation didn't exist, but
320  * that makes queries like "SELECT pg_relation_size(oid) FROM pg_class"
321  * less robust, because while we scan pg_class with an MVCC snapshot,
322  * someone else might drop the table. It's better to return NULL for
323  * already-dropped tables than throw an error and abort the whole query.
324  */
325  if (rel == NULL)
326  PG_RETURN_NULL();
327 
328  size = calculate_relation_size(&(rel->rd_node), rel->rd_backend,
330 
332 
333  PG_RETURN_INT64(size);
334 }
335 
336 /*
337  * Calculate total on-disk size of a TOAST relation, including its indexes.
338  * Must not be applied to non-TOAST relations.
339  */
340 static int64
342 {
343  int64 size = 0;
344  Relation toastRel;
345  ForkNumber forkNum;
346  ListCell *lc;
347  List *indexlist;
348 
349  toastRel = relation_open(toastrelid, AccessShareLock);
350 
351  /* toast heap size, including FSM and VM size */
352  for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
353  size += calculate_relation_size(&(toastRel->rd_node),
354  toastRel->rd_backend, forkNum);
355 
356  /* toast index size, including FSM and VM size */
357  indexlist = RelationGetIndexList(toastRel);
358 
359  /* Size is calculated using all the indexes available */
360  foreach(lc, indexlist)
361  {
362  Relation toastIdxRel;
363 
364  toastIdxRel = relation_open(lfirst_oid(lc),
366  for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
367  size += calculate_relation_size(&(toastIdxRel->rd_node),
368  toastIdxRel->rd_backend, forkNum);
369 
370  relation_close(toastIdxRel, AccessShareLock);
371  }
372  list_free(indexlist);
373  relation_close(toastRel, AccessShareLock);
374 
375  return size;
376 }
377 
378 /*
379  * Calculate total on-disk size of a given table,
380  * including FSM and VM, plus TOAST table if any.
381  * Indexes other than the TOAST table's index are not included.
382  *
383  * Note that this also behaves sanely if applied to an index or toast table;
384  * those won't have attached toast tables, but they can have multiple forks.
385  */
386 static int64
388 {
389  int64 size = 0;
390  ForkNumber forkNum;
391 
392  /*
393  * heap size, including FSM and VM
394  */
395  for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
396  size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
397  forkNum);
398 
399  /*
400  * Size of toast relation
401  */
402  if (OidIsValid(rel->rd_rel->reltoastrelid))
403  size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
404 
405  return size;
406 }
407 
408 /*
409  * Calculate total on-disk size of all indexes attached to the given table.
410  *
411  * Can be applied safely to an index, but you'll just get zero.
412  */
413 static int64
415 {
416  int64 size = 0;
417 
418  /*
419  * Aggregate all indexes on the given relation
420  */
421  if (rel->rd_rel->relhasindex)
422  {
423  List *index_oids = RelationGetIndexList(rel);
424  ListCell *cell;
425 
426  foreach(cell, index_oids)
427  {
428  Oid idxOid = lfirst_oid(cell);
429  Relation idxRel;
430  ForkNumber forkNum;
431 
432  idxRel = relation_open(idxOid, AccessShareLock);
433 
434  for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
435  size += calculate_relation_size(&(idxRel->rd_node),
436  idxRel->rd_backend,
437  forkNum);
438 
440  }
441 
442  list_free(index_oids);
443  }
444 
445  return size;
446 }
447 
448 Datum
450 {
451  Oid relOid = PG_GETARG_OID(0);
452  Relation rel;
453  int64 size;
454 
455  rel = try_relation_open(relOid, AccessShareLock);
456 
457  if (rel == NULL)
458  PG_RETURN_NULL();
459 
460  size = calculate_table_size(rel);
461 
463 
464  PG_RETURN_INT64(size);
465 }
466 
467 Datum
469 {
470  Oid relOid = PG_GETARG_OID(0);
471  Relation rel;
472  int64 size;
473 
474  rel = try_relation_open(relOid, AccessShareLock);
475 
476  if (rel == NULL)
477  PG_RETURN_NULL();
478 
479  size = calculate_indexes_size(rel);
480 
482 
483  PG_RETURN_INT64(size);
484 }
485 
486 /*
487  * Compute the on-disk size of all files for the relation,
488  * including heap data, index data, toast data, FSM, VM.
489  */
490 static int64
492 {
493  int64 size;
494 
495  /*
496  * Aggregate the table size, this includes size of the heap, toast and
497  * toast index with free space and visibility map
498  */
499  size = calculate_table_size(rel);
500 
501  /*
502  * Add size of all attached indexes as well
503  */
504  size += calculate_indexes_size(rel);
505 
506  return size;
507 }
508 
509 Datum
511 {
512  Oid relOid = PG_GETARG_OID(0);
513  Relation rel;
514  int64 size;
515 
516  rel = try_relation_open(relOid, AccessShareLock);
517 
518  if (rel == NULL)
519  PG_RETURN_NULL();
520 
521  size = calculate_total_relation_size(rel);
522 
524 
525  PG_RETURN_INT64(size);
526 }
527 
528 /*
529  * formatting with size units
530  */
531 Datum
533 {
534  int64 size = PG_GETARG_INT64(0);
535  char buf[64];
536  int64 limit = 10 * 1024;
537  int64 limit2 = limit * 2 - 1;
538 
539  if (Abs(size) < limit)
540  snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size);
541  else
542  {
543  size >>= 9; /* keep one extra bit for rounding */
544  if (Abs(size) < limit2)
545  snprintf(buf, sizeof(buf), INT64_FORMAT " kB",
546  half_rounded(size));
547  else
548  {
549  size >>= 10;
550  if (Abs(size) < limit2)
551  snprintf(buf, sizeof(buf), INT64_FORMAT " MB",
552  half_rounded(size));
553  else
554  {
555  size >>= 10;
556  if (Abs(size) < limit2)
557  snprintf(buf, sizeof(buf), INT64_FORMAT " GB",
558  half_rounded(size));
559  else
560  {
561  size >>= 10;
562  snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
563  half_rounded(size));
564  }
565  }
566  }
567  }
568 
570 }
571 
572 static char *
574 {
575  Datum d = NumericGetDatum(n);
576 
578 }
579 
580 static Numeric
582 {
583  Datum d = Int64GetDatum(v);
584 
586 }
587 
588 static bool
590 {
591  Datum da = NumericGetDatum(a);
592  Datum db = NumericGetDatum(b);
593 
595 }
596 
597 static Numeric
599 {
600  Datum d = NumericGetDatum(n);
601  Datum result;
602 
603  result = DirectFunctionCall1(numeric_abs, d);
604  return DatumGetNumeric(result);
605 }
606 
607 static Numeric
609 {
610  Datum d = NumericGetDatum(n);
611  Datum zero;
612  Datum one;
613  Datum two;
614  Datum result;
615 
619 
621  d = DirectFunctionCall2(numeric_add, d, one);
622  else
623  d = DirectFunctionCall2(numeric_sub, d, one);
624 
625  result = DirectFunctionCall2(numeric_div_trunc, d, two);
626  return DatumGetNumeric(result);
627 }
628 
629 static Numeric
630 numeric_shift_right(Numeric n, unsigned count)
631 {
632  Datum d = NumericGetDatum(n);
633  Datum divisor_int64;
634  Datum divisor_numeric;
635  Datum result;
636 
637  divisor_int64 = Int64GetDatum((int64) (1 << count));
638  divisor_numeric = DirectFunctionCall1(int8_numeric, divisor_int64);
639  result = DirectFunctionCall2(numeric_div_trunc, d, divisor_numeric);
640  return DatumGetNumeric(result);
641 }
642 
643 Datum
645 {
646  Numeric size = PG_GETARG_NUMERIC(0);
647  Numeric limit,
648  limit2;
649  char *result;
650 
651  limit = int64_to_numeric(10 * 1024);
652  limit2 = int64_to_numeric(10 * 1024 * 2 - 1);
653 
654  if (numeric_is_less(numeric_absolute(size), limit))
655  {
656  result = psprintf("%s bytes", numeric_to_cstring(size));
657  }
658  else
659  {
660  /* keep one extra bit for rounding */
661  /* size >>= 9 */
662  size = numeric_shift_right(size, 9);
663 
664  if (numeric_is_less(numeric_absolute(size), limit2))
665  {
666  size = numeric_half_rounded(size);
667  result = psprintf("%s kB", numeric_to_cstring(size));
668  }
669  else
670  {
671  /* size >>= 10 */
672  size = numeric_shift_right(size, 10);
673  if (numeric_is_less(numeric_absolute(size), limit2))
674  {
675  size = numeric_half_rounded(size);
676  result = psprintf("%s MB", numeric_to_cstring(size));
677  }
678  else
679  {
680  /* size >>= 10 */
681  size = numeric_shift_right(size, 10);
682 
683  if (numeric_is_less(numeric_absolute(size), limit2))
684  {
685  size = numeric_half_rounded(size);
686  result = psprintf("%s GB", numeric_to_cstring(size));
687  }
688  else
689  {
690  /* size >>= 10 */
691  size = numeric_shift_right(size, 10);
692  size = numeric_half_rounded(size);
693  result = psprintf("%s TB", numeric_to_cstring(size));
694  }
695  }
696  }
697  }
698 
700 }
701 
702 /*
703  * Convert a human-readable size to a size in bytes
704  */
705 Datum
707 {
708  text *arg = PG_GETARG_TEXT_PP(0);
709  char *str,
710  *strptr,
711  *endptr;
712  char saved_char;
713  Numeric num;
714  int64 result;
715  bool have_digits = false;
716 
717  str = text_to_cstring(arg);
718 
719  /* Skip leading whitespace */
720  strptr = str;
721  while (isspace((unsigned char) *strptr))
722  strptr++;
723 
724  /* Check that we have a valid number and determine where it ends */
725  endptr = strptr;
726 
727  /* Part (1): sign */
728  if (*endptr == '-' || *endptr == '+')
729  endptr++;
730 
731  /* Part (2): main digit string */
732  if (isdigit((unsigned char) *endptr))
733  {
734  have_digits = true;
735  do
736  endptr++;
737  while (isdigit((unsigned char) *endptr));
738  }
739 
740  /* Part (3): optional decimal point and fractional digits */
741  if (*endptr == '.')
742  {
743  endptr++;
744  if (isdigit((unsigned char) *endptr))
745  {
746  have_digits = true;
747  do
748  endptr++;
749  while (isdigit((unsigned char) *endptr));
750  }
751  }
752 
753  /* Complain if we don't have a valid number at this point */
754  if (!have_digits)
755  ereport(ERROR,
756  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
757  errmsg("invalid size: \"%s\"", str)));
758 
759  /* Part (4): optional exponent */
760  if (*endptr == 'e' || *endptr == 'E')
761  {
762  long exponent;
763  char *cp;
764 
765  /*
766  * Note we might one day support EB units, so if what follows 'E'
767  * isn't a number, just treat it all as a unit to be parsed.
768  */
769  exponent = strtol(endptr + 1, &cp, 10);
770  (void) exponent; /* Silence -Wunused-result warnings */
771  if (cp > endptr + 1)
772  endptr = cp;
773  }
774 
775  /*
776  * Parse the number, saving the next character, which may be the first
777  * character of the unit string.
778  */
779  saved_char = *endptr;
780  *endptr = '\0';
781 
783  CStringGetDatum(strptr),
785  Int32GetDatum(-1)));
786 
787  *endptr = saved_char;
788 
789  /* Skip whitespace between number and unit */
790  strptr = endptr;
791  while (isspace((unsigned char) *strptr))
792  strptr++;
793 
794  /* Handle possible unit */
795  if (*strptr != '\0')
796  {
797  int64 multiplier = 0;
798 
799  /* Trim any trailing whitespace */
800  endptr = str + VARSIZE_ANY_EXHDR(arg) - 1;
801 
802  while (isspace((unsigned char) *endptr))
803  endptr--;
804 
805  endptr++;
806  *endptr = '\0';
807 
808  /* Parse the unit case-insensitively */
809  if (pg_strcasecmp(strptr, "bytes") == 0)
810  multiplier = (int64) 1;
811  else if (pg_strcasecmp(strptr, "kb") == 0)
812  multiplier = (int64) 1024;
813  else if (pg_strcasecmp(strptr, "mb") == 0)
814  multiplier = ((int64) 1024) * 1024;
815 
816  else if (pg_strcasecmp(strptr, "gb") == 0)
817  multiplier = ((int64) 1024) * 1024 * 1024;
818 
819  else if (pg_strcasecmp(strptr, "tb") == 0)
820  multiplier = ((int64) 1024) * 1024 * 1024 * 1024;
821 
822  else
823  ereport(ERROR,
824  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
825  errmsg("invalid size: \"%s\"", text_to_cstring(arg)),
826  errdetail("Invalid size unit: \"%s\".", strptr),
827  errhint("Valid units are \"bytes\", \"kB\", \"MB\", \"GB\", and \"TB\".")));
828 
829  if (multiplier > 1)
830  {
831  Numeric mul_num;
832 
834  Int64GetDatum(multiplier)));
835 
837  NumericGetDatum(mul_num),
838  NumericGetDatum(num)));
839  }
840  }
841 
843  NumericGetDatum(num)));
844 
845  PG_RETURN_INT64(result);
846 }
847 
848 /*
849  * Get the filenode of a relation
850  *
851  * This is expected to be used in queries like
852  * SELECT pg_relation_filenode(oid) FROM pg_class;
853  * That leads to a couple of choices. We work from the pg_class row alone
854  * rather than actually opening each relation, for efficiency. We don't
855  * fail if we can't find the relation --- some rows might be visible in
856  * the query's MVCC snapshot even though the relations have been dropped.
857  * (Note: we could avoid using the catcache, but there's little point
858  * because the relation mapper also works "in the now".) We also don't
859  * fail if the relation doesn't have storage. In all these cases it
860  * seems better to quietly return NULL.
861  */
862 Datum
864 {
865  Oid relid = PG_GETARG_OID(0);
866  Oid result;
867  HeapTuple tuple;
868  Form_pg_class relform;
869 
870  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
871  if (!HeapTupleIsValid(tuple))
872  PG_RETURN_NULL();
873  relform = (Form_pg_class) GETSTRUCT(tuple);
874 
875  switch (relform->relkind)
876  {
877  case RELKIND_RELATION:
878  case RELKIND_MATVIEW:
879  case RELKIND_INDEX:
880  case RELKIND_SEQUENCE:
881  case RELKIND_TOASTVALUE:
882  /* okay, these have storage */
883  if (relform->relfilenode)
884  result = relform->relfilenode;
885  else /* Consult the relation mapper */
886  result = RelationMapOidToFilenode(relid,
887  relform->relisshared);
888  break;
889 
890  default:
891  /* no storage, return NULL */
892  result = InvalidOid;
893  break;
894  }
895 
896  ReleaseSysCache(tuple);
897 
898  if (!OidIsValid(result))
899  PG_RETURN_NULL();
900 
901  PG_RETURN_OID(result);
902 }
903 
904 /*
905  * Get the relation via (reltablespace, relfilenode)
906  *
907  * This is expected to be used when somebody wants to match an individual file
908  * on the filesystem back to its table. That's not trivially possible via
909  * pg_class, because that doesn't contain the relfilenodes of shared and nailed
910  * tables.
911  *
912  * We don't fail but return NULL if we cannot find a mapping.
913  *
914  * InvalidOid can be passed instead of the current database's default
915  * tablespace.
916  */
917 Datum
919 {
920  Oid reltablespace = PG_GETARG_OID(0);
921  Oid relfilenode = PG_GETARG_OID(1);
922  Oid heaprel = InvalidOid;
923 
924  heaprel = RelidByRelfilenode(reltablespace, relfilenode);
925 
926  if (!OidIsValid(heaprel))
927  PG_RETURN_NULL();
928  else
929  PG_RETURN_OID(heaprel);
930 }
931 
932 /*
933  * Get the pathname (relative to $PGDATA) of a relation
934  *
935  * See comments for pg_relation_filenode.
936  */
937 Datum
939 {
940  Oid relid = PG_GETARG_OID(0);
941  HeapTuple tuple;
942  Form_pg_class relform;
943  RelFileNode rnode;
944  BackendId backend;
945  char *path;
946 
947  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
948  if (!HeapTupleIsValid(tuple))
949  PG_RETURN_NULL();
950  relform = (Form_pg_class) GETSTRUCT(tuple);
951 
952  switch (relform->relkind)
953  {
954  case RELKIND_RELATION:
955  case RELKIND_MATVIEW:
956  case RELKIND_INDEX:
957  case RELKIND_SEQUENCE:
958  case RELKIND_TOASTVALUE:
959  /* okay, these have storage */
960 
961  /* This logic should match RelationInitPhysicalAddr */
962  if (relform->reltablespace)
963  rnode.spcNode = relform->reltablespace;
964  else
966  if (rnode.spcNode == GLOBALTABLESPACE_OID)
967  rnode.dbNode = InvalidOid;
968  else
969  rnode.dbNode = MyDatabaseId;
970  if (relform->relfilenode)
971  rnode.relNode = relform->relfilenode;
972  else /* Consult the relation mapper */
973  rnode.relNode = RelationMapOidToFilenode(relid,
974  relform->relisshared);
975  break;
976 
977  default:
978  /* no storage, return NULL */
979  rnode.relNode = InvalidOid;
980  /* some compilers generate warnings without these next two lines */
981  rnode.dbNode = InvalidOid;
982  rnode.spcNode = InvalidOid;
983  break;
984  }
985 
986  if (!OidIsValid(rnode.relNode))
987  {
988  ReleaseSysCache(tuple);
989  PG_RETURN_NULL();
990  }
991 
992  /* Determine owning backend. */
993  switch (relform->relpersistence)
994  {
997  backend = InvalidBackendId;
998  break;
999  case RELPERSISTENCE_TEMP:
1000  if (isTempOrTempToastNamespace(relform->relnamespace))
1001  backend = BackendIdForTempRelations();
1002  else
1003  {
1004  /* Do it the hard way. */
1005  backend = GetTempNamespaceBackendId(relform->relnamespace);
1006  Assert(backend != InvalidBackendId);
1007  }
1008  break;
1009  default:
1010  elog(ERROR, "invalid relpersistence: %c", relform->relpersistence);
1011  backend = InvalidBackendId; /* placate compiler */
1012  break;
1013  }
1014 
1015  ReleaseSysCache(tuple);
1016 
1017  path = relpathbackend(rnode, backend, MAIN_FORKNUM);
1018 
1020 }
Datum pg_filenode_relation(PG_FUNCTION_ARGS)
Definition: dbsize.c:918
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1381
AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4471
int errhint(const char *fmt,...)
Definition: elog.c:987
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
static int64 calculate_total_relation_size(Relation rel)
Definition: dbsize.c:491
#define RELPERSISTENCE_UNLOGGED
Definition: pg_class.h:171
Datum pg_relation_filepath(PG_FUNCTION_ARGS)
Definition: dbsize.c:938
static int64 calculate_relation_size(RelFileNode *rfn, BackendId backend, ForkNumber forknum)
Definition: dbsize.c:271
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1150
Oid GetUserId(void)
Definition: miscinit.c:283
#define PG_RETURN_INT64(x)
Definition: fmgr.h:311
bool isTempOrTempToastNamespace(Oid namespaceId)
Definition: namespace.c:2976
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define NumericGetDatum(X)
Definition: numeric.h:51
Oid RelidByRelfilenode(Oid reltablespace, Oid relfilenode)
Datum numeric_out(PG_FUNCTION_ARGS)
Definition: numeric.c:641
#define RELKIND_MATVIEW
Definition: pg_class.h:167
#define AccessShareLock
Definition: lockdefs.h:36
#define GLOBALTABLESPACE_OID
Definition: pg_tablespace.h:64
Datum numeric_int8(PG_FUNCTION_ARGS)
Definition: numeric.c:2987
int errcode(int sqlerrcode)
Definition: elog.c:575
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1263
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
int GetTempNamespaceBackendId(Oid namespaceId)
Definition: namespace.c:3029
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:555
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
Form_pg_class rd_rel
Definition: rel.h:113
unsigned int Oid
Definition: postgres_ext.h:31
static Numeric numeric_shift_right(Numeric n, unsigned count)
Definition: dbsize.c:630
Definition: dirent.h:9
Datum int8_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:2968
#define OidIsValid(objectId)
Definition: c.h:534
Datum numeric_div_trunc(PG_FUNCTION_ARGS)
Definition: numeric.c:2414
Datum pg_total_relation_size(PG_FUNCTION_ARGS)
Definition: dbsize.c:510
ForkNumber forkname_to_number(const char *forkName)
Definition: relpath.c:48
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:149
Oid MyDatabaseTableSpace
Definition: globals.c:78
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:270
#define Abs(x)
Definition: c.h:808
#define RELPERSISTENCE_PERMANENT
Definition: pg_class.h:170
Datum pg_relation_filenode(PG_FUNCTION_ARGS)
Definition: dbsize.c:863
static char * numeric_to_cstring(Numeric n)
Definition: dbsize.c:573
static Numeric numeric_absolute(Numeric n)
Definition: dbsize.c:598
Definition: dirent.c:25
Datum numeric_abs(PG_FUNCTION_ARGS)
Definition: numeric.c:1079
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
#define ACL_CREATE
Definition: parsenodes.h:75
#define DatumGetCString(X)
Definition: postgres.h:574
#define MAXPGPATH
#define DatumGetInt64(X)
Definition: postgres.h:615
#define half_rounded(x)
Definition: dbsize.c:35
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2049
Definition: c.h:489
#define BackendIdForTempRelations()
Definition: backendid.h:34
static Numeric int64_to_numeric(int64 v)
Definition: dbsize.c:581
static char * buf
Definition: pg_test_fsync.c:65
#define PG_GETARG_OID(n)
Definition: fmgr.h:231
void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname)
Definition: aclchk.c:3378
static Numeric numeric_half_rounded(Numeric n)
Definition: dbsize.c:608
#define DEFAULTTABLESPACE_OID
Definition: pg_tablespace.h:63
int errdetail(const char *fmt,...)
Definition: elog.c:873
int errcode_for_file_access(void)
Definition: elog.c:598
#define CStringGetDatum(X)
Definition: postgres.h:586
#define DatumGetBool(X)
Definition: postgres.h:401
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2284
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:559
static uint64 totalsize
static bool numeric_is_less(Numeric a, Numeric b)
Definition: dbsize.c:589
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:2102
Datum pg_table_size(PG_FUNCTION_ARGS)
Definition: dbsize.c:449
Datum pg_size_bytes(PG_FUNCTION_ARGS)
Definition: dbsize.c:706
#define ereport(elevel, rest)
Definition: elog.h:122
static int64 calculate_table_size(Relation rel)
Definition: dbsize.c:387
#define ACL_CONNECT
Definition: parsenodes.h:77
ForkNumber
Definition: relpath.h:24
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:559
Datum numeric_mul(PG_FUNCTION_ARGS)
Definition: numeric.c:2324
Datum pg_database_size_name(PG_FUNCTION_ARGS)
Definition: dbsize.c:146
#define InvalidBackendId
Definition: backendid.h:23
#define RELKIND_TOASTVALUE
Definition: pg_class.h:163
AclResult
Definition: acl.h:170
Oid RelationMapOidToFilenode(Oid relationId, bool shared)
Definition: relmapper.c:145
uintptr_t Datum
Definition: postgres.h:374
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1083
Datum numeric_sub(PG_FUNCTION_ARGS)
Definition: numeric.c:2286
int BackendId
Definition: backendid.h:21
Oid MyDatabaseId
Definition: globals.c:76
Datum numeric_lt(PG_FUNCTION_ARGS)
Definition: numeric.c:2096
#define InvalidOid
Definition: postgres_ext.h:36
Oid get_database_oid(const char *dbname, bool missing_ok)
Definition: dbcommands.c:2002
Datum pg_size_pretty_numeric(PG_FUNCTION_ARGS)
Definition: dbsize.c:644
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:314
static int64 calculate_toast_table_size(Oid toastrelid)
Definition: dbsize.c:341
static int64 calculate_database_size(Oid dbOid)
Definition: dbsize.c:83
RelFileNode rd_node
Definition: rel.h:85
text * cstring_to_text(const char *s)
Definition: varlena.c:151
#define PG_GETARG_NUMERIC(n)
Definition: numeric.h:52
#define DatumGetNumeric(X)
Definition: numeric.h:49
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4409
#define NULL
Definition: c.h:226
BackendId rd_backend
Definition: rel.h:89
#define Assert(condition)
Definition: c.h:671
Datum pg_tablespace_size_name(PG_FUNCTION_ARGS)
Definition: dbsize.c:249
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2350
Datum pg_size_pretty(PG_FUNCTION_ARGS)
Definition: dbsize.c:532
Datum pg_indexes_size(PG_FUNCTION_ARGS)
Definition: dbsize.c:468
#define MAX_FORKNUM
Definition: relpath.h:39
#define TABLESPACE_VERSION_DIRECTORY
Definition: catalog.h:26
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4336
#define INT64_FORMAT
Definition: c.h:312
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
char * text_to_cstring(const text *t)
Definition: varlena.c:184
Datum pg_tablespace_size_oid(PG_FUNCTION_ARGS)
Definition: dbsize.c:235
#define Int32GetDatum(X)
Definition: postgres.h:487
static char * filename
Definition: pg_dumpall.c:84
Datum pg_database_size_oid(PG_FUNCTION_ARGS)
Definition: dbsize.c:132
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:342
char * dbName
Definition: pgbench.c:183
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define PG_GETARG_TEXT_P(n)
Definition: fmgr.h:269
static int64 calculate_tablespace_size(Oid tblspcOid)
Definition: dbsize.c:166
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1427
void list_free(List *list)
Definition: list.c:1133
#define NameStr(name)
Definition: c.h:495
#define RELKIND_INDEX
Definition: pg_class.h:161
void * arg
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1117
Definition: c.h:435
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:97
char d_name[MAX_PATH]
Definition: dirent.h:14
#define elog
Definition: elog.h:219
#define RELPERSISTENCE_TEMP
Definition: pg_class.h:172
Datum numeric_ge(PG_FUNCTION_ARGS)
Definition: numeric.c:2081
#define RELKIND_RELATION
Definition: pg_class.h:160
#define PG_GETARG_INT64(n)
Definition: fmgr.h:238
#define relpathbackend(rnode, backend, forknum)
Definition: relpath.h:62
#define RELKIND_SEQUENCE
Definition: pg_class.h:162
Definition: pg_list.h:45
#define PG_RETURN_OID(x)
Definition: fmgr.h:304
Datum pg_relation_size(PG_FUNCTION_ARGS)
Definition: dbsize.c:309
Datum numeric_add(PG_FUNCTION_ARGS)
Definition: numeric.c:2248
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:557
#define PG_RETURN_NULL()
Definition: fmgr.h:289
int FreeDir(DIR *dir)
Definition: fd.c:2393
static int64 db_dir_size(const char *path)
Definition: dbsize.c:39
#define PG_GETARG_NAME(n)
Definition: fmgr.h:234
static int64 calculate_indexes_size(Relation rel)
Definition: dbsize.c:414
#define lfirst_oid(lc)
Definition: pg_list.h:108