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