00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "postgres.h"
00026
00027 #include "access/gist_private.h"
00028 #include "access/hash.h"
00029 #include "access/nbtree.h"
00030 #include "access/relscan.h"
00031 #include "catalog/namespace.h"
00032 #include "funcapi.h"
00033 #include "miscadmin.h"
00034 #include "storage/bufmgr.h"
00035 #include "storage/lmgr.h"
00036 #include "utils/builtins.h"
00037 #include "utils/tqual.h"
00038
00039
00040 PG_MODULE_MAGIC;
00041
00042 PG_FUNCTION_INFO_V1(pgstattuple);
00043 PG_FUNCTION_INFO_V1(pgstattuplebyid);
00044
00045 extern Datum pgstattuple(PG_FUNCTION_ARGS);
00046 extern Datum pgstattuplebyid(PG_FUNCTION_ARGS);
00047
00048
00049
00050
00051
00052
00053
00054 typedef struct pgstattuple_type
00055 {
00056 uint64 table_len;
00057 uint64 tuple_count;
00058 uint64 tuple_len;
00059 uint64 dead_tuple_count;
00060 uint64 dead_tuple_len;
00061 uint64 free_space;
00062 } pgstattuple_type;
00063
00064 typedef void (*pgstat_page) (pgstattuple_type *, Relation, BlockNumber,
00065 BufferAccessStrategy);
00066
00067 static Datum build_pgstattuple_type(pgstattuple_type *stat,
00068 FunctionCallInfo fcinfo);
00069 static Datum pgstat_relation(Relation rel, FunctionCallInfo fcinfo);
00070 static Datum pgstat_heap(Relation rel, FunctionCallInfo fcinfo);
00071 static void pgstat_btree_page(pgstattuple_type *stat,
00072 Relation rel, BlockNumber blkno,
00073 BufferAccessStrategy bstrategy);
00074 static void pgstat_hash_page(pgstattuple_type *stat,
00075 Relation rel, BlockNumber blkno,
00076 BufferAccessStrategy bstrategy);
00077 static void pgstat_gist_page(pgstattuple_type *stat,
00078 Relation rel, BlockNumber blkno,
00079 BufferAccessStrategy bstrategy);
00080 static Datum pgstat_index(Relation rel, BlockNumber start,
00081 pgstat_page pagefn, FunctionCallInfo fcinfo);
00082 static void pgstat_index_page(pgstattuple_type *stat, Page page,
00083 OffsetNumber minoff, OffsetNumber maxoff);
00084
00085
00086
00087
00088 static Datum
00089 build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo)
00090 {
00091 #define NCOLUMNS 9
00092 #define NCHARS 32
00093
00094 HeapTuple tuple;
00095 char *values[NCOLUMNS];
00096 char values_buf[NCOLUMNS][NCHARS];
00097 int i;
00098 double tuple_percent;
00099 double dead_tuple_percent;
00100 double free_percent;
00101 TupleDesc tupdesc;
00102 AttInMetadata *attinmeta;
00103
00104
00105 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
00106 elog(ERROR, "return type must be a row type");
00107
00108
00109
00110
00111
00112 attinmeta = TupleDescGetAttInMetadata(tupdesc);
00113
00114 if (stat->table_len == 0)
00115 {
00116 tuple_percent = 0.0;
00117 dead_tuple_percent = 0.0;
00118 free_percent = 0.0;
00119 }
00120 else
00121 {
00122 tuple_percent = 100.0 * stat->tuple_len / stat->table_len;
00123 dead_tuple_percent = 100.0 * stat->dead_tuple_len / stat->table_len;
00124 free_percent = 100.0 * stat->free_space / stat->table_len;
00125 }
00126
00127
00128
00129
00130
00131
00132 for (i = 0; i < NCOLUMNS; i++)
00133 values[i] = values_buf[i];
00134 i = 0;
00135 snprintf(values[i++], NCHARS, INT64_FORMAT, stat->table_len);
00136 snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_count);
00137 snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_len);
00138 snprintf(values[i++], NCHARS, "%.2f", tuple_percent);
00139 snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_count);
00140 snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_len);
00141 snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent);
00142 snprintf(values[i++], NCHARS, INT64_FORMAT, stat->free_space);
00143 snprintf(values[i++], NCHARS, "%.2f", free_percent);
00144
00145
00146 tuple = BuildTupleFromCStrings(attinmeta, values);
00147
00148
00149 return HeapTupleGetDatum(tuple);
00150 }
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162 Datum
00163 pgstattuple(PG_FUNCTION_ARGS)
00164 {
00165 text *relname = PG_GETARG_TEXT_P(0);
00166 RangeVar *relrv;
00167 Relation rel;
00168
00169 if (!superuser())
00170 ereport(ERROR,
00171 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00172 (errmsg("must be superuser to use pgstattuple functions"))));
00173
00174
00175 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
00176 rel = relation_openrv(relrv, AccessShareLock);
00177
00178 PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
00179 }
00180
00181 Datum
00182 pgstattuplebyid(PG_FUNCTION_ARGS)
00183 {
00184 Oid relid = PG_GETARG_OID(0);
00185 Relation rel;
00186
00187 if (!superuser())
00188 ereport(ERROR,
00189 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00190 (errmsg("must be superuser to use pgstattuple functions"))));
00191
00192
00193 rel = relation_open(relid, AccessShareLock);
00194
00195 PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
00196 }
00197
00198
00199
00200
00201 static Datum
00202 pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
00203 {
00204 const char *err;
00205
00206
00207
00208
00209
00210
00211 if (RELATION_IS_OTHER_TEMP(rel))
00212 ereport(ERROR,
00213 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00214 errmsg("cannot access temporary tables of other sessions")));
00215
00216 switch (rel->rd_rel->relkind)
00217 {
00218 case RELKIND_RELATION:
00219 case RELKIND_MATVIEW:
00220 case RELKIND_TOASTVALUE:
00221 case RELKIND_SEQUENCE:
00222 return pgstat_heap(rel, fcinfo);
00223 case RELKIND_INDEX:
00224 switch (rel->rd_rel->relam)
00225 {
00226 case BTREE_AM_OID:
00227 return pgstat_index(rel, BTREE_METAPAGE + 1,
00228 pgstat_btree_page, fcinfo);
00229 case HASH_AM_OID:
00230 return pgstat_index(rel, HASH_METAPAGE + 1,
00231 pgstat_hash_page, fcinfo);
00232 case GIST_AM_OID:
00233 return pgstat_index(rel, GIST_ROOT_BLKNO + 1,
00234 pgstat_gist_page, fcinfo);
00235 case GIN_AM_OID:
00236 err = "gin index";
00237 break;
00238 case SPGIST_AM_OID:
00239 err = "spgist index";
00240 break;
00241 default:
00242 err = "unknown index";
00243 break;
00244 }
00245 break;
00246 case RELKIND_VIEW:
00247 err = "view";
00248 break;
00249 case RELKIND_COMPOSITE_TYPE:
00250 err = "composite type";
00251 break;
00252 case RELKIND_FOREIGN_TABLE:
00253 err = "foreign table";
00254 break;
00255 default:
00256 err = "unknown";
00257 break;
00258 }
00259
00260 ereport(ERROR,
00261 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00262 errmsg("\"%s\" (%s) is not supported",
00263 RelationGetRelationName(rel), err)));
00264 return 0;
00265 }
00266
00267
00268
00269
00270 static Datum
00271 pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
00272 {
00273 HeapScanDesc scan;
00274 HeapTuple tuple;
00275 BlockNumber nblocks;
00276 BlockNumber block = 0;
00277 BlockNumber tupblock;
00278 Buffer buffer;
00279 pgstattuple_type stat = {0};
00280 BufferAccessStrategy bstrategy;
00281
00282
00283 scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
00284
00285 nblocks = scan->rs_nblocks;
00286
00287
00288 bstrategy = GetAccessStrategy(BAS_BULKREAD);
00289 scan->rs_strategy = bstrategy;
00290
00291
00292 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
00293 {
00294 CHECK_FOR_INTERRUPTS();
00295
00296
00297 LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
00298
00299 if (HeapTupleSatisfiesVisibility(tuple, SnapshotNow, scan->rs_cbuf))
00300 {
00301 stat.tuple_len += tuple->t_len;
00302 stat.tuple_count++;
00303 }
00304 else
00305 {
00306 stat.dead_tuple_len += tuple->t_len;
00307 stat.dead_tuple_count++;
00308 }
00309
00310 LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
00311
00312
00313
00314
00315
00316
00317
00318 tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid);
00319
00320 while (block <= tupblock)
00321 {
00322 CHECK_FOR_INTERRUPTS();
00323
00324 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block, RBM_NORMAL, bstrategy);
00325 LockBuffer(buffer, BUFFER_LOCK_SHARE);
00326 stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
00327 UnlockReleaseBuffer(buffer);
00328 block++;
00329 }
00330 }
00331 heap_endscan(scan);
00332
00333 while (block < nblocks)
00334 {
00335 CHECK_FOR_INTERRUPTS();
00336
00337 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block, RBM_NORMAL, bstrategy);
00338 LockBuffer(buffer, BUFFER_LOCK_SHARE);
00339 stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
00340 UnlockReleaseBuffer(buffer);
00341 block++;
00342 }
00343
00344 relation_close(rel, AccessShareLock);
00345
00346 stat.table_len = (uint64) nblocks *BLCKSZ;
00347
00348 return build_pgstattuple_type(&stat, fcinfo);
00349 }
00350
00351
00352
00353
00354 static void
00355 pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
00356 BufferAccessStrategy bstrategy)
00357 {
00358 Buffer buf;
00359 Page page;
00360
00361 buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
00362 LockBuffer(buf, BT_READ);
00363 page = BufferGetPage(buf);
00364
00365
00366 if (PageIsNew(page))
00367 {
00368
00369 stat->free_space += BLCKSZ;
00370 }
00371 else
00372 {
00373 BTPageOpaque opaque;
00374
00375 opaque = (BTPageOpaque) PageGetSpecialPointer(page);
00376 if (opaque->btpo_flags & (BTP_DELETED | BTP_HALF_DEAD))
00377 {
00378
00379 stat->free_space += BLCKSZ;
00380 }
00381 else if (P_ISLEAF(opaque))
00382 {
00383 pgstat_index_page(stat, page, P_FIRSTDATAKEY(opaque),
00384 PageGetMaxOffsetNumber(page));
00385 }
00386 else
00387 {
00388
00389 }
00390 }
00391
00392 _bt_relbuf(rel, buf);
00393 }
00394
00395
00396
00397
00398 static void
00399 pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
00400 BufferAccessStrategy bstrategy)
00401 {
00402 Buffer buf;
00403 Page page;
00404
00405 _hash_getlock(rel, blkno, HASH_SHARE);
00406 buf = _hash_getbuf_with_strategy(rel, blkno, HASH_READ, 0, bstrategy);
00407 page = BufferGetPage(buf);
00408
00409 if (PageGetSpecialSize(page) == MAXALIGN(sizeof(HashPageOpaqueData)))
00410 {
00411 HashPageOpaque opaque;
00412
00413 opaque = (HashPageOpaque) PageGetSpecialPointer(page);
00414 switch (opaque->hasho_flag)
00415 {
00416 case LH_UNUSED_PAGE:
00417 stat->free_space += BLCKSZ;
00418 break;
00419 case LH_BUCKET_PAGE:
00420 case LH_OVERFLOW_PAGE:
00421 pgstat_index_page(stat, page, FirstOffsetNumber,
00422 PageGetMaxOffsetNumber(page));
00423 break;
00424 case LH_BITMAP_PAGE:
00425 case LH_META_PAGE:
00426 default:
00427 break;
00428 }
00429 }
00430 else
00431 {
00432
00433 }
00434
00435 _hash_relbuf(rel, buf);
00436 _hash_droplock(rel, blkno, HASH_SHARE);
00437 }
00438
00439
00440
00441
00442 static void
00443 pgstat_gist_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
00444 BufferAccessStrategy bstrategy)
00445 {
00446 Buffer buf;
00447 Page page;
00448
00449 buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
00450 LockBuffer(buf, GIST_SHARE);
00451 gistcheckpage(rel, buf);
00452 page = BufferGetPage(buf);
00453
00454 if (GistPageIsLeaf(page))
00455 {
00456 pgstat_index_page(stat, page, FirstOffsetNumber,
00457 PageGetMaxOffsetNumber(page));
00458 }
00459 else
00460 {
00461
00462 }
00463
00464 UnlockReleaseBuffer(buf);
00465 }
00466
00467
00468
00469
00470 static Datum
00471 pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
00472 FunctionCallInfo fcinfo)
00473 {
00474 BlockNumber nblocks;
00475 BlockNumber blkno;
00476 BufferAccessStrategy bstrategy;
00477 pgstattuple_type stat = {0};
00478
00479
00480 bstrategy = GetAccessStrategy(BAS_BULKREAD);
00481
00482 blkno = start;
00483 for (;;)
00484 {
00485
00486 LockRelationForExtension(rel, ExclusiveLock);
00487 nblocks = RelationGetNumberOfBlocks(rel);
00488 UnlockRelationForExtension(rel, ExclusiveLock);
00489
00490
00491 if (blkno >= nblocks)
00492 {
00493 stat.table_len = (uint64) nblocks *BLCKSZ;
00494
00495 break;
00496 }
00497
00498 for (; blkno < nblocks; blkno++)
00499 {
00500 CHECK_FOR_INTERRUPTS();
00501
00502 pagefn(&stat, rel, blkno, bstrategy);
00503 }
00504 }
00505
00506 relation_close(rel, AccessShareLock);
00507
00508 return build_pgstattuple_type(&stat, fcinfo);
00509 }
00510
00511
00512
00513
00514 static void
00515 pgstat_index_page(pgstattuple_type *stat, Page page,
00516 OffsetNumber minoff, OffsetNumber maxoff)
00517 {
00518 OffsetNumber i;
00519
00520 stat->free_space += PageGetFreeSpace(page);
00521
00522 for (i = minoff; i <= maxoff; i = OffsetNumberNext(i))
00523 {
00524 ItemId itemid = PageGetItemId(page, i);
00525
00526 if (ItemIdIsDead(itemid))
00527 {
00528 stat->dead_tuple_count++;
00529 stat->dead_tuple_len += ItemIdGetLength(itemid);
00530 }
00531 else
00532 {
00533 stat->tuple_count++;
00534 stat->tuple_len += ItemIdGetLength(itemid);
00535 }
00536 }
00537 }