]> granicus.if.org Git - postgresql/blob - contrib/pageinspect/btreefuncs.c
Silence a rather odd compiler warning. In passing, make this file's
[postgresql] / contrib / pageinspect / btreefuncs.c
1 /*
2  * btreefuncs.c
3  *
4  * Copyright (c) 2006 Satoshi Nagayasu <nagayasus@nttdata.co.jp>
5  *
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.
11  *
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.
17  *
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.
23  */
24
25 #include "postgres.h"
26
27 #include "fmgr.h"
28 #include "funcapi.h"
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"
37
38 PG_FUNCTION_INFO_V1(bt_metap);
39 PG_FUNCTION_INFO_V1(bt_page_items);
40 PG_FUNCTION_INFO_V1(bt_page_stats);
41
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);
45
46 #define BTMETAP_TYPE "public.bt_metap_type"
47 #define BTMETAP_NCOLUMNS 6
48
49 #define BTPAGEITEMS_TYPE "public.bt_page_items_type"
50 #define BTPAGEITEMS_NCOLUMNS 6
51
52 #define BTPAGESTATS_TYPE "public.bt_page_stats_type"
53 #define BTPAGESTATS_NCOLUMNS 11
54
55
56 #define IS_INDEX(r) ((r)->rd_rel->relkind == 'i')
57 #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
58
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"); }
63
64 #define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
65                 if ( (blkno)<0 && RelationGetNumberOfBlocks((rel))<=(blkno) ) \
66                          elog(ERROR, "block number out of range"); }
67
68 /* ------------------------------------------------
69  * structure for single btree page statistics
70  * ------------------------------------------------
71  */
72 typedef struct BTPageStat
73 {
74         uint32          blkno;
75         uint32          live_items;
76         uint32          dead_items;
77         uint32          page_size;
78         uint32          max_avail;
79         uint32          free_size;
80         uint32          avg_item_size;
81         char            type;
82
83         /* opaque data */
84         BlockNumber btpo_prev;
85         BlockNumber btpo_next;
86         union
87         {
88                 uint32          level;
89                 TransactionId xact;
90         }                       btpo;
91         uint16          btpo_flags;
92         BTCycleId       btpo_cycleid;
93 }       BTPageStat;
94
95 /* ------------------------------------------------
96  * A structure for a whole btree index statistics
97  * used by pgstatindex().
98  * ------------------------------------------------
99  */
100 typedef struct BTIndexStat
101 {
102         uint32          magic;
103         uint32          version;
104         BlockNumber root_blkno;
105         uint32          level;
106
107         BlockNumber fastroot;
108         uint32          fastlevel;
109
110         uint32          live_items;
111         uint32          dead_items;
112
113         uint32          root_pages;
114         uint32          internal_pages;
115         uint32          leaf_pages;
116         uint32          empty_pages;
117         uint32          deleted_pages;
118
119         uint32          page_size;
120         uint32          avg_item_size;
121
122         uint32          max_avail;
123         uint32          free_space;
124 }       BTIndexStat;
125
126
127 /* -------------------------------------------------
128  * GetBTPageStatistics()
129  *
130  * Collect statistics of single b-tree leaf page
131  * -------------------------------------------------
132  */
133 static void
134 GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat * stat)
135 {
136         Page            page = BufferGetPage(buffer);
137         PageHeader      phdr = (PageHeader) page;
138         OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
139         BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
140         int                     item_size = 0;
141         int                     off;
142
143         stat->blkno = blkno;
144
145         stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
146
147         stat->dead_items = stat->live_items = 0;
148
149         stat->page_size = PageGetPageSize(page);
150
151         /* page type (flags) */
152         if (P_ISDELETED(opaque))
153         {
154                 stat->type = 'd';
155                 stat->btpo.xact = opaque->btpo.xact;
156                 return;
157         }
158         else if (P_IGNORE(opaque))
159                 stat->type = 'e';
160         else if (P_ISLEAF(opaque))
161                 stat->type = 'l';
162         else if (P_ISROOT(opaque))
163                 stat->type = 'r';
164         else
165                 stat->type = 'i';
166
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;
173
174         /* count live and dead tuples, and free space */
175         for (off = FirstOffsetNumber; off <= maxoff; off++)
176         {
177                 IndexTuple      itup;
178
179                 ItemId          id = PageGetItemId(page, off);
180
181                 itup = (IndexTuple) PageGetItem(page, id);
182
183                 item_size += IndexTupleSize(itup);
184
185                 if (!ItemIdDeleted(id))
186                         stat->live_items++;
187                 else
188                         stat->dead_items++;
189         }
190         stat->free_size = PageGetFreeSpace(page);
191
192         if ((stat->live_items + stat->dead_items) > 0)
193                 stat->avg_item_size = item_size / (stat->live_items + stat->dead_items);
194         else
195                 stat->avg_item_size = 0;
196 }
197
198 /* -----------------------------------------------
199  * bt_page()
200  *
201  * Usage: SELECT * FROM bt_page('t1_pkey', 0);
202  * -----------------------------------------------
203  */
204 Datum
205 bt_page_stats(PG_FUNCTION_ARGS)
206 {
207         text       *relname = PG_GETARG_TEXT_P(0);
208         uint32          blkno = PG_GETARG_UINT32(1);
209         Buffer          buffer;
210
211         Relation        rel;
212         RangeVar   *relrv;
213         Datum           result;
214
215         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
216         rel = relation_openrv(relrv, AccessShareLock);
217
218         CHECK_RELATION_BLOCK_RANGE(rel, blkno);
219
220         buffer = ReadBuffer(rel, blkno);
221
222         if (!IS_INDEX(rel) || !IS_BTREE(rel))
223                 elog(ERROR, "bt_page_stats() can only be used on b-tree index");
224
225         if (blkno == 0)
226                 elog(ERROR, "block 0 is a meta page");
227
228         {
229                 HeapTuple       tuple;
230                 TupleDesc       tupleDesc;
231                 int                     j;
232                 char       *values[BTPAGESTATS_NCOLUMNS];
233
234                 BTPageStat      stat;
235
236                 /* keep compiler quiet */
237                 stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
238                 stat.btpo_flags = stat.free_size = stat.avg_item_size = 0;
239
240                 GetBTPageStatistics(blkno, buffer, &stat);
241
242                 tupleDesc = RelationNameGetTupleDesc(BTPAGESTATS_TYPE);
243
244                 j = 0;
245                 values[j] = palloc(32);
246                 snprintf(values[j++], 32, "%d", stat.blkno);
247
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);
264
265                 values[j] = palloc(32);
266                 if (stat.type == 'd')
267                         snprintf(values[j++], 32, "%d", stat.btpo.xact);
268                 else
269                         snprintf(values[j++], 32, "%d", stat.btpo.level);
270
271                 values[j] = palloc(32);
272                 snprintf(values[j++], 32, "%d", stat.btpo_flags);
273
274                 tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
275                                                                            values);
276
277                 result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
278         }
279
280         ReleaseBuffer(buffer);
281
282         relation_close(rel, AccessShareLock);
283
284         PG_RETURN_DATUM(result);
285 }
286
287 /*-------------------------------------------------------
288  * bt_page_items()
289  *
290  * Get IndexTupleData set in a leaf page
291  *
292  * Usage: SELECT * FROM bt_page_items('t1_pkey', 0);
293  *-------------------------------------------------------
294  */
295 /* ---------------------------------------------------
296  * data structure for SRF to hold a scan information
297  * ---------------------------------------------------
298  */
299 struct user_args
300 {
301         TupleDesc       tupd;
302         Relation        rel;
303         Buffer          buffer;
304         Page            page;
305         uint16          offset;
306 };
307
308 Datum
309 bt_page_items(PG_FUNCTION_ARGS)
310 {
311         text       *relname = PG_GETARG_TEXT_P(0);
312         uint32          blkno = PG_GETARG_UINT32(1);
313
314         RangeVar   *relrv;
315         Datum           result;
316         char       *values[BTPAGEITEMS_NCOLUMNS];
317         BTPageOpaque opaque;
318         HeapTuple       tuple;
319         ItemId          id;
320
321         FuncCallContext *fctx;
322         MemoryContext mctx;
323         struct user_args *uargs = NULL;
324
325         if (blkno == 0)
326                 elog(ERROR, "block 0 is a meta page");
327
328         if (SRF_IS_FIRSTCALL())
329         {
330                 fctx = SRF_FIRSTCALL_INIT();
331                 mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
332
333                 uargs = palloc(sizeof(struct user_args));
334
335                 uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE);
336                 uargs->offset = FirstOffsetNumber;
337
338                 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
339                 uargs->rel = relation_openrv(relrv, AccessShareLock);
340
341                 CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno);
342
343                 uargs->buffer = ReadBuffer(uargs->rel, blkno);
344
345                 if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel))
346                         elog(ERROR, "bt_page_items() can only be used on b-tree index");
347
348                 uargs->page = BufferGetPage(uargs->buffer);
349
350                 opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
351
352                 if (P_ISDELETED(opaque))
353                         elog(NOTICE, "page is deleted");
354
355                 fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
356                 fctx->user_fctx = uargs;
357
358                 MemoryContextSwitchTo(mctx);
359         }
360
361         fctx = SRF_PERCALL_SETUP();
362         uargs = fctx->user_fctx;
363
364         if (fctx->call_cntr < fctx->max_calls)
365         {
366                 IndexTuple      itup;
367
368                 id = PageGetItemId(uargs->page, uargs->offset);
369
370                 if (!ItemIdIsValid(id))
371                         elog(ERROR, "invalid ItemId");
372
373                 itup = (IndexTuple) PageGetItem(uargs->page, id);
374
375                 {
376                         int                     j = 0;
377
378                         BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid));
379
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');
390
391                         {
392                                 int                     off;
393                                 char       *dump;
394                                 char       *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
395
396                                 dump = palloc(IndexTupleSize(itup) * 3);
397                                 memset(dump, 0, IndexTupleSize(itup) * 3);
398
399                                 for (off = 0;
400                                          off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
401                                          off++)
402                                 {
403                                         if (dump[0] == '\0')
404                                                 sprintf(dump, "%02x", *(ptr + off) & 0xff);
405                                         else
406                                         {
407                                                 char            buf[4];
408
409                                                 sprintf(buf, " %02x", *(ptr + off) & 0xff);
410                                                 strcat(dump, buf);
411                                         }
412                                 }
413                                 values[j] = dump;
414                         }
415
416                         tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values);
417                         result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple);
418                 }
419
420                 uargs->offset = uargs->offset + 1;
421
422                 SRF_RETURN_NEXT(fctx, result);
423         }
424         else
425         {
426                 ReleaseBuffer(uargs->buffer);
427                 relation_close(uargs->rel, AccessShareLock);
428
429                 SRF_RETURN_DONE(fctx);
430         }
431 }
432
433
434 /* ------------------------------------------------
435  * bt_metap()
436  *
437  * Get a btree meta-page information
438  *
439  * Usage: SELECT * FROM bt_metap('t1_pkey')
440  * ------------------------------------------------
441  */
442 Datum
443 bt_metap(PG_FUNCTION_ARGS)
444 {
445         text       *relname = PG_GETARG_TEXT_P(0);
446         Buffer          buffer;
447
448         Relation        rel;
449         RangeVar   *relrv;
450         Datum           result;
451
452         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
453         rel = relation_openrv(relrv, AccessShareLock);
454
455         if (!IS_INDEX(rel) || !IS_BTREE(rel))
456                 elog(ERROR, "bt_metap() can only be used on b-tree index");
457
458         buffer = ReadBuffer(rel, 0);
459
460         {
461                 BTMetaPageData *metad;
462
463                 TupleDesc       tupleDesc;
464                 int                     j;
465                 char       *values[BTMETAP_NCOLUMNS];
466                 HeapTuple       tuple;
467
468                 Page            page = BufferGetPage(buffer);
469
470                 metad = BTPageGetMeta(page);
471
472                 tupleDesc = RelationNameGetTupleDesc(BTMETAP_TYPE);
473
474                 j = 0;
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);
487
488                 tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
489                                                                            values);
490
491                 result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
492         }
493
494         ReleaseBuffer(buffer);
495
496         relation_close(rel, AccessShareLock);
497
498         PG_RETURN_DATUM(result);
499 }