4 * Copyright (c) 2006 Satoshi Nagayasu <nagayasus@nttdata.co.jp>
6 * Permission to use, copy, modify, and distribute this software and
7 * its documentation for any purpose, without fee, and without a
8 * written agreement is hereby granted, provided that the above
9 * copyright notice and this paragraph and the following two
10 * paragraphs appear in all copies.
12 * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
13 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
16 * OF THE POSSIBILITY OF SUCH DAMAGE.
18 * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
21 * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
22 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
29 #include "access/heapam.h"
30 #include "access/itup.h"
31 #include "access/nbtree.h"
32 #include "access/transam.h"
33 #include "catalog/namespace.h"
34 #include "catalog/pg_type.h"
35 #include "utils/builtins.h"
36 #include "utils/inval.h"
38 PG_FUNCTION_INFO_V1(bt_metap);
39 PG_FUNCTION_INFO_V1(bt_page_items);
40 PG_FUNCTION_INFO_V1(bt_page_stats);
42 extern Datum bt_metap(PG_FUNCTION_ARGS);
43 extern Datum bt_page_items(PG_FUNCTION_ARGS);
44 extern Datum bt_page_stats(PG_FUNCTION_ARGS);
46 #define BTMETAP_TYPE "public.bt_metap_type"
47 #define BTMETAP_NCOLUMNS 6
49 #define BTPAGEITEMS_TYPE "public.bt_page_items_type"
50 #define BTPAGEITEMS_NCOLUMNS 6
52 #define BTPAGESTATS_TYPE "public.bt_page_stats_type"
53 #define BTPAGESTATS_NCOLUMNS 11
56 #define IS_INDEX(r) ((r)->rd_rel->relkind == 'i')
57 #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
59 #define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
60 if ( !(FirstOffsetNumber<=(offnum) && \
61 (offnum)<=PageGetMaxOffsetNumber(pg)) ) \
62 elog(ERROR, "page offset number out of range"); }
64 #define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
65 if ( (blkno)<0 && RelationGetNumberOfBlocks((rel))<=(blkno) ) \
66 elog(ERROR, "block number out of range"); }
68 /* ------------------------------------------------
69 * structure for single btree page statistics
70 * ------------------------------------------------
72 typedef struct BTPageStat
84 BlockNumber btpo_prev;
85 BlockNumber btpo_next;
92 BTCycleId btpo_cycleid;
95 /* ------------------------------------------------
96 * A structure for a whole btree index statistics
97 * used by pgstatindex().
98 * ------------------------------------------------
100 typedef struct BTIndexStat
104 BlockNumber root_blkno;
107 BlockNumber fastroot;
114 uint32 internal_pages;
117 uint32 deleted_pages;
120 uint32 avg_item_size;
127 /* -------------------------------------------------
128 * GetBTPageStatistics()
130 * Collect statistics of single b-tree leaf page
131 * -------------------------------------------------
134 GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat * stat)
136 Page page = BufferGetPage(buffer);
137 PageHeader phdr = (PageHeader) page;
138 OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
139 BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
145 stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
147 stat->dead_items = stat->live_items = 0;
149 stat->page_size = PageGetPageSize(page);
151 /* page type (flags) */
152 if (P_ISDELETED(opaque))
155 stat->btpo.xact = opaque->btpo.xact;
158 else if (P_IGNORE(opaque))
160 else if (P_ISLEAF(opaque))
162 else if (P_ISROOT(opaque))
167 /* btpage opaque data */
168 stat->btpo_prev = opaque->btpo_prev;
169 stat->btpo_next = opaque->btpo_next;
170 stat->btpo.level = opaque->btpo.level;
171 stat->btpo_flags = opaque->btpo_flags;
172 stat->btpo_cycleid = opaque->btpo_cycleid;
174 /* count live and dead tuples, and free space */
175 for (off = FirstOffsetNumber; off <= maxoff; off++)
179 ItemId id = PageGetItemId(page, off);
181 itup = (IndexTuple) PageGetItem(page, id);
183 item_size += IndexTupleSize(itup);
185 if (!ItemIdDeleted(id))
190 stat->free_size = PageGetFreeSpace(page);
192 if ((stat->live_items + stat->dead_items) > 0)
193 stat->avg_item_size = item_size / (stat->live_items + stat->dead_items);
195 stat->avg_item_size = 0;
198 /* -----------------------------------------------
201 * Usage: SELECT * FROM bt_page('t1_pkey', 0);
202 * -----------------------------------------------
205 bt_page_stats(PG_FUNCTION_ARGS)
207 text *relname = PG_GETARG_TEXT_P(0);
208 uint32 blkno = PG_GETARG_UINT32(1);
215 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
216 rel = relation_openrv(relrv, AccessShareLock);
218 CHECK_RELATION_BLOCK_RANGE(rel, blkno);
220 buffer = ReadBuffer(rel, blkno);
222 if (!IS_INDEX(rel) || !IS_BTREE(rel))
223 elog(ERROR, "bt_page_stats() can only be used on b-tree index");
226 elog(ERROR, "block 0 is a meta page");
232 char *values[BTPAGESTATS_NCOLUMNS];
236 /* keep compiler quiet */
237 stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
238 stat.btpo_flags = stat.free_size = stat.avg_item_size = 0;
240 GetBTPageStatistics(blkno, buffer, &stat);
242 tupleDesc = RelationNameGetTupleDesc(BTPAGESTATS_TYPE);
245 values[j] = palloc(32);
246 snprintf(values[j++], 32, "%d", stat.blkno);
248 values[j] = palloc(32);
249 snprintf(values[j++], 32, "%c", stat.type);
250 values[j] = palloc(32);
251 snprintf(values[j++], 32, "%d", stat.live_items);
252 values[j] = palloc(32);
253 snprintf(values[j++], 32, "%d", stat.dead_items);
254 values[j] = palloc(32);
255 snprintf(values[j++], 32, "%d", stat.avg_item_size);
256 values[j] = palloc(32);
257 snprintf(values[j++], 32, "%d", stat.page_size);
258 values[j] = palloc(32);
259 snprintf(values[j++], 32, "%d", stat.free_size);
260 values[j] = palloc(32);
261 snprintf(values[j++], 32, "%d", stat.btpo_prev);
262 values[j] = palloc(32);
263 snprintf(values[j++], 32, "%d", stat.btpo_next);
265 values[j] = palloc(32);
266 if (stat.type == 'd')
267 snprintf(values[j++], 32, "%d", stat.btpo.xact);
269 snprintf(values[j++], 32, "%d", stat.btpo.level);
271 values[j] = palloc(32);
272 snprintf(values[j++], 32, "%d", stat.btpo_flags);
274 tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
277 result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
280 ReleaseBuffer(buffer);
282 relation_close(rel, AccessShareLock);
284 PG_RETURN_DATUM(result);
287 /*-------------------------------------------------------
290 * Get IndexTupleData set in a leaf page
292 * Usage: SELECT * FROM bt_page_items('t1_pkey', 0);
293 *-------------------------------------------------------
295 /* ---------------------------------------------------
296 * data structure for SRF to hold a scan information
297 * ---------------------------------------------------
309 bt_page_items(PG_FUNCTION_ARGS)
311 text *relname = PG_GETARG_TEXT_P(0);
312 uint32 blkno = PG_GETARG_UINT32(1);
316 char *values[BTPAGEITEMS_NCOLUMNS];
321 FuncCallContext *fctx;
323 struct user_args *uargs = NULL;
326 elog(ERROR, "block 0 is a meta page");
328 if (SRF_IS_FIRSTCALL())
330 fctx = SRF_FIRSTCALL_INIT();
331 mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
333 uargs = palloc(sizeof(struct user_args));
335 uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE);
336 uargs->offset = FirstOffsetNumber;
338 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
339 uargs->rel = relation_openrv(relrv, AccessShareLock);
341 CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno);
343 uargs->buffer = ReadBuffer(uargs->rel, blkno);
345 if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel))
346 elog(ERROR, "bt_page_items() can only be used on b-tree index");
348 uargs->page = BufferGetPage(uargs->buffer);
350 opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
352 if (P_ISDELETED(opaque))
353 elog(NOTICE, "page is deleted");
355 fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
356 fctx->user_fctx = uargs;
358 MemoryContextSwitchTo(mctx);
361 fctx = SRF_PERCALL_SETUP();
362 uargs = fctx->user_fctx;
364 if (fctx->call_cntr < fctx->max_calls)
368 id = PageGetItemId(uargs->page, uargs->offset);
370 if (!ItemIdIsValid(id))
371 elog(ERROR, "invalid ItemId");
373 itup = (IndexTuple) PageGetItem(uargs->page, id);
378 BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid));
380 values[j] = palloc(32);
381 snprintf(values[j++], 32, "%d", uargs->offset);
382 values[j] = palloc(32);
383 snprintf(values[j++], 32, "(%u,%u)", blkno, itup->t_tid.ip_posid);
384 values[j] = palloc(32);
385 snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup));
386 values[j] = palloc(32);
387 snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f');
388 values[j] = palloc(32);
389 snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
394 char *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
396 dump = palloc(IndexTupleSize(itup) * 3);
397 memset(dump, 0, IndexTupleSize(itup) * 3);
400 off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
404 sprintf(dump, "%02x", *(ptr + off) & 0xff);
409 sprintf(buf, " %02x", *(ptr + off) & 0xff);
416 tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values);
417 result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple);
420 uargs->offset = uargs->offset + 1;
422 SRF_RETURN_NEXT(fctx, result);
426 ReleaseBuffer(uargs->buffer);
427 relation_close(uargs->rel, AccessShareLock);
429 SRF_RETURN_DONE(fctx);
434 /* ------------------------------------------------
437 * Get a btree meta-page information
439 * Usage: SELECT * FROM bt_metap('t1_pkey')
440 * ------------------------------------------------
443 bt_metap(PG_FUNCTION_ARGS)
445 text *relname = PG_GETARG_TEXT_P(0);
452 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
453 rel = relation_openrv(relrv, AccessShareLock);
455 if (!IS_INDEX(rel) || !IS_BTREE(rel))
456 elog(ERROR, "bt_metap() can only be used on b-tree index");
458 buffer = ReadBuffer(rel, 0);
461 BTMetaPageData *metad;
465 char *values[BTMETAP_NCOLUMNS];
468 Page page = BufferGetPage(buffer);
470 metad = BTPageGetMeta(page);
472 tupleDesc = RelationNameGetTupleDesc(BTMETAP_TYPE);
475 values[j] = palloc(32);
476 snprintf(values[j++], 32, "%d", metad->btm_magic);
477 values[j] = palloc(32);
478 snprintf(values[j++], 32, "%d", metad->btm_version);
479 values[j] = palloc(32);
480 snprintf(values[j++], 32, "%d", metad->btm_root);
481 values[j] = palloc(32);
482 snprintf(values[j++], 32, "%d", metad->btm_level);
483 values[j] = palloc(32);
484 snprintf(values[j++], 32, "%d", metad->btm_fastroot);
485 values[j] = palloc(32);
486 snprintf(values[j++], 32, "%d", metad->btm_fastlevel);
488 tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
491 result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
494 ReleaseBuffer(buffer);
496 relation_close(rel, AccessShareLock);
498 PG_RETURN_DATUM(result);