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