00001
00002
00003
00004
00005
00006
00007
00008
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
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
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
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
00097
00098
00099 snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
00100 totalsize = db_dir_size(pathname);
00101
00102
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
00161
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
00175
00176
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
00264
00265
00266
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
00318
00319
00320
00321
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
00336
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
00349 for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
00350 size += calculate_relation_size(&(toastRel->rd_node),
00351 toastRel->rd_backend, forkNum);
00352
00353
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
00367
00368
00369
00370
00371
00372
00373 static int64
00374 calculate_table_size(Relation rel)
00375 {
00376 int64 size = 0;
00377 ForkNumber forkNum;
00378
00379
00380
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
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
00397
00398
00399
00400 static int64
00401 calculate_indexes_size(Relation rel)
00402 {
00403 int64 size = 0;
00404
00405
00406
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
00475
00476
00477 static int64
00478 calculate_total_relation_size(Relation rel)
00479 {
00480 int64 size;
00481
00482
00483
00484
00485
00486 size = calculate_table_size(rel);
00487
00488
00489
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
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;
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
00635
00636 size = numeric_shift_right(size, 9);
00637
00638 if (numeric_is_less(size, limit2))
00639 {
00640
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
00650 size = numeric_shift_right(size, 10);
00651 if (numeric_is_less(size, limit2))
00652 {
00653
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
00663 size = numeric_shift_right(size, 10);
00664
00665 if (numeric_is_less(size, limit2))
00666 {
00667
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
00677 size = numeric_shift_right(size, 10);
00678
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
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
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
00727 if (relform->relfilenode)
00728 result = relform->relfilenode;
00729 else
00730 result = RelationMapOidToFilenode(relid,
00731 relform->relisshared);
00732 break;
00733
00734 default:
00735
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
00750
00751
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
00776
00777
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
00789 rnode.relNode = RelationMapOidToFilenode(relid,
00790 relform->relisshared);
00791 break;
00792
00793 default:
00794
00795 rnode.relNode = InvalidOid;
00796
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
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
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;
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
00841
00842
00843
00844
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 }