Header And Logo

PostgreSQL
| The world's most advanced open source database.

dbsize.c

Go to the documentation of this file.
00001 /*
00002  * dbsize.c
00003  *      Database object size functions, and related inquiries
00004  *
00005  * Copyright (c) 2002-2013, PostgreSQL Global Development Group
00006  *
00007  * IDENTIFICATION
00008  *    src/backend/utils/adt/dbsize.c
00009  *
00010  */
00011 
00012 #include "postgres.h"
00013 
00014 #include <sys/types.h>
00015 #include <sys/stat.h>
00016 
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "catalog/catalog.h"
00020 #include "catalog/namespace.h"
00021 #include "catalog/pg_tablespace.h"
00022 #include "commands/dbcommands.h"
00023 #include "commands/tablespace.h"
00024 #include "common/relpath.h"
00025 #include "miscadmin.h"
00026 #include "storage/fd.h"
00027 #include "utils/acl.h"
00028 #include "utils/builtins.h"
00029 #include "utils/numeric.h"
00030 #include "utils/rel.h"
00031 #include "utils/relmapper.h"
00032 #include "utils/syscache.h"
00033 
00034 
00035 /* Return physical size of directory contents, or 0 if dir doesn't exist */
00036 static int64
00037 db_dir_size(const char *path)
00038 {
00039     int64       dirsize = 0;
00040     struct dirent *direntry;
00041     DIR        *dirdesc;
00042     char        filename[MAXPGPATH];
00043 
00044     dirdesc = AllocateDir(path);
00045 
00046     if (!dirdesc)
00047         return 0;
00048 
00049     while ((direntry = ReadDir(dirdesc, path)) != NULL)
00050     {
00051         struct stat fst;
00052 
00053         CHECK_FOR_INTERRUPTS();
00054 
00055         if (strcmp(direntry->d_name, ".") == 0 ||
00056             strcmp(direntry->d_name, "..") == 0)
00057             continue;
00058 
00059         snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
00060 
00061         if (stat(filename, &fst) < 0)
00062         {
00063             if (errno == ENOENT)
00064                 continue;
00065             else
00066                 ereport(ERROR,
00067                         (errcode_for_file_access(),
00068                          errmsg("could not stat file \"%s\": %m", filename)));
00069         }
00070         dirsize += fst.st_size;
00071     }
00072 
00073     FreeDir(dirdesc);
00074     return dirsize;
00075 }
00076 
00077 /*
00078  * calculate size of database in all tablespaces
00079  */
00080 static int64
00081 calculate_database_size(Oid dbOid)
00082 {
00083     int64       totalsize;
00084     DIR        *dirdesc;
00085     struct dirent *direntry;
00086     char        dirpath[MAXPGPATH];
00087     char        pathname[MAXPGPATH];
00088     AclResult   aclresult;
00089 
00090     /* User must have connect privilege for target database */
00091     aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT);
00092     if (aclresult != ACLCHECK_OK)
00093         aclcheck_error(aclresult, ACL_KIND_DATABASE,
00094                        get_database_name(dbOid));
00095 
00096     /* Shared storage in pg_global is not counted */
00097 
00098     /* Include pg_default storage */
00099     snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
00100     totalsize = db_dir_size(pathname);
00101 
00102     /* Scan the non-default tablespaces */
00103     snprintf(dirpath, MAXPGPATH, "pg_tblspc");
00104     dirdesc = AllocateDir(dirpath);
00105     if (!dirdesc)
00106         ereport(ERROR,
00107                 (errcode_for_file_access(),
00108                  errmsg("could not open tablespace directory \"%s\": %m",
00109                         dirpath)));
00110 
00111     while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
00112     {
00113         CHECK_FOR_INTERRUPTS();
00114 
00115         if (strcmp(direntry->d_name, ".") == 0 ||
00116             strcmp(direntry->d_name, "..") == 0)
00117             continue;
00118 
00119         snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%s/%u",
00120                  direntry->d_name, TABLESPACE_VERSION_DIRECTORY, dbOid);
00121         totalsize += db_dir_size(pathname);
00122     }
00123 
00124     FreeDir(dirdesc);
00125 
00126     return totalsize;
00127 }
00128 
00129 Datum
00130 pg_database_size_oid(PG_FUNCTION_ARGS)
00131 {
00132     Oid         dbOid = PG_GETARG_OID(0);
00133     int64       size;
00134 
00135     size = calculate_database_size(dbOid);
00136 
00137     if (size == 0)
00138         PG_RETURN_NULL();
00139 
00140     PG_RETURN_INT64(size);
00141 }
00142 
00143 Datum
00144 pg_database_size_name(PG_FUNCTION_ARGS)
00145 {
00146     Name        dbName = PG_GETARG_NAME(0);
00147     Oid         dbOid = get_database_oid(NameStr(*dbName), false);
00148     int64       size;
00149 
00150     size = calculate_database_size(dbOid);
00151 
00152     if (size == 0)
00153         PG_RETURN_NULL();
00154 
00155     PG_RETURN_INT64(size);
00156 }
00157 
00158 
00159 /*
00160  * Calculate total size of tablespace. Returns -1 if the tablespace directory
00161  * cannot be found.
00162  */
00163 static int64
00164 calculate_tablespace_size(Oid tblspcOid)
00165 {
00166     char        tblspcPath[MAXPGPATH];
00167     char        pathname[MAXPGPATH];
00168     int64       totalsize = 0;
00169     DIR        *dirdesc;
00170     struct dirent *direntry;
00171     AclResult   aclresult;
00172 
00173     /*
00174      * User must have CREATE privilege for target tablespace, either
00175      * explicitly granted or implicitly because it is default for current
00176      * database.
00177      */
00178     if (tblspcOid != MyDatabaseTableSpace)
00179     {
00180         aclresult = pg_tablespace_aclcheck(tblspcOid, GetUserId(), ACL_CREATE);
00181         if (aclresult != ACLCHECK_OK)
00182             aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
00183                            get_tablespace_name(tblspcOid));
00184     }
00185 
00186     if (tblspcOid == DEFAULTTABLESPACE_OID)
00187         snprintf(tblspcPath, MAXPGPATH, "base");
00188     else if (tblspcOid == GLOBALTABLESPACE_OID)
00189         snprintf(tblspcPath, MAXPGPATH, "global");
00190     else
00191         snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u/%s", tblspcOid,
00192                  TABLESPACE_VERSION_DIRECTORY);
00193 
00194     dirdesc = AllocateDir(tblspcPath);
00195 
00196     if (!dirdesc)
00197         return -1;
00198 
00199     while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
00200     {
00201         struct stat fst;
00202 
00203         CHECK_FOR_INTERRUPTS();
00204 
00205         if (strcmp(direntry->d_name, ".") == 0 ||
00206             strcmp(direntry->d_name, "..") == 0)
00207             continue;
00208 
00209         snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
00210 
00211         if (stat(pathname, &fst) < 0)
00212         {
00213             if (errno == ENOENT)
00214                 continue;
00215             else
00216                 ereport(ERROR,
00217                         (errcode_for_file_access(),
00218                          errmsg("could not stat file \"%s\": %m", pathname)));
00219         }
00220 
00221         if (S_ISDIR(fst.st_mode))
00222             totalsize += db_dir_size(pathname);
00223 
00224         totalsize += fst.st_size;
00225     }
00226 
00227     FreeDir(dirdesc);
00228 
00229     return totalsize;
00230 }
00231 
00232 Datum
00233 pg_tablespace_size_oid(PG_FUNCTION_ARGS)
00234 {
00235     Oid         tblspcOid = PG_GETARG_OID(0);
00236     int64       size;
00237 
00238     size = calculate_tablespace_size(tblspcOid);
00239 
00240     if (size < 0)
00241         PG_RETURN_NULL();
00242 
00243     PG_RETURN_INT64(size);
00244 }
00245 
00246 Datum
00247 pg_tablespace_size_name(PG_FUNCTION_ARGS)
00248 {
00249     Name        tblspcName = PG_GETARG_NAME(0);
00250     Oid         tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
00251     int64       size;
00252 
00253     size = calculate_tablespace_size(tblspcOid);
00254 
00255     if (size < 0)
00256         PG_RETURN_NULL();
00257 
00258     PG_RETURN_INT64(size);
00259 }
00260 
00261 
00262 /*
00263  * calculate size of (one fork of) a relation
00264  *
00265  * Note: we can safely apply this to temp tables of other sessions, so there
00266  * is no check here or at the call sites for that.
00267  */
00268 static int64
00269 calculate_relation_size(RelFileNode *rfn, BackendId backend, ForkNumber forknum)
00270 {
00271     int64       totalsize = 0;
00272     char       *relationpath;
00273     char        pathname[MAXPGPATH];
00274     unsigned int segcount = 0;
00275 
00276     relationpath = relpathbackend(*rfn, backend, forknum);
00277 
00278     for (segcount = 0;; segcount++)
00279     {
00280         struct stat fst;
00281 
00282         CHECK_FOR_INTERRUPTS();
00283 
00284         if (segcount == 0)
00285             snprintf(pathname, MAXPGPATH, "%s",
00286                      relationpath);
00287         else
00288             snprintf(pathname, MAXPGPATH, "%s.%u",
00289                      relationpath, segcount);
00290 
00291         if (stat(pathname, &fst) < 0)
00292         {
00293             if (errno == ENOENT)
00294                 break;
00295             else
00296                 ereport(ERROR,
00297                         (errcode_for_file_access(),
00298                          errmsg("could not stat file \"%s\": %m", pathname)));
00299         }
00300         totalsize += fst.st_size;
00301     }
00302 
00303     return totalsize;
00304 }
00305 
00306 Datum
00307 pg_relation_size(PG_FUNCTION_ARGS)
00308 {
00309     Oid         relOid = PG_GETARG_OID(0);
00310     text       *forkName = PG_GETARG_TEXT_P(1);
00311     Relation    rel;
00312     int64       size;
00313 
00314     rel = try_relation_open(relOid, AccessShareLock);
00315 
00316     /*
00317      * Before 9.2, we used to throw an error if the relation didn't exist, but
00318      * that makes queries like "SELECT pg_relation_size(oid) FROM pg_class"
00319      * less robust, because while we scan pg_class with an MVCC snapshot,
00320      * someone else might drop the table. It's better to return NULL for
00321      * already-dropped tables than throw an error and abort the whole query.
00322      */
00323     if (rel == NULL)
00324         PG_RETURN_NULL();
00325 
00326     size = calculate_relation_size(&(rel->rd_node), rel->rd_backend,
00327                               forkname_to_number(text_to_cstring(forkName)));
00328 
00329     relation_close(rel, AccessShareLock);
00330 
00331     PG_RETURN_INT64(size);
00332 }
00333 
00334 /*
00335  * Calculate total on-disk size of a TOAST relation, including its index.
00336  * Must not be applied to non-TOAST relations.
00337  */
00338 static int64
00339 calculate_toast_table_size(Oid toastrelid)
00340 {
00341     int64       size = 0;
00342     Relation    toastRel;
00343     Relation    toastIdxRel;
00344     ForkNumber  forkNum;
00345 
00346     toastRel = relation_open(toastrelid, AccessShareLock);
00347 
00348     /* toast heap size, including FSM and VM size */
00349     for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
00350         size += calculate_relation_size(&(toastRel->rd_node),
00351                                         toastRel->rd_backend, forkNum);
00352 
00353     /* toast index size, including FSM and VM size */
00354     toastIdxRel = relation_open(toastRel->rd_rel->reltoastidxid, AccessShareLock);
00355     for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
00356         size += calculate_relation_size(&(toastIdxRel->rd_node),
00357                                         toastIdxRel->rd_backend, forkNum);
00358 
00359     relation_close(toastIdxRel, AccessShareLock);
00360     relation_close(toastRel, AccessShareLock);
00361 
00362     return size;
00363 }
00364 
00365 /*
00366  * Calculate total on-disk size of a given table,
00367  * including FSM and VM, plus TOAST table if any.
00368  * Indexes other than the TOAST table's index are not included.
00369  *
00370  * Note that this also behaves sanely if applied to an index or toast table;
00371  * those won't have attached toast tables, but they can have multiple forks.
00372  */
00373 static int64
00374 calculate_table_size(Relation rel)
00375 {
00376     int64       size = 0;
00377     ForkNumber  forkNum;
00378 
00379     /*
00380      * heap size, including FSM and VM
00381      */
00382     for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
00383         size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
00384                                         forkNum);
00385 
00386     /*
00387      * Size of toast relation
00388      */
00389     if (OidIsValid(rel->rd_rel->reltoastrelid))
00390         size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
00391 
00392     return size;
00393 }
00394 
00395 /*
00396  * Calculate total on-disk size of all indexes attached to the given table.
00397  *
00398  * Can be applied safely to an index, but you'll just get zero.
00399  */
00400 static int64
00401 calculate_indexes_size(Relation rel)
00402 {
00403     int64       size = 0;
00404 
00405     /*
00406      * Aggregate all indexes on the given relation
00407      */
00408     if (rel->rd_rel->relhasindex)
00409     {
00410         List       *index_oids = RelationGetIndexList(rel);
00411         ListCell   *cell;
00412 
00413         foreach(cell, index_oids)
00414         {
00415             Oid         idxOid = lfirst_oid(cell);
00416             Relation    idxRel;
00417             ForkNumber  forkNum;
00418 
00419             idxRel = relation_open(idxOid, AccessShareLock);
00420 
00421             for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
00422                 size += calculate_relation_size(&(idxRel->rd_node),
00423                                                 idxRel->rd_backend,
00424                                                 forkNum);
00425 
00426             relation_close(idxRel, AccessShareLock);
00427         }
00428 
00429         list_free(index_oids);
00430     }
00431 
00432     return size;
00433 }
00434 
00435 Datum
00436 pg_table_size(PG_FUNCTION_ARGS)
00437 {
00438     Oid         relOid = PG_GETARG_OID(0);
00439     Relation    rel;
00440     int64       size;
00441 
00442     rel = try_relation_open(relOid, AccessShareLock);
00443 
00444     if (rel == NULL)
00445         PG_RETURN_NULL();
00446 
00447     size = calculate_table_size(rel);
00448 
00449     relation_close(rel, AccessShareLock);
00450 
00451     PG_RETURN_INT64(size);
00452 }
00453 
00454 Datum
00455 pg_indexes_size(PG_FUNCTION_ARGS)
00456 {
00457     Oid         relOid = PG_GETARG_OID(0);
00458     Relation    rel;
00459     int64       size;
00460 
00461     rel = try_relation_open(relOid, AccessShareLock);
00462 
00463     if (rel == NULL)
00464         PG_RETURN_NULL();
00465 
00466     size = calculate_indexes_size(rel);
00467 
00468     relation_close(rel, AccessShareLock);
00469 
00470     PG_RETURN_INT64(size);
00471 }
00472 
00473 /*
00474  *  Compute the on-disk size of all files for the relation,
00475  *  including heap data, index data, toast data, FSM, VM.
00476  */
00477 static int64
00478 calculate_total_relation_size(Relation rel)
00479 {
00480     int64       size;
00481 
00482     /*
00483      * Aggregate the table size, this includes size of the heap, toast and
00484      * toast index with free space and visibility map
00485      */
00486     size = calculate_table_size(rel);
00487 
00488     /*
00489      * Add size of all attached indexes as well
00490      */
00491     size += calculate_indexes_size(rel);
00492 
00493     return size;
00494 }
00495 
00496 Datum
00497 pg_total_relation_size(PG_FUNCTION_ARGS)
00498 {
00499     Oid         relOid = PG_GETARG_OID(0);
00500     Relation    rel;
00501     int64       size;
00502 
00503     rel = try_relation_open(relOid, AccessShareLock);
00504 
00505     if (rel == NULL)
00506         PG_RETURN_NULL();
00507 
00508     size = calculate_total_relation_size(rel);
00509 
00510     relation_close(rel, AccessShareLock);
00511 
00512     PG_RETURN_INT64(size);
00513 }
00514 
00515 /*
00516  * formatting with size units
00517  */
00518 Datum
00519 pg_size_pretty(PG_FUNCTION_ARGS)
00520 {
00521     int64       size = PG_GETARG_INT64(0);
00522     char        buf[64];
00523     int64       limit = 10 * 1024;
00524     int64       limit2 = limit * 2 - 1;
00525 
00526     if (size < limit)
00527         snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size);
00528     else
00529     {
00530         size >>= 9;             /* keep one extra bit for rounding */
00531         if (size < limit2)
00532             snprintf(buf, sizeof(buf), INT64_FORMAT " kB",
00533                      (size + 1) / 2);
00534         else
00535         {
00536             size >>= 10;
00537             if (size < limit2)
00538                 snprintf(buf, sizeof(buf), INT64_FORMAT " MB",
00539                          (size + 1) / 2);
00540             else
00541             {
00542                 size >>= 10;
00543                 if (size < limit2)
00544                     snprintf(buf, sizeof(buf), INT64_FORMAT " GB",
00545                              (size + 1) / 2);
00546                 else
00547                 {
00548                     size >>= 10;
00549                     snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
00550                              (size + 1) / 2);
00551                 }
00552             }
00553         }
00554     }
00555 
00556     PG_RETURN_TEXT_P(cstring_to_text(buf));
00557 }
00558 
00559 static char *
00560 numeric_to_cstring(Numeric n)
00561 {
00562     Datum       d = NumericGetDatum(n);
00563 
00564     return DatumGetCString(DirectFunctionCall1(numeric_out, d));
00565 }
00566 
00567 static Numeric
00568 int64_to_numeric(int64 v)
00569 {
00570     Datum       d = Int64GetDatum(v);
00571 
00572     return DatumGetNumeric(DirectFunctionCall1(int8_numeric, d));
00573 }
00574 
00575 static bool
00576 numeric_is_less(Numeric a, Numeric b)
00577 {
00578     Datum       da = NumericGetDatum(a);
00579     Datum       db = NumericGetDatum(b);
00580 
00581     return DatumGetBool(DirectFunctionCall2(numeric_lt, da, db));
00582 }
00583 
00584 static Numeric
00585 numeric_plus_one_over_two(Numeric n)
00586 {
00587     Datum       d = NumericGetDatum(n);
00588     Datum       one;
00589     Datum       two;
00590     Datum       result;
00591 
00592     one = DirectFunctionCall1(int8_numeric, Int64GetDatum(1));
00593     two = DirectFunctionCall1(int8_numeric, Int64GetDatum(2));
00594     result = DirectFunctionCall2(numeric_add, d, one);
00595     result = DirectFunctionCall2(numeric_div_trunc, result, two);
00596     return DatumGetNumeric(result);
00597 }
00598 
00599 static Numeric
00600 numeric_shift_right(Numeric n, unsigned count)
00601 {
00602     Datum       d = NumericGetDatum(n);
00603     Datum       divisor_int64;
00604     Datum       divisor_numeric;
00605     Datum       result;
00606 
00607     divisor_int64 = Int64GetDatum((int64) (1 << count));
00608     divisor_numeric = DirectFunctionCall1(int8_numeric, divisor_int64);
00609     result = DirectFunctionCall2(numeric_div_trunc, d, divisor_numeric);
00610     return DatumGetNumeric(result);
00611 }
00612 
00613 Datum
00614 pg_size_pretty_numeric(PG_FUNCTION_ARGS)
00615 {
00616     Numeric     size = PG_GETARG_NUMERIC(0);
00617     Numeric     limit,
00618                 limit2;
00619     char       *buf,
00620                *result;
00621 
00622     limit = int64_to_numeric(10 * 1024);
00623     limit2 = int64_to_numeric(10 * 1024 * 2 - 1);
00624 
00625     if (numeric_is_less(size, limit))
00626     {
00627         buf = numeric_to_cstring(size);
00628         result = palloc(strlen(buf) + 7);
00629         strcpy(result, buf);
00630         strcat(result, " bytes");
00631     }
00632     else
00633     {
00634         /* keep one extra bit for rounding */
00635         /* size >>= 9 */
00636         size = numeric_shift_right(size, 9);
00637 
00638         if (numeric_is_less(size, limit2))
00639         {
00640             /* size = (size + 1) / 2 */
00641             size = numeric_plus_one_over_two(size);
00642             buf = numeric_to_cstring(size);
00643             result = palloc(strlen(buf) + 4);
00644             strcpy(result, buf);
00645             strcat(result, " kB");
00646         }
00647         else
00648         {
00649             /* size >>= 10 */
00650             size = numeric_shift_right(size, 10);
00651             if (numeric_is_less(size, limit2))
00652             {
00653                 /* size = (size + 1) / 2 */
00654                 size = numeric_plus_one_over_two(size);
00655                 buf = numeric_to_cstring(size);
00656                 result = palloc(strlen(buf) + 4);
00657                 strcpy(result, buf);
00658                 strcat(result, " MB");
00659             }
00660             else
00661             {
00662                 /* size >>= 10 */
00663                 size = numeric_shift_right(size, 10);
00664 
00665                 if (numeric_is_less(size, limit2))
00666                 {
00667                     /* size = (size + 1) / 2 */
00668                     size = numeric_plus_one_over_two(size);
00669                     buf = numeric_to_cstring(size);
00670                     result = palloc(strlen(buf) + 4);
00671                     strcpy(result, buf);
00672                     strcat(result, " GB");
00673                 }
00674                 else
00675                 {
00676                     /* size >>= 10 */
00677                     size = numeric_shift_right(size, 10);
00678                     /* size = (size + 1) / 2 */
00679                     size = numeric_plus_one_over_two(size);
00680                     buf = numeric_to_cstring(size);
00681                     result = palloc(strlen(buf) + 4);
00682                     strcpy(result, buf);
00683                     strcat(result, " TB");
00684                 }
00685             }
00686         }
00687     }
00688 
00689     PG_RETURN_TEXT_P(cstring_to_text(result));
00690 }
00691 
00692 /*
00693  * Get the filenode of a relation
00694  *
00695  * This is expected to be used in queries like
00696  *      SELECT pg_relation_filenode(oid) FROM pg_class;
00697  * That leads to a couple of choices.  We work from the pg_class row alone
00698  * rather than actually opening each relation, for efficiency.  We don't
00699  * fail if we can't find the relation --- some rows might be visible in
00700  * the query's MVCC snapshot but already dead according to SnapshotNow.
00701  * (Note: we could avoid using the catcache, but there's little point
00702  * because the relation mapper also works "in the now".)  We also don't
00703  * fail if the relation doesn't have storage.  In all these cases it
00704  * seems better to quietly return NULL.
00705  */
00706 Datum
00707 pg_relation_filenode(PG_FUNCTION_ARGS)
00708 {
00709     Oid         relid = PG_GETARG_OID(0);
00710     Oid         result;
00711     HeapTuple   tuple;
00712     Form_pg_class relform;
00713 
00714     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
00715     if (!HeapTupleIsValid(tuple))
00716         PG_RETURN_NULL();
00717     relform = (Form_pg_class) GETSTRUCT(tuple);
00718 
00719     switch (relform->relkind)
00720     {
00721         case RELKIND_RELATION:
00722         case RELKIND_MATVIEW:
00723         case RELKIND_INDEX:
00724         case RELKIND_SEQUENCE:
00725         case RELKIND_TOASTVALUE:
00726             /* okay, these have storage */
00727             if (relform->relfilenode)
00728                 result = relform->relfilenode;
00729             else    /* Consult the relation mapper */
00730                 result = RelationMapOidToFilenode(relid,
00731                                                   relform->relisshared);
00732             break;
00733 
00734         default:
00735             /* no storage, return NULL */
00736             result = InvalidOid;
00737             break;
00738     }
00739 
00740     ReleaseSysCache(tuple);
00741 
00742     if (!OidIsValid(result))
00743         PG_RETURN_NULL();
00744 
00745     PG_RETURN_OID(result);
00746 }
00747 
00748 /*
00749  * Get the pathname (relative to $PGDATA) of a relation
00750  *
00751  * See comments for pg_relation_filenode.
00752  */
00753 Datum
00754 pg_relation_filepath(PG_FUNCTION_ARGS)
00755 {
00756     Oid         relid = PG_GETARG_OID(0);
00757     HeapTuple   tuple;
00758     Form_pg_class relform;
00759     RelFileNode rnode;
00760     BackendId   backend;
00761     char       *path;
00762 
00763     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
00764     if (!HeapTupleIsValid(tuple))
00765         PG_RETURN_NULL();
00766     relform = (Form_pg_class) GETSTRUCT(tuple);
00767 
00768     switch (relform->relkind)
00769     {
00770         case RELKIND_RELATION:
00771         case RELKIND_MATVIEW:
00772         case RELKIND_INDEX:
00773         case RELKIND_SEQUENCE:
00774         case RELKIND_TOASTVALUE:
00775             /* okay, these have storage */
00776 
00777             /* This logic should match RelationInitPhysicalAddr */
00778             if (relform->reltablespace)
00779                 rnode.spcNode = relform->reltablespace;
00780             else
00781                 rnode.spcNode = MyDatabaseTableSpace;
00782             if (rnode.spcNode == GLOBALTABLESPACE_OID)
00783                 rnode.dbNode = InvalidOid;
00784             else
00785                 rnode.dbNode = MyDatabaseId;
00786             if (relform->relfilenode)
00787                 rnode.relNode = relform->relfilenode;
00788             else    /* Consult the relation mapper */
00789                 rnode.relNode = RelationMapOidToFilenode(relid,
00790                                                        relform->relisshared);
00791             break;
00792 
00793         default:
00794             /* no storage, return NULL */
00795             rnode.relNode = InvalidOid;
00796             /* some compilers generate warnings without these next two lines */
00797             rnode.dbNode = InvalidOid;
00798             rnode.spcNode = InvalidOid;
00799             break;
00800     }
00801 
00802     if (!OidIsValid(rnode.relNode))
00803     {
00804         ReleaseSysCache(tuple);
00805         PG_RETURN_NULL();
00806     }
00807 
00808     /* Determine owning backend. */
00809     switch (relform->relpersistence)
00810     {
00811         case RELPERSISTENCE_UNLOGGED:
00812         case RELPERSISTENCE_PERMANENT:
00813             backend = InvalidBackendId;
00814             break;
00815         case RELPERSISTENCE_TEMP:
00816             if (isTempOrToastNamespace(relform->relnamespace))
00817                 backend = MyBackendId;
00818             else
00819             {
00820                 /* Do it the hard way. */
00821                 backend = GetTempNamespaceBackendId(relform->relnamespace);
00822                 Assert(backend != InvalidBackendId);
00823             }
00824             break;
00825         default:
00826             elog(ERROR, "invalid relpersistence: %c", relform->relpersistence);
00827             backend = InvalidBackendId; /* placate compiler */
00828             break;
00829     }
00830 
00831     ReleaseSysCache(tuple);
00832 
00833     path = relpathbackend(rnode, backend, MAIN_FORKNUM);
00834 
00835     PG_RETURN_TEXT_P(cstring_to_text(path));
00836 }
00837 
00838 
00839 /*
00840  * Indicate whether a relation is scannable.
00841  *
00842  * Currently, this is always true except for a materialized view which has not
00843  * been populated.  It is expected that other conditions for allowing a
00844  * materialized view to be scanned will be added in later releases.
00845  */
00846 Datum
00847 pg_relation_is_scannable(PG_FUNCTION_ARGS)
00848 {
00849     Oid         relid;
00850     Relation    relation;
00851     bool        result;
00852 
00853     relid = PG_GETARG_OID(0);
00854     relation = try_relation_open(relid, AccessShareLock);
00855 
00856     if (relation == NULL)
00857         PG_RETURN_BOOL(false);
00858 
00859     result = RelationIsScannable(relation);
00860 
00861     relation_close(relation, AccessShareLock);
00862     PG_RETURN_BOOL(result);
00863 }