diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile new file mode 100644 index 63fab95..f63b92f *** a/contrib/pageinspect/Makefile --- b/contrib/pageinspect/Makefile *************** *** 1,7 **** # contrib/pageinspect/Makefile MODULE_big = pageinspect ! OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o EXTENSION = pageinspect DATA = pageinspect--1.0.sql pageinspect--1.1.sql \ --- 1,7 ---- # contrib/pageinspect/Makefile MODULE_big = pageinspect ! OBJS = rawpage.o heapfuncs.o btreefuncs.o idxfuncs.o fsmfuncs.o EXTENSION = pageinspect DATA = pageinspect--1.0.sql pageinspect--1.1.sql \ diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c new file mode 100644 index 9c0b0fb..b399b85 *** a/contrib/pageinspect/btreefuncs.c --- b/contrib/pageinspect/btreefuncs.c *************** *** 34,41 **** #include "utils/builtins.h" #include "utils/rel.h" - #include "btreefuncs.h" - extern Datum bt_metap(PG_FUNCTION_ARGS); extern Datum bt_page_items(PG_FUNCTION_ARGS); extern Datum bt_page_stats(PG_FUNCTION_ARGS); --- 34,39 ---- *************** GetBTPageStatistics(BlockNumber blkno, B *** 156,216 **** stat->avg_item_size = 0; } - /*------------------------------------------------ - * GetBTRelationFreeSpace - * - * Get the free space for a btree index. - * This is a helper function for relation_free_space() - *------------------------------------------------ - */ - float4 - GetBTRelationFreeSpace(Relation rel) - { - BTPageStat stat; - - Buffer buffer; - BlockNumber blkno; - BlockNumber totalBlcksInRelation = RelationGetNumberOfBlocks(rel); - BlockNumber totalBlcksCounted = 0; - Size free_space = 0; - double free_percent = 0; - - BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); - - /* Skip page 0 because it is a metapage */ - for (blkno = 1; blkno < totalBlcksInRelation; blkno++) - { - buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); - /* - * get the statistics of the indexes and use that info - * to determine free space on the page - */ - GetBTPageStatistics(blkno, buffer, &stat); - /* - * Consider pages DELETED and HALF_DEAD as empty, - * besides those only consider LEAF pages - */ - if (stat.type == 'd' || stat.type == 'e') - { - free_space += stat.page_size; - totalBlcksCounted++; - } - else if (stat.type == 'l') - { - free_space += stat.free_size; - totalBlcksCounted++; - } - - ReleaseBuffer(buffer); - } - - if (totalBlcksCounted > 0) - free_percent = ((float4) free_space) / (totalBlcksCounted * BLCKSZ); - - return free_percent; - } - - /* ----------------------------------------------- * bt_page() * --- 154,159 ---- diff --git a/contrib/pageinspect/btreefuncs.h b/contrib/pageinspect/btreefuncs.h new file mode . index 549f878..e69de29 *** a/contrib/pageinspect/btreefuncs.h --- b/contrib/pageinspect/btreefuncs.h *************** *** 1,5 **** - /* - * contrib/pageinspect/btreefuncs.h - */ - - float4 GetBTRelationFreeSpace(Relation); --- 0 ---- diff --git a/contrib/pageinspect/idxfuncs.c b/contrib/pageinspect/idxfuncs.c new file mode 100644 index ...6533429 *** a/contrib/pageinspect/idxfuncs.c --- b/contrib/pageinspect/idxfuncs.c *************** *** 0 **** --- 1,313 ---- + /* + * contrib/pageinspect/idxfuncs.c + * + * + * idxfuncs.c + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose, without fee, and without a + * written agreement is hereby granted, provided that the above + * copyright notice and this paragraph and the following two + * paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS + * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + #include "postgres.h" + + #include "access/gin_private.h" + #include "access/gist_private.h" + #include "access/hash.h" + #include "access/nbtree.h" + #include "access/spgist_private.h" + #include "funcapi.h" + #include "utils/rel.h" + + #include "idxfuncs.h" + + /*------------------------------------------------ + * GetIndexRelationFreeSpace + * + * Look the type of the index and call the appropiate + * function to get the free space + *------------------------------------------------ + */ + float4 + GetIndexRelationFreeSpace(Relation rel) + { + switch (rel->rd_rel->relam) + { + case BTREE_AM_OID: + return GetBTRelationFreeSpace(rel); + case HASH_AM_OID: + return GetHashRelationFreeSpace(rel); + case GIST_AM_OID: + return GetGistRelationFreeSpace(rel); + case GIN_AM_OID: + return GetGinRelationFreeSpace(rel); + case SPGIST_AM_OID: + return GetSpGistRelationFreeSpace(rel); + default: + elog(ERROR, "Unknown index type %d", rel->rd_rel->relam); + } + + return 0; + } + + /*------------------------------------------------ + * GetBTRelationFreeSpace + * + * Get the free space for a btree index. + * This is a helper function for relation_free_space() + *------------------------------------------------ + */ + float4 + GetBTRelationFreeSpace(Relation rel) + { + BlockNumber blkno; + BlockNumber totalBlcksInRelation = RelationGetNumberOfBlocks(rel); + BlockNumber totalBlcksCounted = 0; + Size free_space = 0; + double free_percent = 0; + + BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Start just after the metapage */ + for (blkno = BTREE_METAPAGE + 1; blkno < totalBlcksInRelation; blkno++) + { + Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); + Page page = BufferGetPage(buffer); + BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + /* + * Consider pages DELETED and HALF_DEAD as empty, + * besides those only consider LEAF pages + */ + if (P_ISDELETED(opaque) || P_ISHALFDEAD(opaque)) + { + free_space += PageGetPageSize(page); + totalBlcksCounted++; + } + else if (P_ISLEAF(opaque)) + { + free_space += PageGetFreeSpace(page); + totalBlcksCounted++; + } + + ReleaseBuffer(buffer); + } + + if (totalBlcksCounted > 0) + free_percent = ((float4) free_space) / (totalBlcksCounted * BLCKSZ); + + return free_percent; + } + + + /*------------------------------------------------ + * GetHashRelationFreeSpace + * + * Get the free space for a Hash index. + * This is a helper function for relation_free_space() + *------------------------------------------------ + */ + float4 + GetHashRelationFreeSpace(Relation rel) + { + BlockNumber blkno; + BlockNumber totalBlcksInRelation = RelationGetNumberOfBlocks(rel); + BlockNumber totalBlcksCounted = 0; + Size free_space = 0; + double free_percent = 0; + + BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Start just after the metapage */ + for (blkno = HASH_METAPAGE + 1; blkno < totalBlcksInRelation; blkno++) + { + Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); + Page page = BufferGetPage(buffer); + HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page); + + /* + * Consider pages LH_UNUSED_PAGE as empty, + * besides that consider the free space in LH_OVERFLOW_PAGE and LH_BUCKET_PAGE pages + */ + if (opaque->hasho_flag == LH_UNUSED_PAGE) + { + free_space += PageGetPageSize(page); + totalBlcksCounted++; + } + else if (opaque->hasho_flag == LH_OVERFLOW_PAGE || opaque->hasho_flag == LH_BUCKET_PAGE) + { + free_space += PageGetFreeSpace(page); + totalBlcksCounted++; + } + + ReleaseBuffer(buffer); + } + + if (totalBlcksCounted > 0) + free_percent = ((float4) free_space) / (totalBlcksCounted * BLCKSZ); + + return free_percent; + } + + /*------------------------------------------------ + * GetGistRelationFreeSpace + * + * Get the free space for a Gist index. + * This is a helper function for relation_free_space() + *------------------------------------------------ + */ + float4 + GetGistRelationFreeSpace(Relation rel) + { + BlockNumber blkno; + BlockNumber totalBlcksInRelation = RelationGetNumberOfBlocks(rel); + BlockNumber totalBlcksCounted = 0; + Size free_space = 0; + double free_percent = 0; + + BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Start from the root */ + for (blkno = GIST_ROOT_BLKNO; blkno < totalBlcksInRelation; blkno++) + { + Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); + Page page = BufferGetPage(buffer); + + /* + * Consider pages LH_UNUSED_PAGE as empty, + * besides that consider the free space in LH_OVERFLOW_PAGE and LH_BUCKET_PAGE pages + */ + if (GistPageIsDeleted(page)) + { + free_space += PageGetPageSize(page); + totalBlcksCounted++; + } + else if (GistPageIsLeaf(page)) + { + free_space += PageGetFreeSpace(page); + totalBlcksCounted++; + } + + ReleaseBuffer(buffer); + } + + if (totalBlcksCounted > 0) + free_percent = ((float4) free_space) / (totalBlcksCounted * BLCKSZ); + + return free_percent; + } + + /*------------------------------------------------ + * GetGinRelationFreeSpace + * + * Get the free space for a Gin index. + * This is a helper function for relation_free_space() + *------------------------------------------------ + */ + float4 + GetGinRelationFreeSpace(Relation rel) + { + BlockNumber blkno; + BlockNumber totalBlcksInRelation = RelationGetNumberOfBlocks(rel); + BlockNumber totalBlcksCounted = 0; + Size free_space = 0; + double free_percent = 0; + + BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Start just after the first root */ + for (blkno = GIN_METAPAGE_BLKNO + 1; blkno < totalBlcksInRelation; blkno++) + { + Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); + Page page = BufferGetPage(buffer); + + /* + * Consider pages GIN_DELETED as empty, + * besides that consider the free space in GIN_DATA and GIN_LEAF pages + */ + if (GinPageIsDeleted(page)) + { + free_space += PageGetPageSize(page); + totalBlcksCounted++; + } + else if (GinPageIsLeaf(page)) + { + free_space += PageGetFreeSpace(page); + totalBlcksCounted++; + } + else if (GinPageIsData(page)) + { + free_space += GinDataPageGetFreeSpace(page); + totalBlcksCounted++; + } + + ReleaseBuffer(buffer); + } + + if (totalBlcksCounted > 0) + free_percent = ((float4) free_space) / (totalBlcksCounted * BLCKSZ); + + return free_percent; + } + + /*------------------------------------------------ + * GetSPGistRelationFreeSpace + * + * Get the free space for a SPGist index. + * This is a helper function for relation_free_space() + *------------------------------------------------ + */ + float4 + GetSpGistRelationFreeSpace(Relation rel) + { + BlockNumber blkno; + BlockNumber totalBlcksInRelation = RelationGetNumberOfBlocks(rel); + BlockNumber totalBlcksCounted = 0; + Size free_space = 0; + double free_percent = 0; + + BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Start just after the first root */ + for (blkno = SPGIST_METAPAGE_BLKNO + 1; blkno < totalBlcksInRelation; blkno++) + { + Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); + Page page = BufferGetPage(buffer); + + /* + * Consider pages GIN_DELETED as empty, + * besides that consider the free space in GIN_DATA and GIN_LEAF pages + */ + if (SpGistPageIsDeleted(page)) + { + free_space += PageGetPageSize(page); + totalBlcksCounted++; + } + else if (SpGistPageIsLeaf(page)) + { + free_space += SpGistPageGetFreeSpace(page, 1); + totalBlcksCounted++; + } + + ReleaseBuffer(buffer); + } + + if (totalBlcksCounted > 0) + free_percent = ((float4) free_space) / (totalBlcksCounted * BLCKSZ); + + return free_percent; + } diff --git a/contrib/pageinspect/idxfuncs.h b/contrib/pageinspect/idxfuncs.h new file mode 100644 index ...788d920 *** a/contrib/pageinspect/idxfuncs.h --- b/contrib/pageinspect/idxfuncs.h *************** *** 0 **** --- 1,10 ---- + /* + * contrib/pageinspect/idxfuncs.h + */ + + float4 GetIndexRelationFreeSpace(Relation); + float4 GetBTRelationFreeSpace(Relation); + float4 GetHashRelationFreeSpace(Relation); + float4 GetGistRelationFreeSpace(Relation); + float4 GetGinRelationFreeSpace(Relation); + float4 GetSpGistRelationFreeSpace(Relation); diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c new file mode 100644 index 9a54744..99fccab *** a/contrib/pageinspect/rawpage.c --- b/contrib/pageinspect/rawpage.c *************** *** 23,30 **** #include "utils/builtins.h" #include "utils/rel.h" - #include "btreefuncs.h" #include "heapfuncs.h" PG_MODULE_MAGIC; --- 23,30 ---- #include "utils/builtins.h" #include "utils/rel.h" #include "heapfuncs.h" + #include "idxfuncs.h" PG_MODULE_MAGIC; *************** relation_free_space(PG_FUNCTION_ARGS) *** 276,282 **** free_space = GetHeapRelationFreeSpace(rel); break; case RELKIND_INDEX: ! free_space = GetBTRelationFreeSpace(rel); break; } relation_close(rel, AccessShareLock); --- 276,282 ---- free_space = GetHeapRelationFreeSpace(rel); break; case RELKIND_INDEX: ! free_space = GetIndexRelationFreeSpace(rel); break; } relation_close(rel, AccessShareLock);