LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - dbsize.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 56.2 % 354 199 5 11 96 43 6 104 30 59 102 120 4 9
Current Date: 2023-04-08 15:15:32 Functions: 55.6 % 27 15 12 14 1 12 14 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a