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