Header And Logo

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

pgstatindex.c

Go to the documentation of this file.
00001 /*
00002  * contrib/pgstattuple/pgstatindex.c
00003  *
00004  *
00005  * pgstatindex
00006  *
00007  * Copyright (c) 2006 Satoshi Nagayasu <[email protected]>
00008  *
00009  * Permission to use, copy, modify, and distribute this software and
00010  * its documentation for any purpose, without fee, and without a
00011  * written agreement is hereby granted, provided that the above
00012  * copyright notice and this paragraph and the following two
00013  * paragraphs appear in all copies.
00014  *
00015  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
00016  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
00017  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
00018  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
00019  * OF THE POSSIBILITY OF SUCH DAMAGE.
00020  *
00021  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00023  * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
00024  * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
00025  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
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 /* note: BlockNumber is unsigned, hence can't be negative */
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  * A structure for a whole btree index statistics
00066  * used by pgstatindex().
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  * A structure for a whole GIN index statistics
00089  * used by pgstatginindex().
00090  * ------------------------------------------------
00091  */
00092 typedef struct GinIndexStat
00093 {
00094     int32       version;
00095 
00096     BlockNumber pending_pages;
00097     int64       pending_tuples;
00098 } GinIndexStat;
00099 
00100 /* ------------------------------------------------------
00101  * pgstatindex()
00102  *
00103  * Usage: SELECT * FROM pgstatindex('t1_pkey');
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      * Reject attempts to read non-local temporary relations; we would be
00132      * likely to get wrong data since we have no visibility into the owning
00133      * session's local buffers.
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      * Read metapage
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     /* -- init counters -- */
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      * Scan all blocks except the metapage
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         /* Read and lock buffer */
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         /* Determine page type, and update totals */
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              * If the next leaf is on an earlier block, it means a
00201              * fragmentation.
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         /* Unlock and release buffer */
00216         LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
00217         ReleaseBuffer(buffer);
00218     }
00219 
00220     relation_close(rel, AccessShareLock);
00221 
00222     /*----------------------------
00223      * Build a result tuple
00224      *----------------------------
00225      */
00226     {
00227         TupleDesc   tupleDesc;
00228         int         j;
00229         char       *values[10];
00230         HeapTuple   tuple;
00231 
00232         /* Build a tuple descriptor for our result type */
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  * pg_relpages()
00282  *
00283  * Get the number of pages of the table/index.
00284  *
00285  * Usage: SELECT pg_relpages('t1');
00286  *        SELECT pg_relpages('t1_pkey');
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     /* note: this will work OK on non-local temp tables */
00306 
00307     relpages = RelationGetNumberOfBlocks(rel);
00308 
00309     relation_close(rel, AccessShareLock);
00310 
00311     PG_RETURN_INT64(relpages);
00312 }
00313 
00314 /* ------------------------------------------------------
00315  * pgstatginindex()
00316  *
00317  * Usage: SELECT * FROM pgstatginindex('ginindex');
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      * Reject attempts to read non-local temporary relations; we would be
00348      * likely to get wrong data since we have no visibility into the owning
00349      * session's local buffers.
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      * Read metapage
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      * Build a tuple descriptor for our result type
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      * Build and return the tuple
00383      */
00384     tuple = heap_form_tuple(tupleDesc, values, nulls);
00385     result = HeapTupleGetDatum(tuple);
00386 
00387     PG_RETURN_DATUM(result);
00388 }