31
31
#include "access/relation.h"
32
32
#include "catalog/namespace.h"
33
33
#include "catalog/pg_am.h"
34
+ #include "catalog/pg_type.h"
34
35
#include "funcapi.h"
35
36
#include "miscadmin.h"
36
37
#include "pageinspect.h"
38
+ #include "utils/array.h"
37
39
#include "utils/builtins.h"
38
40
#include "utils/rel.h"
39
41
#include "utils/varlena.h"
@@ -45,6 +47,8 @@ PG_FUNCTION_INFO_V1(bt_page_stats);
45
47
46
48
#define IS_INDEX (r ) ((r)->rd_rel->relkind == RELKIND_INDEX)
47
49
#define IS_BTREE (r ) ((r)->rd_rel->relam == BTREE_AM_OID)
50
+ #define DatumGetItemPointer (X ) ((ItemPointer) DatumGetPointer(X))
51
+ #define ItemPointerGetDatum (X ) PointerGetDatum(X)
48
52
49
53
/* note: BlockNumber is unsigned, hence can't be negative */
50
54
#define CHECK_RELATION_BLOCK_RANGE (rel , blkno ) { \
@@ -243,6 +247,9 @@ struct user_args
243
247
{
244
248
Page page ;
245
249
OffsetNumber offset ;
250
+ bool leafpage ;
251
+ bool rightmost ;
252
+ TupleDesc tupd ;
246
253
};
247
254
248
255
/*-------------------------------------------------------
@@ -252,17 +259,25 @@ struct user_args
252
259
* ------------------------------------------------------
253
260
*/
254
261
static Datum
255
- bt_page_print_tuples (FuncCallContext * fctx , Page page , OffsetNumber offset )
262
+ bt_page_print_tuples (FuncCallContext * fctx , struct user_args * uargs )
256
263
{
257
- char * values [6 ];
264
+ Page page = uargs -> page ;
265
+ OffsetNumber offset = uargs -> offset ;
266
+ bool leafpage = uargs -> leafpage ;
267
+ bool rightmost = uargs -> rightmost ;
268
+ bool ispivottuple ;
269
+ Datum values [9 ];
270
+ bool nulls [9 ];
258
271
HeapTuple tuple ;
259
272
ItemId id ;
260
273
IndexTuple itup ;
261
274
int j ;
262
275
int off ;
263
276
int dlen ;
264
- char * dump ;
277
+ char * dump ,
278
+ * datacstring ;
265
279
char * ptr ;
280
+ ItemPointer htid ;
266
281
267
282
id = PageGetItemId (page , offset );
268
283
@@ -272,27 +287,112 @@ bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset)
272
287
itup = (IndexTuple ) PageGetItem (page , id );
273
288
274
289
j = 0 ;
275
- values [j ++ ] = psprintf ("%d" , offset );
276
- values [j ++ ] = psprintf ("(%u,%u)" ,
277
- ItemPointerGetBlockNumberNoCheck (& itup -> t_tid ),
278
- ItemPointerGetOffsetNumberNoCheck (& itup -> t_tid ));
279
- values [j ++ ] = psprintf ("%d" , (int ) IndexTupleSize (itup ));
280
- values [j ++ ] = psprintf ("%c" , IndexTupleHasNulls (itup ) ? 't' : 'f' );
281
- values [j ++ ] = psprintf ("%c" , IndexTupleHasVarwidths (itup ) ? 't' : 'f' );
290
+ memset (nulls , 0 , sizeof (nulls ));
291
+ values [j ++ ] = DatumGetInt16 (offset );
292
+ values [j ++ ] = ItemPointerGetDatum (& itup -> t_tid );
293
+ values [j ++ ] = Int32GetDatum ((int ) IndexTupleSize (itup ));
294
+ values [j ++ ] = BoolGetDatum (IndexTupleHasNulls (itup ));
295
+ values [j ++ ] = BoolGetDatum (IndexTupleHasVarwidths (itup ));
282
296
283
297
ptr = (char * ) itup + IndexInfoFindDataOffset (itup -> t_info );
284
298
dlen = IndexTupleSize (itup ) - IndexInfoFindDataOffset (itup -> t_info );
299
+
300
+ /*
301
+ * Make sure that "data" column does not include posting list or pivot
302
+ * tuple representation of heap TID(s).
303
+ *
304
+ * Note: BTreeTupleIsPivot() won't work reliably on !heapkeyspace indexes
305
+ * (those built before BTREE_VERSION 4), but we have no way of determining
306
+ * if this page came from a !heapkeyspace index. We may only have a bytea
307
+ * nbtree page image to go on, so in general there is no metapage that we
308
+ * can check.
309
+ *
310
+ * That's okay here because BTreeTupleIsPivot() can only return false for
311
+ * a !heapkeyspace pivot, never true for a !heapkeyspace non-pivot. Since
312
+ * heap TID isn't part of the keyspace in a !heapkeyspace index anyway,
313
+ * there cannot possibly be a pivot tuple heap TID representation that we
314
+ * fail to make an adjustment for. A !heapkeyspace index can have
315
+ * BTreeTupleIsPivot() return true (due to things like suffix truncation
316
+ * for INCLUDE indexes in Postgres v11), but when that happens
317
+ * BTreeTupleGetHeapTID() can be trusted to work reliably (i.e. return
318
+ * NULL).
319
+ *
320
+ * Note: BTreeTupleIsPosting() always works reliably, even with
321
+ * !heapkeyspace indexes.
322
+ */
323
+ if (BTreeTupleIsPosting (itup ))
324
+ dlen -= IndexTupleSize (itup ) - BTreeTupleGetPostingOffset (itup );
325
+ else if (BTreeTupleIsPivot (itup ) && BTreeTupleGetHeapTID (itup ) != NULL )
326
+ dlen -= MAXALIGN (sizeof (ItemPointerData ));
327
+
328
+ if (dlen < 0 || dlen > INDEX_SIZE_MASK )
329
+ elog (ERROR , "invalid tuple length %d for tuple at offset number %u" ,
330
+ dlen , offset );
285
331
dump = palloc0 (dlen * 3 + 1 );
286
- values [ j ] = dump ;
332
+ datacstring = dump ;
287
333
for (off = 0 ; off < dlen ; off ++ )
288
334
{
289
335
if (off > 0 )
290
336
* dump ++ = ' ' ;
291
337
sprintf (dump , "%02x" , * (ptr + off ) & 0xff );
292
338
dump += 2 ;
293
339
}
340
+ values [j ++ ] = CStringGetTextDatum (datacstring );
341
+ pfree (datacstring );
342
+
343
+ /*
344
+ * We need to work around the BTreeTupleIsPivot() !heapkeyspace limitation
345
+ * again. Deduce whether or not tuple must be a pivot tuple based on
346
+ * whether or not the page is a leaf page, as well as the page offset
347
+ * number of the tuple.
348
+ */
349
+ ispivottuple = (!leafpage || (!rightmost && offset == P_HIKEY ));
350
+
351
+ /* LP_DEAD bit can never be set for pivot tuples, so show a NULL there */
352
+ if (!ispivottuple )
353
+ values [j ++ ] = BoolGetDatum (ItemIdIsDead (id ));
354
+ else
355
+ {
356
+ Assert (!ItemIdIsDead (id ));
357
+ nulls [j ++ ] = true;
358
+ }
359
+
360
+ htid = BTreeTupleGetHeapTID (itup );
361
+ if (ispivottuple && !BTreeTupleIsPivot (itup ))
362
+ {
363
+ /* Don't show bogus heap TID in !heapkeyspace pivot tuple */
364
+ htid = NULL ;
365
+ }
366
+
367
+ if (htid )
368
+ values [j ++ ] = ItemPointerGetDatum (htid );
369
+ else
370
+ nulls [j ++ ] = true;
371
+
372
+ if (BTreeTupleIsPosting (itup ))
373
+ {
374
+ /* Build an array of item pointers */
375
+ ItemPointer tids ;
376
+ Datum * tids_datum ;
377
+ int nposting ;
378
+
379
+ tids = BTreeTupleGetPosting (itup );
380
+ nposting = BTreeTupleGetNPosting (itup );
381
+ tids_datum = (Datum * ) palloc (nposting * sizeof (Datum ));
382
+ for (int i = 0 ; i < nposting ; i ++ )
383
+ tids_datum [i ] = ItemPointerGetDatum (& tids [i ]);
384
+ values [j ++ ] = PointerGetDatum (construct_array (tids_datum ,
385
+ nposting ,
386
+ TIDOID ,
387
+ sizeof (ItemPointerData ),
388
+ false, 's' ));
389
+ pfree (tids_datum );
390
+ }
391
+ else
392
+ nulls [j ++ ] = true;
294
393
295
- tuple = BuildTupleFromCStrings (fctx -> attinmeta , values );
394
+ /* Build and return the result tuple */
395
+ tuple = heap_form_tuple (uargs -> tupd , values , nulls );
296
396
297
397
return HeapTupleGetDatum (tuple );
298
398
}
@@ -378,12 +478,15 @@ bt_page_items(PG_FUNCTION_ARGS)
378
478
elog (NOTICE , "page is deleted" );
379
479
380
480
fctx -> max_calls = PageGetMaxOffsetNumber (uargs -> page );
481
+ uargs -> leafpage = P_ISLEAF (opaque );
482
+ uargs -> rightmost = P_RIGHTMOST (opaque );
381
483
382
484
/* Build a tuple descriptor for our result type */
383
485
if (get_call_result_type (fcinfo , NULL , & tupleDesc ) != TYPEFUNC_COMPOSITE )
384
486
elog (ERROR , "return type must be a row type" );
487
+ tupleDesc = BlessTupleDesc (tupleDesc );
385
488
386
- fctx -> attinmeta = TupleDescGetAttInMetadata ( tupleDesc ) ;
489
+ uargs -> tupd = tupleDesc ;
387
490
388
491
fctx -> user_fctx = uargs ;
389
492
@@ -395,7 +498,7 @@ bt_page_items(PG_FUNCTION_ARGS)
395
498
396
499
if (fctx -> call_cntr < fctx -> max_calls )
397
500
{
398
- result = bt_page_print_tuples (fctx , uargs -> page , uargs -> offset );
501
+ result = bt_page_print_tuples (fctx , uargs );
399
502
uargs -> offset ++ ;
400
503
SRF_RETURN_NEXT (fctx , result );
401
504
}
@@ -463,12 +566,15 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
463
566
elog (NOTICE , "page is deleted" );
464
567
465
568
fctx -> max_calls = PageGetMaxOffsetNumber (uargs -> page );
569
+ uargs -> leafpage = P_ISLEAF (opaque );
570
+ uargs -> rightmost = P_RIGHTMOST (opaque );
466
571
467
572
/* Build a tuple descriptor for our result type */
468
573
if (get_call_result_type (fcinfo , NULL , & tupleDesc ) != TYPEFUNC_COMPOSITE )
469
574
elog (ERROR , "return type must be a row type" );
575
+ tupleDesc = BlessTupleDesc (tupleDesc );
470
576
471
- fctx -> attinmeta = TupleDescGetAttInMetadata ( tupleDesc ) ;
577
+ uargs -> tupd = tupleDesc ;
472
578
473
579
fctx -> user_fctx = uargs ;
474
580
@@ -480,7 +586,7 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
480
586
481
587
if (fctx -> call_cntr < fctx -> max_calls )
482
588
{
483
- result = bt_page_print_tuples (fctx , uargs -> page , uargs -> offset );
589
+ result = bt_page_print_tuples (fctx , uargs );
484
590
uargs -> offset ++ ;
485
591
SRF_RETURN_NEXT (fctx , result );
486
592
}
@@ -510,7 +616,7 @@ bt_metap(PG_FUNCTION_ARGS)
510
616
BTMetaPageData * metad ;
511
617
TupleDesc tupleDesc ;
512
618
int j ;
513
- char * values [8 ];
619
+ char * values [9 ];
514
620
Buffer buffer ;
515
621
Page page ;
516
622
HeapTuple tuple ;
@@ -557,17 +663,21 @@ bt_metap(PG_FUNCTION_ARGS)
557
663
558
664
/*
559
665
* Get values of extended metadata if available, use default values
560
- * otherwise.
666
+ * otherwise. Note that we rely on the assumption that btm_allequalimage
667
+ * is initialized to zero with indexes that were built on versions prior
668
+ * to Postgres 13 (just like _bt_metaversion()).
561
669
*/
562
670
if (metad -> btm_version >= BTREE_NOVAC_VERSION )
563
671
{
564
672
values [j ++ ] = psprintf ("%u" , metad -> btm_oldest_btpo_xact );
565
673
values [j ++ ] = psprintf ("%f" , metad -> btm_last_cleanup_num_heap_tuples );
674
+ values [j ++ ] = metad -> btm_allequalimage ? "t" : "f" ;
566
675
}
567
676
else
568
677
{
569
678
values [j ++ ] = "0" ;
570
679
values [j ++ ] = "-1" ;
680
+ values [j ++ ] = "f" ;
571
681
}
572
682
573
683
tuple = BuildTupleFromCStrings (TupleDescGetAttInMetadata (tupleDesc ),
0 commit comments