4141
4242PG_FUNCTION_INFO_V1 (bt_metap );
4343PG_FUNCTION_INFO_V1 (bt_page_items );
44+ PG_FUNCTION_INFO_V1 (bt_page_items_bytea );
4445PG_FUNCTION_INFO_V1 (bt_page_stats );
4546
4647#define IS_INDEX (r ) ((r)->rd_rel->relkind == RELKIND_INDEX)
@@ -235,14 +236,6 @@ bt_page_stats(PG_FUNCTION_ARGS)
235236 PG_RETURN_DATUM (result );
236237}
237238
238- /*-------------------------------------------------------
239- * bt_page_items()
240- *
241- * Get IndexTupleData set in a btree page
242- *
243- * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
244- *-------------------------------------------------------
245- */
246239
247240/*
248241 * cross-call data structure for SRF
@@ -253,14 +246,72 @@ struct user_args
253246 OffsetNumber offset ;
254247};
255248
249+ /*-------------------------------------------------------
250+ * bt_page_print_tuples()
251+ *
252+ * Form a tuple describing index tuple at a given offset
253+ * ------------------------------------------------------
254+ */
255+ static Datum
256+ bt_page_print_tuples (FuncCallContext * fctx , Page page , OffsetNumber offset )
257+ {
258+ char * values [6 ];
259+ HeapTuple tuple ;
260+ ItemId id ;
261+ IndexTuple itup ;
262+ int j ;
263+ int off ;
264+ int dlen ;
265+ char * dump ;
266+ char * ptr ;
267+
268+ id = PageGetItemId (page , offset );
269+
270+ if (!ItemIdIsValid (id ))
271+ elog (ERROR , "invalid ItemId" );
272+
273+ itup = (IndexTuple ) PageGetItem (page , id );
274+
275+ j = 0 ;
276+ values [j ++ ] = psprintf ("%d" , offset );
277+ values [j ++ ] = psprintf ("(%u,%u)" ,
278+ ItemPointerGetBlockNumberNoCheck (& itup -> t_tid ),
279+ ItemPointerGetOffsetNumberNoCheck (& itup -> t_tid ));
280+ values [j ++ ] = psprintf ("%d" , (int ) IndexTupleSize (itup ));
281+ values [j ++ ] = psprintf ("%c" , IndexTupleHasNulls (itup ) ? 't' : 'f' );
282+ values [j ++ ] = psprintf ("%c" , IndexTupleHasVarwidths (itup ) ? 't' : 'f' );
283+
284+ ptr = (char * ) itup + IndexInfoFindDataOffset (itup -> t_info );
285+ dlen = IndexTupleSize (itup ) - IndexInfoFindDataOffset (itup -> t_info );
286+ dump = palloc0 (dlen * 3 + 1 );
287+ values [j ] = dump ;
288+ for (off = 0 ; off < dlen ; off ++ )
289+ {
290+ if (off > 0 )
291+ * dump ++ = ' ' ;
292+ sprintf (dump , "%02x" , * (ptr + off ) & 0xff );
293+ dump += 2 ;
294+ }
295+
296+ tuple = BuildTupleFromCStrings (fctx -> attinmeta , values );
297+
298+ return HeapTupleGetDatum (tuple );
299+ }
300+
301+ /*-------------------------------------------------------
302+ * bt_page_items()
303+ *
304+ * Get IndexTupleData set in a btree page
305+ *
306+ * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
307+ *-------------------------------------------------------
308+ */
256309Datum
257310bt_page_items (PG_FUNCTION_ARGS )
258311{
259312 text * relname = PG_GETARG_TEXT_PP (0 );
260313 uint32 blkno = PG_GETARG_UINT32 (1 );
261314 Datum result ;
262- char * values [6 ];
263- HeapTuple tuple ;
264315 FuncCallContext * fctx ;
265316 MemoryContext mctx ;
266317 struct user_args * uargs ;
@@ -345,47 +396,8 @@ bt_page_items(PG_FUNCTION_ARGS)
345396
346397 if (fctx -> call_cntr < fctx -> max_calls )
347398 {
348- ItemId id ;
349- IndexTuple itup ;
350- int j ;
351- int off ;
352- int dlen ;
353- char * dump ;
354- char * ptr ;
355-
356- id = PageGetItemId (uargs -> page , uargs -> offset );
357-
358- if (!ItemIdIsValid (id ))
359- elog (ERROR , "invalid ItemId" );
360-
361- itup = (IndexTuple ) PageGetItem (uargs -> page , id );
362-
363- j = 0 ;
364- values [j ++ ] = psprintf ("%d" , uargs -> offset );
365- values [j ++ ] = psprintf ("(%u,%u)" ,
366- ItemPointerGetBlockNumberNoCheck (& itup -> t_tid ),
367- ItemPointerGetOffsetNumberNoCheck (& itup -> t_tid ));
368- values [j ++ ] = psprintf ("%d" , (int ) IndexTupleSize (itup ));
369- values [j ++ ] = psprintf ("%c" , IndexTupleHasNulls (itup ) ? 't' : 'f' );
370- values [j ++ ] = psprintf ("%c" , IndexTupleHasVarwidths (itup ) ? 't' : 'f' );
371-
372- ptr = (char * ) itup + IndexInfoFindDataOffset (itup -> t_info );
373- dlen = IndexTupleSize (itup ) - IndexInfoFindDataOffset (itup -> t_info );
374- dump = palloc0 (dlen * 3 + 1 );
375- values [j ] = dump ;
376- for (off = 0 ; off < dlen ; off ++ )
377- {
378- if (off > 0 )
379- * dump ++ = ' ' ;
380- sprintf (dump , "%02x" , * (ptr + off ) & 0xff );
381- dump += 2 ;
382- }
383-
384- tuple = BuildTupleFromCStrings (fctx -> attinmeta , values );
385- result = HeapTupleGetDatum (tuple );
386-
387- uargs -> offset = uargs -> offset + 1 ;
388-
399+ result = bt_page_print_tuples (fctx , uargs -> page , uargs -> offset );
400+ uargs -> offset ++ ;
389401 SRF_RETURN_NEXT (fctx , result );
390402 }
391403 else
@@ -396,6 +408,90 @@ bt_page_items(PG_FUNCTION_ARGS)
396408 }
397409}
398410
411+ /*-------------------------------------------------------
412+ * bt_page_items_bytea()
413+ *
414+ * Get IndexTupleData set in a btree page
415+ *
416+ * Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1));
417+ *-------------------------------------------------------
418+ */
419+
420+ Datum
421+ bt_page_items_bytea (PG_FUNCTION_ARGS )
422+ {
423+ bytea * raw_page = PG_GETARG_BYTEA_P (0 );
424+ Datum result ;
425+ FuncCallContext * fctx ;
426+ struct user_args * uargs ;
427+ int raw_page_size ;
428+
429+ if (!superuser ())
430+ ereport (ERROR ,
431+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
432+ (errmsg ("must be superuser to use pageinspect functions" ))));
433+
434+ if (SRF_IS_FIRSTCALL ())
435+ {
436+ BTPageOpaque opaque ;
437+ MemoryContext mctx ;
438+ TupleDesc tupleDesc ;
439+
440+ raw_page_size = VARSIZE (raw_page ) - VARHDRSZ ;
441+
442+ if (raw_page_size < SizeOfPageHeaderData )
443+ ereport (ERROR ,
444+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
445+ errmsg ("input page too small (%d bytes)" , raw_page_size )));
446+
447+ fctx = SRF_FIRSTCALL_INIT ();
448+ mctx = MemoryContextSwitchTo (fctx -> multi_call_memory_ctx );
449+
450+ uargs = palloc (sizeof (struct user_args ));
451+
452+ uargs -> page = VARDATA (raw_page );
453+
454+ uargs -> offset = FirstOffsetNumber ;
455+
456+ opaque = (BTPageOpaque ) PageGetSpecialPointer (uargs -> page );
457+
458+ if (P_ISMETA (opaque ))
459+ ereport (ERROR ,
460+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
461+ errmsg ("block is a meta page" )));
462+
463+ if (P_ISDELETED (opaque ))
464+ elog (NOTICE , "page is deleted" );
465+
466+ fctx -> max_calls = PageGetMaxOffsetNumber (uargs -> page );
467+
468+ /* Build a tuple descriptor for our result type */
469+ if (get_call_result_type (fcinfo , NULL , & tupleDesc ) != TYPEFUNC_COMPOSITE )
470+ elog (ERROR , "return type must be a row type" );
471+
472+ fctx -> attinmeta = TupleDescGetAttInMetadata (tupleDesc );
473+
474+ fctx -> user_fctx = uargs ;
475+
476+ MemoryContextSwitchTo (mctx );
477+ }
478+
479+ fctx = SRF_PERCALL_SETUP ();
480+ uargs = fctx -> user_fctx ;
481+
482+ if (fctx -> call_cntr < fctx -> max_calls )
483+ {
484+ result = bt_page_print_tuples (fctx , uargs -> page , uargs -> offset );
485+ uargs -> offset ++ ;
486+ SRF_RETURN_NEXT (fctx , result );
487+ }
488+ else
489+ {
490+ pfree (uargs );
491+ SRF_RETURN_DONE (fctx );
492+ }
493+ }
494+
399495
400496/* ------------------------------------------------
401497 * bt_metap()
0 commit comments