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