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
00026
00027
00028 #include "postgres.h"
00029
00030 #include "access/gin_private.h"
00031 #include "access/heapam.h"
00032 #include "access/htup_details.h"
00033 #include "access/nbtree.h"
00034 #include "catalog/namespace.h"
00035 #include "funcapi.h"
00036 #include "miscadmin.h"
00037 #include "storage/bufmgr.h"
00038 #include "utils/builtins.h"
00039 #include "utils/rel.h"
00040
00041
00042 extern Datum pgstatindex(PG_FUNCTION_ARGS);
00043 extern Datum pg_relpages(PG_FUNCTION_ARGS);
00044 extern Datum pgstatginindex(PG_FUNCTION_ARGS);
00045
00046 PG_FUNCTION_INFO_V1(pgstatindex);
00047 PG_FUNCTION_INFO_V1(pg_relpages);
00048 PG_FUNCTION_INFO_V1(pgstatginindex);
00049
00050 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
00051 #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
00052 #define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID)
00053
00054 #define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
00055 if ( !(FirstOffsetNumber <= (offnum) && \
00056 (offnum) <= PageGetMaxOffsetNumber(pg)) ) \
00057 elog(ERROR, "page offset number out of range"); }
00058
00059
00060 #define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
00061 if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \
00062 elog(ERROR, "block number out of range"); }
00063
00064
00065
00066
00067
00068
00069 typedef struct BTIndexStat
00070 {
00071 uint32 version;
00072 uint32 level;
00073 BlockNumber root_blkno;
00074
00075 uint64 root_pages;
00076 uint64 internal_pages;
00077 uint64 leaf_pages;
00078 uint64 empty_pages;
00079 uint64 deleted_pages;
00080
00081 uint64 max_avail;
00082 uint64 free_space;
00083
00084 uint64 fragments;
00085 } BTIndexStat;
00086
00087
00088
00089
00090
00091
00092 typedef struct GinIndexStat
00093 {
00094 int32 version;
00095
00096 BlockNumber pending_pages;
00097 int64 pending_tuples;
00098 } GinIndexStat;
00099
00100
00101
00102
00103
00104
00105
00106 Datum
00107 pgstatindex(PG_FUNCTION_ARGS)
00108 {
00109 text *relname = PG_GETARG_TEXT_P(0);
00110 Relation rel;
00111 RangeVar *relrv;
00112 Datum result;
00113 BlockNumber nblocks;
00114 BlockNumber blkno;
00115 BTIndexStat indexStat;
00116 BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
00117
00118 if (!superuser())
00119 ereport(ERROR,
00120 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00121 (errmsg("must be superuser to use pgstattuple functions"))));
00122
00123 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
00124 rel = relation_openrv(relrv, AccessShareLock);
00125
00126 if (!IS_INDEX(rel) || !IS_BTREE(rel))
00127 elog(ERROR, "relation \"%s\" is not a btree index",
00128 RelationGetRelationName(rel));
00129
00130
00131
00132
00133
00134
00135 if (RELATION_IS_OTHER_TEMP(rel))
00136 ereport(ERROR,
00137 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00138 errmsg("cannot access temporary tables of other sessions")));
00139
00140
00141
00142
00143 {
00144 Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 0, RBM_NORMAL, bstrategy);
00145 Page page = BufferGetPage(buffer);
00146 BTMetaPageData *metad = BTPageGetMeta(page);
00147
00148 indexStat.version = metad->btm_version;
00149 indexStat.level = metad->btm_level;
00150 indexStat.root_blkno = metad->btm_root;
00151
00152 ReleaseBuffer(buffer);
00153 }
00154
00155
00156 indexStat.root_pages = 0;
00157 indexStat.internal_pages = 0;
00158 indexStat.leaf_pages = 0;
00159 indexStat.empty_pages = 0;
00160 indexStat.deleted_pages = 0;
00161
00162 indexStat.max_avail = 0;
00163 indexStat.free_space = 0;
00164
00165 indexStat.fragments = 0;
00166
00167
00168
00169
00170 nblocks = RelationGetNumberOfBlocks(rel);
00171
00172 for (blkno = 1; blkno < nblocks; blkno++)
00173 {
00174 Buffer buffer;
00175 Page page;
00176 BTPageOpaque opaque;
00177
00178 CHECK_FOR_INTERRUPTS();
00179
00180
00181 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
00182 LockBuffer(buffer, BUFFER_LOCK_SHARE);
00183
00184 page = BufferGetPage(buffer);
00185 opaque = (BTPageOpaque) PageGetSpecialPointer(page);
00186
00187
00188
00189 if (P_ISLEAF(opaque))
00190 {
00191 int max_avail;
00192
00193 max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
00194 indexStat.max_avail += max_avail;
00195 indexStat.free_space += PageGetFreeSpace(page);
00196
00197 indexStat.leaf_pages++;
00198
00199
00200
00201
00202
00203 if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno)
00204 indexStat.fragments++;
00205 }
00206 else if (P_ISDELETED(opaque))
00207 indexStat.deleted_pages++;
00208 else if (P_IGNORE(opaque))
00209 indexStat.empty_pages++;
00210 else if (P_ISROOT(opaque))
00211 indexStat.root_pages++;
00212 else
00213 indexStat.internal_pages++;
00214
00215
00216 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
00217 ReleaseBuffer(buffer);
00218 }
00219
00220 relation_close(rel, AccessShareLock);
00221
00222
00223
00224
00225
00226 {
00227 TupleDesc tupleDesc;
00228 int j;
00229 char *values[10];
00230 HeapTuple tuple;
00231
00232
00233 if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
00234 elog(ERROR, "return type must be a row type");
00235
00236 j = 0;
00237 values[j] = palloc(32);
00238 snprintf(values[j++], 32, "%d", indexStat.version);
00239 values[j] = palloc(32);
00240 snprintf(values[j++], 32, "%d", indexStat.level);
00241 values[j] = palloc(32);
00242 snprintf(values[j++], 32, INT64_FORMAT,
00243 (indexStat.root_pages +
00244 indexStat.leaf_pages +
00245 indexStat.internal_pages +
00246 indexStat.deleted_pages +
00247 indexStat.empty_pages) * BLCKSZ);
00248 values[j] = palloc(32);
00249 snprintf(values[j++], 32, "%u", indexStat.root_blkno);
00250 values[j] = palloc(32);
00251 snprintf(values[j++], 32, INT64_FORMAT, indexStat.internal_pages);
00252 values[j] = palloc(32);
00253 snprintf(values[j++], 32, INT64_FORMAT, indexStat.leaf_pages);
00254 values[j] = palloc(32);
00255 snprintf(values[j++], 32, INT64_FORMAT, indexStat.empty_pages);
00256 values[j] = palloc(32);
00257 snprintf(values[j++], 32, INT64_FORMAT, indexStat.deleted_pages);
00258 values[j] = palloc(32);
00259 if (indexStat.max_avail > 0)
00260 snprintf(values[j++], 32, "%.2f",
00261 100.0 - (double) indexStat.free_space / (double) indexStat.max_avail * 100.0);
00262 else
00263 snprintf(values[j++], 32, "NaN");
00264 values[j] = palloc(32);
00265 if (indexStat.leaf_pages > 0)
00266 snprintf(values[j++], 32, "%.2f",
00267 (double) indexStat.fragments / (double) indexStat.leaf_pages * 100.0);
00268 else
00269 snprintf(values[j++], 32, "NaN");
00270
00271 tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
00272 values);
00273
00274 result = HeapTupleGetDatum(tuple);
00275 }
00276
00277 PG_RETURN_DATUM(result);
00278 }
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289 Datum
00290 pg_relpages(PG_FUNCTION_ARGS)
00291 {
00292 text *relname = PG_GETARG_TEXT_P(0);
00293 int64 relpages;
00294 Relation rel;
00295 RangeVar *relrv;
00296
00297 if (!superuser())
00298 ereport(ERROR,
00299 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00300 (errmsg("must be superuser to use pgstattuple functions"))));
00301
00302 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
00303 rel = relation_openrv(relrv, AccessShareLock);
00304
00305
00306
00307 relpages = RelationGetNumberOfBlocks(rel);
00308
00309 relation_close(rel, AccessShareLock);
00310
00311 PG_RETURN_INT64(relpages);
00312 }
00313
00314
00315
00316
00317
00318
00319
00320 Datum
00321 pgstatginindex(PG_FUNCTION_ARGS)
00322 {
00323 Oid relid = PG_GETARG_OID(0);
00324 Relation rel;
00325 Buffer buffer;
00326 Page page;
00327 GinMetaPageData *metadata;
00328 GinIndexStat stats;
00329 HeapTuple tuple;
00330 TupleDesc tupleDesc;
00331 Datum values[3];
00332 bool nulls[3] = {false, false, false};
00333 Datum result;
00334
00335 if (!superuser())
00336 ereport(ERROR,
00337 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00338 (errmsg("must be superuser to use pgstattuple functions"))));
00339
00340 rel = relation_open(relid, AccessShareLock);
00341
00342 if (!IS_INDEX(rel) || !IS_GIN(rel))
00343 elog(ERROR, "relation \"%s\" is not a GIN index",
00344 RelationGetRelationName(rel));
00345
00346
00347
00348
00349
00350
00351 if (RELATION_IS_OTHER_TEMP(rel))
00352 ereport(ERROR,
00353 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00354 errmsg("cannot access temporary indexes of other sessions")));
00355
00356
00357
00358
00359 buffer = ReadBuffer(rel, GIN_METAPAGE_BLKNO);
00360 LockBuffer(buffer, GIN_SHARE);
00361 page = BufferGetPage(buffer);
00362 metadata = GinPageGetMeta(page);
00363
00364 stats.version = metadata->ginVersion;
00365 stats.pending_pages = metadata->nPendingPages;
00366 stats.pending_tuples = metadata->nPendingHeapTuples;
00367
00368 UnlockReleaseBuffer(buffer);
00369 relation_close(rel, AccessShareLock);
00370
00371
00372
00373
00374 if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
00375 elog(ERROR, "return type must be a row type");
00376
00377 values[0] = Int32GetDatum(stats.version);
00378 values[1] = UInt32GetDatum(stats.pending_pages);
00379 values[2] = Int64GetDatum(stats.pending_tuples);
00380
00381
00382
00383
00384 tuple = heap_form_tuple(tupleDesc, values, nulls);
00385 result = HeapTupleGetDatum(tuple);
00386
00387 PG_RETURN_DATUM(result);
00388 }