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