Skip to content

Commit 3a82bc6

Browse files
committed
Add pageinspect functions for inspecting GIN indexes.
Patch by me, Peter Geoghegan and Michael Paquier, reviewed by Amit Kapila.
1 parent adbfab1 commit 3a82bc6

File tree

5 files changed

+451
-4
lines changed

5 files changed

+451
-4
lines changed

contrib/pageinspect/Makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# contrib/pageinspect/Makefile
22

33
MODULE_big = pageinspect
4-
OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o brinfuncs.o $(WIN32RES)
4+
OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o \
5+
brinfuncs.o ginfuncs.o $(WIN32RES)
56

67
EXTENSION = pageinspect
7-
DATA = pageinspect--1.3.sql pageinspect--1.0--1.1.sql \
8-
pageinspect--1.2--1.3.sql \
9-
pageinspect--1.1--1.2.sql pageinspect--unpackaged--1.0.sql
8+
DATA = pageinspect--1.3.sql pageinspect--1.2--1.3.sql \
9+
pageinspect--1.1--1.2.sql pageinspect--1.0--1.1.sql \
10+
pageinspect--unpackaged--1.0.sql
1011
PGFILEDESC = "pageinspect - functions to inspect contents of database pages"
1112

1213
ifdef USE_PGXS

contrib/pageinspect/ginfuncs.c

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
* ginfuncs.c
3+
* Functions to investigate the content of GIN indexes
4+
*
5+
* Copyright (c) 2014, PostgreSQL Global Development Group
6+
*
7+
* IDENTIFICATION
8+
* contrib/pageinspect/ginfuncs.c
9+
*/
10+
#include "postgres.h"
11+
12+
#include "access/gin.h"
13+
#include "access/gin_private.h"
14+
#include "access/htup_details.h"
15+
#include "catalog/namespace.h"
16+
#include "catalog/pg_type.h"
17+
#include "funcapi.h"
18+
#include "miscadmin.h"
19+
#include "utils/array.h"
20+
#include "utils/builtins.h"
21+
#include "utils/rel.h"
22+
23+
#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
24+
#define ItemPointerGetDatum(X) PointerGetDatum(X)
25+
26+
27+
PG_FUNCTION_INFO_V1(gin_metapage_info);
28+
PG_FUNCTION_INFO_V1(gin_page_opaque_info);
29+
PG_FUNCTION_INFO_V1(gin_leafpage_items);
30+
31+
Datum
32+
gin_metapage_info(PG_FUNCTION_ARGS)
33+
{
34+
bytea *raw_page = PG_GETARG_BYTEA_P(0);
35+
int raw_page_size;
36+
TupleDesc tupdesc;
37+
Page page;
38+
GinPageOpaque opaq;
39+
GinMetaPageData *metadata;
40+
HeapTuple resultTuple;
41+
Datum values[10];
42+
bool nulls[10];
43+
44+
if (!superuser())
45+
ereport(ERROR,
46+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
47+
(errmsg("must be superuser to use raw page functions"))));
48+
49+
raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
50+
if (raw_page_size < BLCKSZ)
51+
ereport(ERROR,
52+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
53+
errmsg("input page too small (%d bytes)", raw_page_size)));
54+
page = VARDATA(raw_page);
55+
56+
opaq = (GinPageOpaque) PageGetSpecialPointer(page);
57+
if (opaq->flags != GIN_META)
58+
ereport(ERROR,
59+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
60+
errmsg("input page is not a GIN metapage"),
61+
errdetail("Flags %04X, expected %04X",
62+
opaq->flags, GIN_META)));
63+
64+
/* Build a tuple descriptor for our result type */
65+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
66+
elog(ERROR, "return type must be a row type");
67+
68+
metadata = GinPageGetMeta(page);
69+
70+
memset(nulls, 0, sizeof(nulls));
71+
72+
values[0] = Int64GetDatum(metadata->head);
73+
values[1] = Int64GetDatum(metadata->tail);
74+
values[2] = Int32GetDatum(metadata->tailFreeSize);
75+
values[3] = Int64GetDatum(metadata->nPendingPages);
76+
values[4] = Int64GetDatum(metadata->nPendingHeapTuples);
77+
78+
/* statistics, updated by VACUUM */
79+
values[5] = Int64GetDatum(metadata->nTotalPages);
80+
values[6] = Int64GetDatum(metadata->nEntryPages);
81+
values[7] = Int64GetDatum(metadata->nDataPages);
82+
values[8] = Int64GetDatum(metadata->nEntries);
83+
84+
values[9] = Int32GetDatum(metadata->ginVersion);
85+
86+
/* Build and return the result tuple. */
87+
resultTuple = heap_form_tuple(tupdesc, values, nulls);
88+
89+
return HeapTupleGetDatum(resultTuple);
90+
}
91+
92+
93+
Datum
94+
gin_page_opaque_info(PG_FUNCTION_ARGS)
95+
{
96+
bytea *raw_page = PG_GETARG_BYTEA_P(0);
97+
int raw_page_size;
98+
TupleDesc tupdesc;
99+
Page page;
100+
GinPageOpaque opaq;
101+
HeapTuple resultTuple;
102+
Datum values[3];
103+
bool nulls[10];
104+
Datum flags[16];
105+
int nflags = 0;
106+
uint16 flagbits;
107+
108+
if (!superuser())
109+
ereport(ERROR,
110+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
111+
(errmsg("must be superuser to use raw page functions"))));
112+
113+
raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
114+
if (raw_page_size < BLCKSZ)
115+
ereport(ERROR,
116+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117+
errmsg("input page too small (%d bytes)", raw_page_size)));
118+
page = VARDATA(raw_page);
119+
120+
opaq = (GinPageOpaque) PageGetSpecialPointer(page);
121+
122+
/* Build a tuple descriptor for our result type */
123+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
124+
elog(ERROR, "return type must be a row type");
125+
126+
/* Convert the flags bitmask to an array of human-readable names */
127+
flagbits = opaq->flags;
128+
if (flagbits & GIN_DATA)
129+
flags[nflags++] = CStringGetTextDatum("data");
130+
if (flagbits & GIN_LEAF)
131+
flags[nflags++] = CStringGetTextDatum("leaf");
132+
if (flagbits & GIN_DELETED)
133+
flags[nflags++] = CStringGetTextDatum("deleted");
134+
if (flagbits & GIN_META)
135+
flags[nflags++] = CStringGetTextDatum("meta");
136+
if (flagbits & GIN_LIST)
137+
flags[nflags++] = CStringGetTextDatum("list");
138+
if (flagbits & GIN_LIST_FULLROW)
139+
flags[nflags++] = CStringGetTextDatum("list_fullrow");
140+
if (flagbits & GIN_INCOMPLETE_SPLIT)
141+
flags[nflags++] = CStringGetTextDatum("incomplete_split");
142+
if (flagbits & GIN_COMPRESSED)
143+
flags[nflags++] = CStringGetTextDatum("compressed");
144+
flagbits &= ~(GIN_DATA | GIN_LEAF | GIN_DELETED | GIN_META | GIN_LIST |
145+
GIN_LIST_FULLROW | GIN_INCOMPLETE_SPLIT | GIN_COMPRESSED);
146+
if (flagbits)
147+
{
148+
/* any flags we don't recognize are printed in hex */
149+
flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits));
150+
}
151+
152+
memset(nulls, 0, sizeof(nulls));
153+
154+
values[0] = Int64GetDatum(opaq->rightlink);
155+
values[1] = Int64GetDatum(opaq->maxoff);
156+
values[2] = PointerGetDatum(
157+
construct_array(flags, nflags, TEXTOID, -1, false, 'i'));
158+
159+
/* Build and return the result tuple. */
160+
resultTuple = heap_form_tuple(tupdesc, values, nulls);
161+
162+
return HeapTupleGetDatum(resultTuple);
163+
}
164+
165+
typedef struct gin_leafpage_items_state
166+
{
167+
TupleDesc tupd;
168+
GinPostingList *seg;
169+
GinPostingList *lastseg;
170+
} gin_leafpage_items_state;
171+
172+
Datum
173+
gin_leafpage_items(PG_FUNCTION_ARGS)
174+
{
175+
bytea *raw_page = PG_GETARG_BYTEA_P(0);
176+
int raw_page_size;
177+
FuncCallContext *fctx;
178+
gin_leafpage_items_state *inter_call_data;
179+
180+
if (!superuser())
181+
ereport(ERROR,
182+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
183+
(errmsg("must be superuser to use raw page functions"))));
184+
185+
raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
186+
187+
if (SRF_IS_FIRSTCALL())
188+
{
189+
TupleDesc tupdesc;
190+
MemoryContext mctx;
191+
Page page;
192+
GinPageOpaque opaq;
193+
194+
if (raw_page_size < BLCKSZ)
195+
ereport(ERROR,
196+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
197+
errmsg("input page too small (%d bytes)", raw_page_size)));
198+
page = VARDATA(raw_page);
199+
200+
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
201+
ereport(ERROR,
202+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
203+
errmsg("input page is not a valid GIN data leaf page"),
204+
errdetail("Special size %d, expected %d",
205+
(int) PageGetSpecialSize(page),
206+
(int) MAXALIGN(sizeof(GinPageOpaqueData)))));
207+
208+
opaq = (GinPageOpaque) PageGetSpecialPointer(page);
209+
if (opaq->flags != (GIN_DATA | GIN_LEAF | GIN_COMPRESSED))
210+
ereport(ERROR,
211+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
212+
errmsg("input page is not a compressed GIN data leaf page"),
213+
errdetail("Flags %04X, expected %04X",
214+
opaq->flags,
215+
(GIN_DATA | GIN_LEAF | GIN_COMPRESSED))));
216+
217+
fctx = SRF_FIRSTCALL_INIT();
218+
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
219+
220+
inter_call_data = palloc(sizeof(gin_leafpage_items_state));
221+
222+
/* Build a tuple descriptor for our result type */
223+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
224+
elog(ERROR, "return type must be a row type");
225+
226+
inter_call_data->tupd = tupdesc;
227+
228+
inter_call_data->seg = GinDataLeafPageGetPostingList(page);
229+
inter_call_data->lastseg = (GinPostingList *)
230+
(((char *) inter_call_data->seg) +
231+
GinDataLeafPageGetPostingListSize(page));
232+
233+
fctx->user_fctx = inter_call_data;
234+
235+
MemoryContextSwitchTo(mctx);
236+
}
237+
238+
fctx = SRF_PERCALL_SETUP();
239+
inter_call_data = fctx->user_fctx;
240+
241+
if (inter_call_data->seg != inter_call_data->lastseg)
242+
{
243+
GinPostingList *cur = inter_call_data->seg;
244+
HeapTuple resultTuple;
245+
Datum result;
246+
Datum values[3];
247+
bool nulls[3];
248+
int ndecoded,
249+
i;
250+
ItemPointer tids;
251+
Datum *tids_datum;
252+
253+
memset(nulls, 0, sizeof(nulls));
254+
255+
values[0] = ItemPointerGetDatum(&cur->first);
256+
values[1] = UInt16GetDatum(cur->nbytes);
257+
258+
/* build an array of decoded item pointers */
259+
tids = ginPostingListDecode(cur, &ndecoded);
260+
tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum));
261+
for (i = 0; i < ndecoded; i++)
262+
tids_datum[i] = ItemPointerGetDatum(&tids[i]);
263+
values[2] = PointerGetDatum(construct_array(tids_datum,
264+
ndecoded,
265+
TIDOID,
266+
sizeof(ItemPointerData),
267+
false, 's'));
268+
pfree(tids_datum);
269+
pfree(tids);
270+
271+
/* Build and return the result tuple. */
272+
resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
273+
result = HeapTupleGetDatum(resultTuple);
274+
275+
inter_call_data->seg = GinNextPostingListSegment(cur);
276+
277+
SRF_RETURN_NEXT(fctx, result);
278+
}
279+
else
280+
SRF_RETURN_DONE(fctx);
281+
}

contrib/pageinspect/pageinspect--1.2--1.3.sql

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,41 @@ CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass,
4141
RETURNS SETOF record
4242
AS 'MODULE_PATHNAME', 'brin_page_items'
4343
LANGUAGE C STRICT;
44+
45+
--
46+
-- gin_metapage_info()
47+
--
48+
CREATE FUNCTION gin_metapage_info(IN page bytea,
49+
OUT pending_head bigint,
50+
OUT pending_tail bigint,
51+
OUT tail_free_size int4,
52+
OUT n_pending_pages bigint,
53+
OUT n_pending_tuples bigint,
54+
OUT n_total_pages bigint,
55+
OUT n_entry_pages bigint,
56+
OUT n_data_pages bigint,
57+
OUT n_entries bigint,
58+
OUT version int4)
59+
AS 'MODULE_PATHNAME', 'gin_metapage_info'
60+
LANGUAGE C STRICT;
61+
62+
--
63+
-- gin_page_opaque_info()
64+
--
65+
CREATE FUNCTION gin_page_opaque_info(IN page bytea,
66+
OUT rightlink bigint,
67+
OUT maxoff int4,
68+
OUT flags text[])
69+
AS 'MODULE_PATHNAME', 'gin_page_opaque_info'
70+
LANGUAGE C STRICT;
71+
72+
--
73+
-- gin_leafpage_items()
74+
--
75+
CREATE FUNCTION gin_leafpage_items(IN page bytea,
76+
OUT first_tid tid,
77+
OUT nbytes int2,
78+
OUT tids tid[])
79+
RETURNS SETOF record
80+
AS 'MODULE_PATHNAME', 'gin_leafpage_items'
81+
LANGUAGE C STRICT;

contrib/pageinspect/pageinspect--1.3.sql

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,45 @@ CREATE FUNCTION fsm_page_contents(IN page bytea)
144144
RETURNS text
145145
AS 'MODULE_PATHNAME', 'fsm_page_contents'
146146
LANGUAGE C STRICT;
147+
148+
--
149+
-- GIN functions
150+
--
151+
152+
--
153+
-- gin_metapage_info()
154+
--
155+
CREATE FUNCTION gin_metapage_info(IN page bytea,
156+
OUT pending_head bigint,
157+
OUT pending_tail bigint,
158+
OUT tail_free_size int4,
159+
OUT n_pending_pages bigint,
160+
OUT n_pending_tuples bigint,
161+
OUT n_total_pages bigint,
162+
OUT n_entry_pages bigint,
163+
OUT n_data_pages bigint,
164+
OUT n_entries bigint,
165+
OUT version int4)
166+
AS 'MODULE_PATHNAME', 'gin_metapage_info'
167+
LANGUAGE C STRICT;
168+
169+
--
170+
-- gin_page_opaque_info()
171+
--
172+
CREATE FUNCTION gin_page_opaque_info(IN page bytea,
173+
OUT rightlink bigint,
174+
OUT maxoff int4,
175+
OUT flags text[])
176+
AS 'MODULE_PATHNAME', 'gin_page_opaque_info'
177+
LANGUAGE C STRICT;
178+
179+
--
180+
-- gin_leafpage_items()
181+
--
182+
CREATE FUNCTION gin_leafpage_items(IN page bytea,
183+
OUT first_tid tid,
184+
OUT nbytes int2,
185+
OUT tids tid[])
186+
RETURNS SETOF record
187+
AS 'MODULE_PATHNAME', 'gin_leafpage_items'
188+
LANGUAGE C STRICT;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy