|
| 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 | +#defineDatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X)) |
| 24 | +#defineItemPointerGetDatum(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 | +intraw_page_size; |
| 36 | +TupleDesctupdesc; |
| 37 | +Pagepage; |
| 38 | +GinPageOpaqueopaq; |
| 39 | +GinMetaPageData*metadata; |
| 40 | +HeapTupleresultTuple; |
| 41 | +Datumvalues[10]; |
| 42 | +boolnulls[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 | +returnHeapTupleGetDatum(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 | +intraw_page_size; |
| 98 | +TupleDesctupdesc; |
| 99 | +Pagepage; |
| 100 | +GinPageOpaqueopaq; |
| 101 | +HeapTupleresultTuple; |
| 102 | +Datumvalues[3]; |
| 103 | +boolnulls[10]; |
| 104 | +Datumflags[16]; |
| 105 | +intnflags=0; |
| 106 | +uint16flagbits; |
| 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 | +returnHeapTupleGetDatum(resultTuple); |
| 163 | +} |
| 164 | + |
| 165 | +typedefstructgin_leafpage_items_state |
| 166 | +{ |
| 167 | +TupleDesctupd; |
| 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 | +intraw_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 | +TupleDesctupdesc; |
| 190 | +MemoryContextmctx; |
| 191 | +Pagepage; |
| 192 | +GinPageOpaqueopaq; |
| 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 | +HeapTupleresultTuple; |
| 245 | +Datumresult; |
| 246 | +Datumvalues[3]; |
| 247 | +boolnulls[3]; |
| 248 | +intndecoded, |
| 249 | +i; |
| 250 | +ItemPointertids; |
| 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 | +} |