LCOV - code coverage report
Current view: top level - src/backend/utils/adt - dbsize.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 196 374 52.4 %
Date: 2019-09-19 23:07:04 Functions: 14 28 50.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13