2929
3030#include "access/nbtree.h"
3131#include "access/relation.h"
32+ #include "access/tupdesc.h"
3233#include "catalog/namespace.h"
3334#include "catalog/pg_am.h"
3435#include "catalog/pg_type.h"
3839#include "utils/array.h"
3940#include "utils/builtins.h"
4041#include "utils/rel.h"
42+ #include "utils/ruleutils.h"
43+ #include "utils/lsyscache.h"
4144#include "utils/varlena.h"
4245
4346PG_FUNCTION_INFO_V1 (bt_metap );
@@ -294,6 +297,8 @@ struct user_args
294297 bool leafpage ;
295298 bool rightmost ;
296299 TupleDesc tupd ;
300+ Relation indexRel ;
301+ bool pretty_print ;
297302};
298303
299304/*-------------------------------------------------------
@@ -372,17 +377,118 @@ bt_page_print_tuples(struct user_args *uargs)
372377 if (dlen < 0 || dlen > INDEX_SIZE_MASK )
373378 elog (ERROR , "invalid tuple length %d for tuple at offset number %u" ,
374379 dlen , offset );
375- dump = palloc0 (dlen * 3 + 1 );
376- datacstring = dump ;
377- for (off = 0 ; off < dlen ; off ++ )
380+
381+ if (!uargs -> pretty_print )
378382 {
379- if (off > 0 )
380- * dump ++ = ' ' ;
381- sprintf (dump , "%02x" , * (ptr + off ) & 0xff );
382- dump += 2 ;
383+ /* Old-style, print hex bytes */
384+ dump = palloc0 (dlen * 3 + 1 );
385+ datacstring = dump ;
386+ for (off = 0 ; off < dlen ; off ++ )
387+ {
388+ if (off > 0 )
389+ * dump ++ = ' ' ;
390+ sprintf (dump , "%02x" , * (ptr + off ) & 0xff );
391+ dump += 2 ;
392+ }
393+ values [j ++ ] = CStringGetTextDatum (datacstring );
394+ pfree (datacstring );
395+ }
396+ else
397+ {
398+ /* Do pretty-print, akin to record_out() */
399+ StringInfoData buf ;
400+ TupleDesc tupdesc ;
401+
402+ Datum itup_values [INDEX_MAX_KEYS ];
403+ bool itup_isnull [INDEX_MAX_KEYS ];
404+ char * index_columns ;
405+
406+ /*
407+ * Included attributes are added when dealing with leaf pages, discarded
408+ * for non-leaf pages as these include only data for key attributes.
409+ */
410+ int printflags = RULE_INDEXDEF_PRETTY ;
411+ if (P_ISLEAF ((BTPageOpaque ) PageGetSpecialPointer (page )))
412+ {
413+ tupdesc = RelationGetDescr (uargs -> indexRel );
414+ }
415+ else
416+ {
417+ tupdesc = CreateTupleDescCopy (RelationGetDescr (uargs -> indexRel ));
418+ tupdesc -> natts = IndexRelationGetNumberOfKeyAttributes (uargs -> indexRel );
419+ printflags |= RULE_INDEXDEF_KEYS_ONLY ;
420+ }
421+
422+ index_columns = pg_get_indexdef_columns_extended (RelationGetRelid (uargs -> indexRel ),
423+ printflags );
424+
425+
426+ index_deform_tuple (itup , tupdesc ,
427+ itup_values , itup_isnull );
428+
429+
430+ initStringInfo (& buf );
431+ appendStringInfo (& buf , "(%s)=(" , index_columns );
432+
433+ for (int i = 0 ; i < tupdesc -> natts ; i ++ )
434+ {
435+ char * value ;
436+ char * tmp ;
437+ bool nq = false;
438+
439+ if (itup_isnull [i ])
440+ value = "null" ;
441+ else
442+ {
443+ Oid foutoid ;
444+ bool typisvarlena ;
445+ Oid typoid ;
446+
447+ typoid = TupleDescAttr (tupdesc , i )-> atttypid ;
448+ getTypeOutputInfo (typoid , & foutoid , & typisvarlena );
449+ value = OidOutputFunctionCall (foutoid , itup_values [i ]);
450+ }
451+
452+ if (i == IndexRelationGetNumberOfKeyAttributes (uargs -> indexRel ))
453+ appendStringInfoString (& buf , ") INCLUDE (" );
454+ else if (i > 0 )
455+ appendStringInfoString (& buf , ", " );
456+
457+ /* Check whether we need double quotes for this value */
458+ nq = (value [0 ] == '\0' ); /* force quotes for empty string */
459+ for (tmp = value ; * tmp ; tmp ++ )
460+ {
461+ char ch = * tmp ;
462+
463+ if (ch == '"' || ch == '\\' ||
464+ ch == '(' || ch == ')' || ch == ',' ||
465+ isspace ((unsigned char ) ch ))
466+ {
467+ nq = true;
468+ break ;
469+ }
470+ }
471+
472+ /* And emit the string */
473+ if (nq )
474+ appendStringInfoCharMacro (& buf , '"' );
475+ for (tmp = value ; * tmp ; tmp ++ )
476+ {
477+ char ch = * tmp ;
478+
479+ if (ch == '"' || ch == '\\' )
480+ appendStringInfoCharMacro (& buf , ch );
481+ appendStringInfoCharMacro (& buf , ch );
482+ }
483+ if (nq )
484+ appendStringInfoCharMacro (& buf , '"' );
485+ }
486+
487+ appendStringInfoChar (& buf , ')' );
488+
489+ values [j ++ ] = CStringGetTextDatum (buf .data );
490+ pfree (buf .data );
383491 }
384- values [j ++ ] = CStringGetTextDatum (datacstring );
385- pfree (datacstring );
386492
387493 /*
388494 * We need to work around the BTreeTupleIsPivot() !heapkeyspace limitation
@@ -458,6 +564,11 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
458564 FuncCallContext * fctx ;
459565 MemoryContext mctx ;
460566 struct user_args * uargs ;
567+ bool pretty_print = false;
568+
569+ if (PG_NARGS () >= 3 ) {
570+ pretty_print = PG_GETARG_BOOL (2 );
571+ }
461572
462573 if (!superuser ())
463574 ereport (ERROR ,
@@ -521,7 +632,6 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
521632 memcpy (uargs -> page , BufferGetPage (buffer ), BLCKSZ );
522633
523634 UnlockReleaseBuffer (buffer );
524- relation_close (rel , AccessShareLock );
525635
526636 uargs -> offset = FirstOffsetNumber ;
527637
@@ -537,6 +647,8 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
537647 }
538648 uargs -> leafpage = P_ISLEAF (opaque );
539649 uargs -> rightmost = P_RIGHTMOST (opaque );
650+ uargs -> pretty_print = pretty_print ;
651+ uargs -> indexRel = rel ;
540652
541653 /* Build a tuple descriptor for our result type */
542654 if (get_call_result_type (fcinfo , NULL , & tupleDesc ) != TYPEFUNC_COMPOSITE )
@@ -560,6 +672,9 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
560672 SRF_RETURN_NEXT (fctx , result );
561673 }
562674
675+ if (uargs -> indexRel )
676+ relation_close (uargs -> indexRel , AccessShareLock );
677+
563678 SRF_RETURN_DONE (fctx );
564679}
565680
@@ -654,6 +769,10 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
654769 uargs -> leafpage = P_ISLEAF (opaque );
655770 uargs -> rightmost = P_RIGHTMOST (opaque );
656771
772+ uargs -> pretty_print = false;
773+ /* For bytea function, we cannot do pretty-print */
774+ uargs -> indexRel = NULL ;
775+
657776 /* Build a tuple descriptor for our result type */
658777 if (get_call_result_type (fcinfo , NULL , & tupleDesc ) != TYPEFUNC_COMPOSITE )
659778 elog (ERROR , "return type must be a row type" );
@@ -676,6 +795,9 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
676795 SRF_RETURN_NEXT (fctx , result );
677796 }
678797
798+ if (uargs -> indexRel )
799+ relation_close (uargs -> indexRel , AccessShareLock );
800+
679801 SRF_RETURN_DONE (fctx );
680802}
681803
0 commit comments