~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to contrib/pageinspect/btreefuncs.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * contrib/pageinspect/btreefuncs.c
 
3
 *
 
4
 *
 
5
 * btreefuncs.c
 
6
 *
 
7
 * Copyright (c) 2006 Satoshi Nagayasu <nagayasus@nttdata.co.jp>
 
8
 *
 
9
 * Permission to use, copy, modify, and distribute this software and
 
10
 * its documentation for any purpose, without fee, and without a
 
11
 * written agreement is hereby granted, provided that the above
 
12
 * copyright notice and this paragraph and the following two
 
13
 * paragraphs appear in all copies.
 
14
 *
 
15
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
 
16
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
 
17
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
 
18
 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
 
19
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 
20
 *
 
21
 * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 
22
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
23
 * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
 
24
 * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
 
25
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
26
 */
 
27
 
 
28
#include "postgres.h"
 
29
 
 
30
#include "access/heapam.h"
 
31
#include "access/nbtree.h"
 
32
#include "catalog/namespace.h"
 
33
#include "catalog/pg_type.h"
 
34
#include "funcapi.h"
 
35
#include "miscadmin.h"
 
36
#include "storage/bufmgr.h"
 
37
#include "utils/builtins.h"
 
38
 
 
39
 
 
40
extern Datum bt_metap(PG_FUNCTION_ARGS);
 
41
extern Datum bt_page_items(PG_FUNCTION_ARGS);
 
42
extern Datum bt_page_stats(PG_FUNCTION_ARGS);
 
43
 
 
44
PG_FUNCTION_INFO_V1(bt_metap);
 
45
PG_FUNCTION_INFO_V1(bt_page_items);
 
46
PG_FUNCTION_INFO_V1(bt_page_stats);
 
47
 
 
48
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
 
49
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
 
50
 
 
51
#define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
 
52
                if ( !(FirstOffsetNumber <= (offnum) && \
 
53
                                                (offnum) <= PageGetMaxOffsetNumber(pg)) ) \
 
54
                         elog(ERROR, "page offset number out of range"); }
 
55
 
 
56
/* note: BlockNumber is unsigned, hence can't be negative */
 
57
#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
 
58
                if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \
 
59
                         elog(ERROR, "block number out of range"); }
 
60
 
 
61
/* ------------------------------------------------
 
62
 * structure for single btree page statistics
 
63
 * ------------------------------------------------
 
64
 */
 
65
typedef struct BTPageStat
 
66
{
 
67
        uint32          blkno;
 
68
        uint32          live_items;
 
69
        uint32          dead_items;
 
70
        uint32          page_size;
 
71
        uint32          max_avail;
 
72
        uint32          free_size;
 
73
        uint32          avg_item_size;
 
74
        char            type;
 
75
 
 
76
        /* opaque data */
 
77
        BlockNumber btpo_prev;
 
78
        BlockNumber btpo_next;
 
79
        union
 
80
        {
 
81
                uint32          level;
 
82
                TransactionId xact;
 
83
        }                       btpo;
 
84
        uint16          btpo_flags;
 
85
        BTCycleId       btpo_cycleid;
 
86
} BTPageStat;
 
87
 
 
88
 
 
89
/* -------------------------------------------------
 
90
 * GetBTPageStatistics()
 
91
 *
 
92
 * Collect statistics of single b-tree page
 
93
 * -------------------------------------------------
 
94
 */
 
95
static void
 
96
GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
 
97
{
 
98
        Page            page = BufferGetPage(buffer);
 
99
        PageHeader      phdr = (PageHeader) page;
 
100
        OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
 
101
        BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
 
102
        int                     item_size = 0;
 
103
        int                     off;
 
104
 
 
105
        stat->blkno = blkno;
 
106
 
 
107
        stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
 
108
 
 
109
        stat->dead_items = stat->live_items = 0;
 
110
 
 
111
        stat->page_size = PageGetPageSize(page);
 
112
 
 
113
        /* page type (flags) */
 
114
        if (P_ISDELETED(opaque))
 
115
        {
 
116
                stat->type = 'd';
 
117
                stat->btpo.xact = opaque->btpo.xact;
 
118
                return;
 
119
        }
 
120
        else if (P_IGNORE(opaque))
 
121
                stat->type = 'e';
 
122
        else if (P_ISLEAF(opaque))
 
123
                stat->type = 'l';
 
124
        else if (P_ISROOT(opaque))
 
125
                stat->type = 'r';
 
126
        else
 
127
                stat->type = 'i';
 
128
 
 
129
        /* btpage opaque data */
 
130
        stat->btpo_prev = opaque->btpo_prev;
 
131
        stat->btpo_next = opaque->btpo_next;
 
132
        stat->btpo.level = opaque->btpo.level;
 
133
        stat->btpo_flags = opaque->btpo_flags;
 
134
        stat->btpo_cycleid = opaque->btpo_cycleid;
 
135
 
 
136
        /* count live and dead tuples, and free space */
 
137
        for (off = FirstOffsetNumber; off <= maxoff; off++)
 
138
        {
 
139
                IndexTuple      itup;
 
140
 
 
141
                ItemId          id = PageGetItemId(page, off);
 
142
 
 
143
                itup = (IndexTuple) PageGetItem(page, id);
 
144
 
 
145
                item_size += IndexTupleSize(itup);
 
146
 
 
147
                if (!ItemIdIsDead(id))
 
148
                        stat->live_items++;
 
149
                else
 
150
                        stat->dead_items++;
 
151
        }
 
152
        stat->free_size = PageGetFreeSpace(page);
 
153
 
 
154
        if ((stat->live_items + stat->dead_items) > 0)
 
155
                stat->avg_item_size = item_size / (stat->live_items + stat->dead_items);
 
156
        else
 
157
                stat->avg_item_size = 0;
 
158
}
 
159
 
 
160
/* -----------------------------------------------
 
161
 * bt_page()
 
162
 *
 
163
 * Usage: SELECT * FROM bt_page('t1_pkey', 1);
 
164
 * -----------------------------------------------
 
165
 */
 
166
Datum
 
167
bt_page_stats(PG_FUNCTION_ARGS)
 
168
{
 
169
        text       *relname = PG_GETARG_TEXT_P(0);
 
170
        uint32          blkno = PG_GETARG_UINT32(1);
 
171
        Buffer          buffer;
 
172
        Relation        rel;
 
173
        RangeVar   *relrv;
 
174
        Datum           result;
 
175
        HeapTuple       tuple;
 
176
        TupleDesc       tupleDesc;
 
177
        int                     j;
 
178
        char       *values[11];
 
179
        BTPageStat      stat;
 
180
 
 
181
        if (!superuser())
 
182
                ereport(ERROR,
 
183
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
184
                                 (errmsg("must be superuser to use pageinspect functions"))));
 
185
 
 
186
        relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 
187
        rel = relation_openrv(relrv, AccessShareLock);
 
188
 
 
189
        if (!IS_INDEX(rel) || !IS_BTREE(rel))
 
190
                elog(ERROR, "relation \"%s\" is not a btree index",
 
191
                         RelationGetRelationName(rel));
 
192
 
 
193
        /*
 
194
         * Reject attempts to read non-local temporary relations; we would be
 
195
         * likely to get wrong data since we have no visibility into the owning
 
196
         * session's local buffers.
 
197
         */
 
198
        if (RELATION_IS_OTHER_TEMP(rel))
 
199
                ereport(ERROR,
 
200
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
201
                                 errmsg("cannot access temporary tables of other sessions")));
 
202
 
 
203
        if (blkno == 0)
 
204
                elog(ERROR, "block 0 is a meta page");
 
205
 
 
206
        CHECK_RELATION_BLOCK_RANGE(rel, blkno);
 
207
 
 
208
        buffer = ReadBuffer(rel, blkno);
 
209
 
 
210
        /* keep compiler quiet */
 
211
        stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
 
212
        stat.btpo_flags = stat.free_size = stat.avg_item_size = 0;
 
213
 
 
214
        GetBTPageStatistics(blkno, buffer, &stat);
 
215
 
 
216
        /* Build a tuple descriptor for our result type */
 
217
        if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
 
218
                elog(ERROR, "return type must be a row type");
 
219
 
 
220
        j = 0;
 
221
        values[j] = palloc(32);
 
222
        snprintf(values[j++], 32, "%d", stat.blkno);
 
223
        values[j] = palloc(32);
 
224
        snprintf(values[j++], 32, "%c", stat.type);
 
225
        values[j] = palloc(32);
 
226
        snprintf(values[j++], 32, "%d", stat.live_items);
 
227
        values[j] = palloc(32);
 
228
        snprintf(values[j++], 32, "%d", stat.dead_items);
 
229
        values[j] = palloc(32);
 
230
        snprintf(values[j++], 32, "%d", stat.avg_item_size);
 
231
        values[j] = palloc(32);
 
232
        snprintf(values[j++], 32, "%d", stat.page_size);
 
233
        values[j] = palloc(32);
 
234
        snprintf(values[j++], 32, "%d", stat.free_size);
 
235
        values[j] = palloc(32);
 
236
        snprintf(values[j++], 32, "%d", stat.btpo_prev);
 
237
        values[j] = palloc(32);
 
238
        snprintf(values[j++], 32, "%d", stat.btpo_next);
 
239
        values[j] = palloc(32);
 
240
        if (stat.type == 'd')
 
241
                snprintf(values[j++], 32, "%d", stat.btpo.xact);
 
242
        else
 
243
                snprintf(values[j++], 32, "%d", stat.btpo.level);
 
244
        values[j] = palloc(32);
 
245
        snprintf(values[j++], 32, "%d", stat.btpo_flags);
 
246
 
 
247
        tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 
248
                                                                   values);
 
249
 
 
250
        result = HeapTupleGetDatum(tuple);
 
251
 
 
252
        ReleaseBuffer(buffer);
 
253
 
 
254
        relation_close(rel, AccessShareLock);
 
255
 
 
256
        PG_RETURN_DATUM(result);
 
257
}
 
258
 
 
259
/*-------------------------------------------------------
 
260
 * bt_page_items()
 
261
 *
 
262
 * Get IndexTupleData set in a btree page
 
263
 *
 
264
 * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
 
265
 *-------------------------------------------------------
 
266
 */
 
267
 
 
268
/*
 
269
 * cross-call data structure for SRF
 
270
 */
 
271
struct user_args
 
272
{
 
273
        Page            page;
 
274
        OffsetNumber offset;
 
275
};
 
276
 
 
277
Datum
 
278
bt_page_items(PG_FUNCTION_ARGS)
 
279
{
 
280
        text       *relname = PG_GETARG_TEXT_P(0);
 
281
        uint32          blkno = PG_GETARG_UINT32(1);
 
282
        Datum           result;
 
283
        char       *values[6];
 
284
        HeapTuple       tuple;
 
285
        FuncCallContext *fctx;
 
286
        MemoryContext mctx;
 
287
        struct user_args *uargs;
 
288
 
 
289
        if (!superuser())
 
290
                ereport(ERROR,
 
291
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
292
                                 (errmsg("must be superuser to use pageinspect functions"))));
 
293
 
 
294
        if (SRF_IS_FIRSTCALL())
 
295
        {
 
296
                RangeVar   *relrv;
 
297
                Relation        rel;
 
298
                Buffer          buffer;
 
299
                BTPageOpaque opaque;
 
300
                TupleDesc       tupleDesc;
 
301
 
 
302
                fctx = SRF_FIRSTCALL_INIT();
 
303
 
 
304
                relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 
305
                rel = relation_openrv(relrv, AccessShareLock);
 
306
 
 
307
                if (!IS_INDEX(rel) || !IS_BTREE(rel))
 
308
                        elog(ERROR, "relation \"%s\" is not a btree index",
 
309
                                 RelationGetRelationName(rel));
 
310
 
 
311
                /*
 
312
                 * Reject attempts to read non-local temporary relations; we would be
 
313
                 * likely to get wrong data since we have no visibility into the
 
314
                 * owning session's local buffers.
 
315
                 */
 
316
                if (RELATION_IS_OTHER_TEMP(rel))
 
317
                        ereport(ERROR,
 
318
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
319
                                errmsg("cannot access temporary tables of other sessions")));
 
320
 
 
321
                if (blkno == 0)
 
322
                        elog(ERROR, "block 0 is a meta page");
 
323
 
 
324
                CHECK_RELATION_BLOCK_RANGE(rel, blkno);
 
325
 
 
326
                buffer = ReadBuffer(rel, blkno);
 
327
 
 
328
                /*
 
329
                 * We copy the page into local storage to avoid holding pin on the
 
330
                 * buffer longer than we must, and possibly failing to release it at
 
331
                 * all if the calling query doesn't fetch all rows.
 
332
                 */
 
333
                mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
 
334
 
 
335
                uargs = palloc(sizeof(struct user_args));
 
336
 
 
337
                uargs->page = palloc(BLCKSZ);
 
338
                memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
 
339
 
 
340
                ReleaseBuffer(buffer);
 
341
                relation_close(rel, AccessShareLock);
 
342
 
 
343
                uargs->offset = FirstOffsetNumber;
 
344
 
 
345
                opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
 
346
 
 
347
                if (P_ISDELETED(opaque))
 
348
                        elog(NOTICE, "page is deleted");
 
349
 
 
350
                fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
 
351
 
 
352
                /* Build a tuple descriptor for our result type */
 
353
                if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
 
354
                        elog(ERROR, "return type must be a row type");
 
355
 
 
356
                fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);
 
357
 
 
358
                fctx->user_fctx = uargs;
 
359
 
 
360
                MemoryContextSwitchTo(mctx);
 
361
        }
 
362
 
 
363
        fctx = SRF_PERCALL_SETUP();
 
364
        uargs = fctx->user_fctx;
 
365
 
 
366
        if (fctx->call_cntr < fctx->max_calls)
 
367
        {
 
368
                ItemId          id;
 
369
                IndexTuple      itup;
 
370
                int                     j;
 
371
                int                     off;
 
372
                int                     dlen;
 
373
                char       *dump;
 
374
                char       *ptr;
 
375
 
 
376
                id = PageGetItemId(uargs->page, uargs->offset);
 
377
 
 
378
                if (!ItemIdIsValid(id))
 
379
                        elog(ERROR, "invalid ItemId");
 
380
 
 
381
                itup = (IndexTuple) PageGetItem(uargs->page, id);
 
382
 
 
383
                j = 0;
 
384
                values[j] = palloc(32);
 
385
                snprintf(values[j++], 32, "%d", uargs->offset);
 
386
                values[j] = palloc(32);
 
387
                snprintf(values[j++], 32, "(%u,%u)",
 
388
                                 BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)),
 
389
                                 itup->t_tid.ip_posid);
 
390
                values[j] = palloc(32);
 
391
                snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup));
 
392
                values[j] = palloc(32);
 
393
                snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f');
 
394
                values[j] = palloc(32);
 
395
                snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
 
396
 
 
397
                ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
 
398
                dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
 
399
                dump = palloc0(dlen * 3 + 1);
 
400
                values[j] = dump;
 
401
                for (off = 0; off < dlen; off++)
 
402
                {
 
403
                        if (off > 0)
 
404
                                *dump++ = ' ';
 
405
                        sprintf(dump, "%02x", *(ptr + off) & 0xff);
 
406
                        dump += 2;
 
407
                }
 
408
 
 
409
                tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
 
410
                result = HeapTupleGetDatum(tuple);
 
411
 
 
412
                uargs->offset = uargs->offset + 1;
 
413
 
 
414
                SRF_RETURN_NEXT(fctx, result);
 
415
        }
 
416
        else
 
417
        {
 
418
                pfree(uargs->page);
 
419
                pfree(uargs);
 
420
                SRF_RETURN_DONE(fctx);
 
421
        }
 
422
}
 
423
 
 
424
 
 
425
/* ------------------------------------------------
 
426
 * bt_metap()
 
427
 *
 
428
 * Get a btree's meta-page information
 
429
 *
 
430
 * Usage: SELECT * FROM bt_metap('t1_pkey')
 
431
 * ------------------------------------------------
 
432
 */
 
433
Datum
 
434
bt_metap(PG_FUNCTION_ARGS)
 
435
{
 
436
        text       *relname = PG_GETARG_TEXT_P(0);
 
437
        Datum           result;
 
438
        Relation        rel;
 
439
        RangeVar   *relrv;
 
440
        BTMetaPageData *metad;
 
441
        TupleDesc       tupleDesc;
 
442
        int                     j;
 
443
        char       *values[6];
 
444
        Buffer          buffer;
 
445
        Page            page;
 
446
        HeapTuple       tuple;
 
447
 
 
448
        if (!superuser())
 
449
                ereport(ERROR,
 
450
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
451
                                 (errmsg("must be superuser to use pageinspect functions"))));
 
452
 
 
453
        relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 
454
        rel = relation_openrv(relrv, AccessShareLock);
 
455
 
 
456
        if (!IS_INDEX(rel) || !IS_BTREE(rel))
 
457
                elog(ERROR, "relation \"%s\" is not a btree index",
 
458
                         RelationGetRelationName(rel));
 
459
 
 
460
        /*
 
461
         * Reject attempts to read non-local temporary relations; we would be
 
462
         * likely to get wrong data since we have no visibility into the owning
 
463
         * session's local buffers.
 
464
         */
 
465
        if (RELATION_IS_OTHER_TEMP(rel))
 
466
                ereport(ERROR,
 
467
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
468
                                 errmsg("cannot access temporary tables of other sessions")));
 
469
 
 
470
        buffer = ReadBuffer(rel, 0);
 
471
        page = BufferGetPage(buffer);
 
472
        metad = BTPageGetMeta(page);
 
473
 
 
474
        /* Build a tuple descriptor for our result type */
 
475
        if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
 
476
                elog(ERROR, "return type must be a row type");
 
477
 
 
478
        j = 0;
 
479
        values[j] = palloc(32);
 
480
        snprintf(values[j++], 32, "%d", metad->btm_magic);
 
481
        values[j] = palloc(32);
 
482
        snprintf(values[j++], 32, "%d", metad->btm_version);
 
483
        values[j] = palloc(32);
 
484
        snprintf(values[j++], 32, "%d", metad->btm_root);
 
485
        values[j] = palloc(32);
 
486
        snprintf(values[j++], 32, "%d", metad->btm_level);
 
487
        values[j] = palloc(32);
 
488
        snprintf(values[j++], 32, "%d", metad->btm_fastroot);
 
489
        values[j] = palloc(32);
 
490
        snprintf(values[j++], 32, "%d", metad->btm_fastlevel);
 
491
 
 
492
        tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 
493
                                                                   values);
 
494
 
 
495
        result = HeapTupleGetDatum(tuple);
 
496
 
 
497
        ReleaseBuffer(buffer);
 
498
 
 
499
        relation_close(rel, AccessShareLock);
 
500
 
 
501
        PG_RETURN_DATUM(result);
 
502
}