Skip to content

Commit 8146397

Browse files
committed
v6 of bt_page_items pretty-print
1 parent 18065f1 commit 8146397

6 files changed

Lines changed: 180 additions & 12 deletions

File tree

contrib/pageinspect/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ OBJS = \
1313
rawpage.o
1414

1515
EXTENSION = pageinspect
16-
DATA = pageinspect--1.8--1.9.sql \
16+
DATA = pageinspect--1.9--1.10.sql pageinspect--1.8--1.9.sql \
1717
pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \
1818
pageinspect--1.5.sql pageinspect--1.5--1.6.sql \
1919
pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \

contrib/pageinspect/btreefuncs.c

Lines changed: 132 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
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"
@@ -38,6 +39,8 @@
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

4346
PG_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

contrib/pageinspect/expected/btree.out

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
CREATE TABLE test1 (a int8, b int4range);
22
INSERT INTO test1 VALUES (72057594037927937, '[0,1)');
33
CREATE INDEX test1_a_idx ON test1 USING btree (a);
4+
CREATE INDEX test1_a_b_idx ON test1 USING btree (a, b);
45
\x
56
SELECT * FROM bt_metap('test1_a_idx');
67
-[ RECORD 1 ]-------------+-------
@@ -34,6 +35,30 @@ btpo_flags | 3
3435

3536
SELECT * FROM bt_page_stats('test1_a_idx', 2);
3637
ERROR: block number out of range
38+
SELECT * FROM bt_page_items('test1_a_idx', 1, true);
39+
-[ RECORD 1 ]-----------------------
40+
itemoffset | 1
41+
ctid | (0,1)
42+
itemlen | 16
43+
nulls | f
44+
vars | f
45+
data | (a)=(72057594037927937)
46+
dead | f
47+
htid | (0,1)
48+
tids |
49+
50+
SELECT * FROM bt_page_items('test1_a_b_idx', 1, true);
51+
-[ RECORD 1 ]-----------------------------------
52+
itemoffset | 1
53+
ctid | (0,1)
54+
itemlen | 32
55+
nulls | f
56+
vars | t
57+
data | (a, b)=(72057594037927937, "[0,1)")
58+
dead | f
59+
htid | (0,1)
60+
tids |
61+
3762
SELECT * FROM bt_page_items('test1_a_idx', -1);
3863
ERROR: invalid block number
3964
SELECT * FROM bt_page_items('test1_a_idx', 0);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
3+
DROP FUNCTION bt_page_items(text, int8);
4+
CREATE FUNCTION bt_page_items(IN relname text, IN blkno int8,
5+
IN pretty_print boolean DEFAULT FALSE,
6+
OUT itemoffset smallint,
7+
OUT ctid tid,
8+
OUT itemlen smallint,
9+
OUT nulls bool,
10+
OUT vars bool,
11+
OUT data text,
12+
OUT dead boolean,
13+
OUT htid tid,
14+
OUT tids tid[])
15+
RETURNS SETOF record
16+
AS 'MODULE_PATHNAME', 'bt_page_items_1_9'
17+
LANGUAGE C STRICT PARALLEL SAFE;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pageinspect extension
22
comment = 'inspect the contents of database pages at a low level'
3-
default_version = '1.9'
3+
default_version = '1.10'
44
module_pathname = '$libdir/pageinspect'
55
relocatable = true

contrib/pageinspect/sql/btree.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
CREATE TABLE test1 (a int8, b int4range);
22
INSERT INTO test1 VALUES (72057594037927937, '[0,1)');
33
CREATE INDEX test1_a_idx ON test1 USING btree (a);
4+
CREATE INDEX test1_a_b_idx ON test1 USING btree (a, b);
45

56
\x
67

@@ -11,6 +12,9 @@ SELECT * FROM bt_page_stats('test1_a_idx', 0);
1112
SELECT * FROM bt_page_stats('test1_a_idx', 1);
1213
SELECT * FROM bt_page_stats('test1_a_idx', 2);
1314

15+
SELECT * FROM bt_page_items('test1_a_idx', 1, true);
16+
SELECT * FROM bt_page_items('test1_a_b_idx', 1, true);
17+
1418
SELECT * FROM bt_page_items('test1_a_idx', -1);
1519
SELECT * FROM bt_page_items('test1_a_idx', 0);
1620
SELECT * FROM bt_page_items('test1_a_idx', 1);

0 commit comments

Comments
 (0)