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

Generated by: LCOV version 1.11