Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginutil.c
4 : * Utility routines for the Postgres inverted index access method.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/gin/ginutil.c
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include "access/gin_private.h"
18 : #include "access/ginxlog.h"
19 : #include "access/reloptions.h"
20 : #include "access/xloginsert.h"
21 : #include "catalog/pg_collation.h"
22 : #include "catalog/pg_type.h"
23 : #include "commands/vacuum.h"
24 : #include "miscadmin.h"
25 : #include "storage/indexfsm.h"
26 : #include "storage/lmgr.h"
27 : #include "storage/predicate.h"
28 : #include "utils/builtins.h"
29 : #include "utils/index_selfuncs.h"
30 : #include "utils/typcache.h"
31 :
32 :
33 : /*
34 : * GIN handler function: return IndexAmRoutine with access method parameters
35 : * and callbacks.
36 : */
37 : Datum
2639 tgl 38 CBC 870 : ginhandler(PG_FUNCTION_ARGS)
39 : {
40 870 : IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
41 :
42 870 : amroutine->amstrategies = 0;
2537 teodor 43 870 : amroutine->amsupport = GINNProcs;
1105 akorotkov 44 870 : amroutine->amoptsprocnum = GIN_OPTIONS_PROC;
2639 tgl 45 870 : amroutine->amcanorder = false;
46 870 : amroutine->amcanorderbyop = false;
47 870 : amroutine->amcanbackward = false;
48 870 : amroutine->amcanunique = false;
49 870 : amroutine->amcanmulticol = true;
50 870 : amroutine->amoptionalkey = true;
51 870 : amroutine->amsearcharray = false;
52 870 : amroutine->amsearchnulls = false;
53 870 : amroutine->amstorage = true;
54 870 : amroutine->amclusterable = false;
1836 teodor 55 870 : amroutine->ampredlocks = true;
2244 rhaas 56 870 : amroutine->amcanparallel = false;
1828 teodor 57 870 : amroutine->amcaninclude = false;
1180 akapila 58 870 : amroutine->amusemaintenanceworkmem = true;
20 tomas.vondra 59 GNC 870 : amroutine->amsummarizing = false;
1180 akapila 60 CBC 870 : amroutine->amparallelvacuumoptions =
1180 akapila 61 ECB : VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_CLEANUP;
2639 tgl 62 GIC 870 : amroutine->amkeytype = InvalidOid;
2639 tgl 63 ECB :
2639 tgl 64 GIC 870 : amroutine->ambuild = ginbuild;
2639 tgl 65 CBC 870 : amroutine->ambuildempty = ginbuildempty;
66 870 : amroutine->aminsert = gininsert;
67 870 : amroutine->ambulkdelete = ginbulkdelete;
68 870 : amroutine->amvacuumcleanup = ginvacuumcleanup;
69 870 : amroutine->amcanreturn = NULL;
70 870 : amroutine->amcostestimate = gincostestimate;
71 870 : amroutine->amoptions = ginoptions;
2430 72 870 : amroutine->amproperty = NULL;
1468 alvherre 73 870 : amroutine->ambuildphasename = NULL;
2639 tgl 74 870 : amroutine->amvalidate = ginvalidate;
981 75 870 : amroutine->amadjustmembers = ginadjustmembers;
2639 76 870 : amroutine->ambeginscan = ginbeginscan;
77 870 : amroutine->amrescan = ginrescan;
78 870 : amroutine->amgettuple = NULL;
79 870 : amroutine->amgetbitmap = gingetbitmap;
80 870 : amroutine->amendscan = ginendscan;
81 870 : amroutine->ammarkpos = NULL;
82 870 : amroutine->amrestrpos = NULL;
2266 rhaas 83 870 : amroutine->amestimateparallelscan = NULL;
84 870 : amroutine->aminitparallelscan = NULL;
85 870 : amroutine->amparallelrescan = NULL;
2639 tgl 86 ECB :
2639 tgl 87 GIC 870 : PG_RETURN_POINTER(amroutine);
2639 tgl 88 ECB : }
89 :
90 : /*
91 : * initGinState: fill in an empty GinState struct to describe the index
92 : *
93 : * Note: assorted subsidiary data is allocated in the CurrentMemoryContext.
94 : */
95 : void
6031 bruce 96 GIC 1064 : initGinState(GinState *state, Relation index)
6031 bruce 97 ECB : {
4475 tgl 98 GIC 1064 : TupleDesc origTupdesc = RelationGetDescr(index);
5050 bruce 99 ECB : int i;
100 :
4475 tgl 101 GIC 1064 : MemSet(state, 0, sizeof(GinState));
5385 tgl 102 ECB :
4475 tgl 103 GIC 1064 : state->index = index;
578 michael 104 CBC 1064 : state->oneCol = (origTupdesc->natts == 1);
4475 tgl 105 1064 : state->origTupdesc = origTupdesc;
5385 tgl 106 ECB :
4475 tgl 107 GIC 2269 : for (i = 0; i < origTupdesc->natts; i++)
5385 tgl 108 ECB : {
2058 andres 109 GIC 1205 : Form_pg_attribute attr = TupleDescAttr(origTupdesc, i);
2058 andres 110 ECB :
4475 tgl 111 GIC 1205 : if (state->oneCol)
4475 tgl 112 CBC 923 : state->tupdesc[i] = state->origTupdesc;
4475 tgl 113 ECB : else
114 : {
1601 andres 115 GIC 282 : state->tupdesc[i] = CreateTemplateTupleDesc(2);
4475 tgl 116 ECB :
4475 tgl 117 GIC 282 : TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
4475 tgl 118 ECB : INT2OID, -1, 0);
4475 tgl 119 GIC 282 : TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
2058 andres 120 ECB : attr->atttypid,
121 : attr->atttypmod,
2058 andres 122 GIC 282 : attr->attndims);
4397 tgl 123 CBC 282 : TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2,
2058 andres 124 ECB : attr->attcollation);
125 : }
126 :
127 : /*
128 : * If the compare proc isn't specified in the opclass definition, look
129 : * up the index key type's default btree comparator.
130 : */
2386 tgl 131 GIC 1205 : if (index_getprocid(index, i + 1, GIN_COMPARE_PROC) != InvalidOid)
2386 tgl 132 ECB : {
2386 tgl 133 GIC 640 : fmgr_info_copy(&(state->compareFn[i]),
2386 tgl 134 CBC 640 : index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
2386 tgl 135 ECB : CurrentMemoryContext);
136 : }
137 : else
138 : {
139 : TypeCacheEntry *typentry;
140 :
2058 andres 141 GIC 565 : typentry = lookup_type_cache(attr->atttypid,
2386 tgl 142 ECB : TYPECACHE_CMP_PROC_FINFO);
2386 tgl 143 GIC 565 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
2386 tgl 144 LBC 0 : ereport(ERROR,
2386 tgl 145 EUB : (errcode(ERRCODE_UNDEFINED_FUNCTION),
146 : errmsg("could not identify a comparison function for type %s",
147 : format_type_be(attr->atttypid))));
2386 tgl 148 GIC 565 : fmgr_info_copy(&(state->compareFn[i]),
2386 tgl 149 ECB : &(typentry->cmp_proc_finfo),
150 : CurrentMemoryContext);
151 : }
152 :
153 : /* Opclass must always provide extract procs */
5385 tgl 154 GIC 1205 : fmgr_info_copy(&(state->extractValueFn[i]),
5050 bruce 155 CBC 1205 : index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
5050 bruce 156 ECB : CurrentMemoryContext);
5385 tgl 157 GIC 1205 : fmgr_info_copy(&(state->extractQueryFn[i]),
5050 bruce 158 CBC 1205 : index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
5050 bruce 159 ECB : CurrentMemoryContext);
160 :
161 : /*
162 : * Check opclass capability to do tri-state or binary logic consistent
163 : * check.
164 : */
3315 heikki.linnakangas 165 GIC 1205 : if (index_getprocid(index, i + 1, GIN_TRICONSISTENT_PROC) != InvalidOid)
3315 heikki.linnakangas 166 ECB : {
3315 heikki.linnakangas 167 GIC 1003 : fmgr_info_copy(&(state->triConsistentFn[i]),
2118 tgl 168 CBC 1003 : index_getprocinfo(index, i + 1, GIN_TRICONSISTENT_PROC),
3315 heikki.linnakangas 169 ECB : CurrentMemoryContext);
170 : }
171 :
3315 heikki.linnakangas 172 GIC 1205 : if (index_getprocid(index, i + 1, GIN_CONSISTENT_PROC) != InvalidOid)
3315 heikki.linnakangas 173 ECB : {
3315 heikki.linnakangas 174 GIC 1205 : fmgr_info_copy(&(state->consistentFn[i]),
2118 tgl 175 CBC 1205 : index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
3315 heikki.linnakangas 176 ECB : CurrentMemoryContext);
177 : }
178 :
3315 heikki.linnakangas 179 GIC 1205 : if (state->consistentFn[i].fn_oid == InvalidOid &&
3315 heikki.linnakangas 180 LBC 0 : state->triConsistentFn[i].fn_oid == InvalidOid)
3315 heikki.linnakangas 181 EUB : {
3315 heikki.linnakangas 182 UIC 0 : elog(ERROR, "missing GIN support function (%d or %d) for attribute %d of index \"%s\"",
3315 heikki.linnakangas 183 EUB : GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC,
184 : i + 1, RelationGetRelationName(index));
185 : }
186 :
187 : /*
188 : * Check opclass capability to do partial match.
189 : */
5050 bruce 190 GIC 1205 : if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
5385 tgl 191 ECB : {
5385 tgl 192 GIC 302 : fmgr_info_copy(&(state->comparePartialFn[i]),
2118 tgl 193 CBC 302 : index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
5385 tgl 194 ECB : CurrentMemoryContext);
5385 tgl 195 GIC 302 : state->canPartialMatch[i] = true;
5385 tgl 196 ECB : }
197 : else
198 : {
5385 tgl 199 GIC 903 : state->canPartialMatch[i] = false;
5385 tgl 200 ECB : }
201 :
202 : /*
203 : * If the index column has a specified collation, we should honor that
204 : * while doing comparisons. However, we may have a collatable storage
205 : * type for a noncollatable indexed data type (for instance, hstore
206 : * uses text index entries). If there's no index collation then
207 : * specify default collation in case the support functions need
208 : * collation. This is harmless if the support functions don't care
209 : * about collation, so we just do it unconditionally. (We could
210 : * alternatively call get_typcollation, but that seems like expensive
211 : * overkill --- there aren't going to be any cases where a GIN storage
212 : * type has a nondefault collation.)
213 : */
4380 tgl 214 GIC 1205 : if (OidIsValid(index->rd_indcollation[i]))
4370 tgl 215 CBC 171 : state->supportCollation[i] = index->rd_indcollation[i];
4380 tgl 216 ECB : else
4370 tgl 217 GIC 1034 : state->supportCollation[i] = DEFAULT_COLLATION_OID;
5385 tgl 218 ECB : }
5385 tgl 219 GIC 1064 : }
5385 tgl 220 ECB :
221 : /*
222 : * Extract attribute (column) number of stored entry from GIN tuple
223 : */
224 : OffsetNumber
5385 tgl 225 GIC 6404657 : gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
5385 tgl 226 ECB : {
227 : OffsetNumber colN;
228 :
4475 tgl 229 GIC 6404657 : if (ginstate->oneCol)
4475 tgl 230 ECB : {
231 : /* column number is not stored explicitly */
4475 tgl 232 GIC 2075192 : colN = FirstOffsetNumber;
4475 tgl 233 ECB : }
234 : else
235 : {
236 : Datum res;
237 : bool isnull;
238 :
239 : /*
240 : * First attribute is always int16, so we can safely use any tuple
241 : * descriptor to obtain first attribute of tuple
242 : */
5385 tgl 243 GIC 4329465 : res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
5385 tgl 244 ECB : &isnull);
5385 tgl 245 GIC 4329465 : Assert(!isnull);
5441 tgl 246 ECB :
5385 tgl 247 GIC 4329465 : colN = DatumGetUInt16(res);
5050 bruce 248 CBC 4329465 : Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
5385 tgl 249 ECB : }
250 :
5385 tgl 251 GIC 6404657 : return colN;
5385 tgl 252 ECB : }
253 :
254 : /*
255 : * Extract stored datum (and possible null category) from GIN tuple
256 : */
257 : Datum
4475 tgl 258 GIC 4239522 : gintuple_get_key(GinState *ginstate, IndexTuple tuple,
4475 tgl 259 ECB : GinNullCategory *category)
260 : {
261 : Datum res;
262 : bool isnull;
263 :
5050 bruce 264 GIC 4239522 : if (ginstate->oneCol)
5385 tgl 265 ECB : {
266 : /*
267 : * Single column index doesn't store attribute numbers in tuples
268 : */
5385 tgl 269 GIC 2075154 : res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
5385 tgl 270 ECB : &isnull);
271 : }
272 : else
273 : {
274 : /*
275 : * Since the datum type depends on which index column it's from, we
276 : * must be careful to use the right tuple descriptor here.
277 : */
5385 tgl 278 GIC 2164368 : OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);
5385 tgl 279 ECB :
5385 tgl 280 GIC 2164368 : res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
5385 tgl 281 CBC 2164368 : ginstate->tupdesc[colN - 1],
5385 tgl 282 ECB : &isnull);
283 : }
284 :
4475 tgl 285 GIC 4239522 : if (isnull)
4475 tgl 286 CBC 897 : *category = GinGetNullCategory(tuple, ginstate);
4475 tgl 287 ECB : else
4475 tgl 288 GIC 4238625 : *category = GIN_CAT_NORM_KEY;
5385 tgl 289 ECB :
5385 tgl 290 GIC 4239522 : return res;
6186 teodor 291 ECB : }
292 :
293 : /*
294 : * Allocate a new page (either by recycling, or by extending the index file)
295 : * The returned buffer is already pinned and exclusive-locked
296 : * Caller is responsible for initializing the page by calling GinInitBuffer
297 : */
298 : Buffer
6031 bruce 299 GIC 3753 : GinNewBuffer(Relation index)
6031 bruce 300 ECB : {
301 : Buffer buffer;
302 :
303 : /* First, try to get a page from FSM */
304 : for (;;)
6031 bruce 305 UBC 0 : {
5304 heikki.linnakangas 306 CBC 3753 : BlockNumber blkno = GetFreeIndexPage(index);
307 :
6186 teodor 308 3753 : if (blkno == InvalidBlockNumber)
309 3701 : break;
310 :
311 52 : buffer = ReadBuffer(index, blkno);
312 :
313 : /*
314 : * We have to guard against the possibility that someone else already
315 : * recycled this page; the buffer may be locked if so.
316 : */
6031 bruce 317 52 : if (ConditionalLockBuffer(buffer))
318 : {
1578 akorotkov 319 52 : if (GinPageIsRecyclable(BufferGetPage(buffer)))
6031 bruce 320 52 : return buffer; /* OK to use */
321 :
6186 teodor 322 UBC 0 : LockBuffer(buffer, GIN_UNLOCK);
323 : }
324 :
325 : /* Can't use it, so release buffer and try again */
326 0 : ReleaseBuffer(buffer);
327 : }
328 :
329 : /* Must extend the file */
4 andres 330 GNC 3701 : buffer = ExtendBufferedRel(EB_REL(index), MAIN_FORKNUM, NULL,
331 : EB_LOCK_FIRST);
332 :
6186 teodor 333 GIC 3701 : return buffer;
6186 teodor 334 ECB : }
335 :
336 : void
6031 bruce 337 CBC 29924 : GinInitPage(Page page, uint32 f, Size pageSize)
6031 bruce 338 ECB : {
6186 teodor 339 : GinPageOpaque opaque;
340 :
6186 teodor 341 GIC 29924 : PageInit(page, pageSize, sizeof(GinPageOpaqueData));
6186 teodor 342 ECB :
6186 teodor 343 GIC 29924 : opaque = GinPageGetOpaque(page);
6031 bruce 344 CBC 29924 : opaque->flags = f;
6186 teodor 345 29924 : opaque->rightlink = InvalidBlockNumber;
6186 teodor 346 GIC 29924 : }
347 :
6186 teodor 348 ECB : void
6031 bruce 349 GIC 1945 : GinInitBuffer(Buffer b, uint32 f)
350 : {
2545 kgrittn 351 CBC 1945 : GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
6186 teodor 352 GIC 1945 : }
6186 teodor 353 ECB :
354 : void
5129 tgl 355 CBC 24111 : GinInitMetabuffer(Buffer b)
356 : {
5050 bruce 357 ECB : GinMetaPageData *metadata;
2545 kgrittn 358 CBC 24111 : Page page = BufferGetPage(b);
5129 tgl 359 ECB :
5129 tgl 360 CBC 24111 : GinInitPage(page, GIN_META, BufferGetPageSize(b));
5129 tgl 361 ECB :
5129 tgl 362 CBC 24111 : metadata = GinPageGetMeta(page);
5129 tgl 363 ECB :
5129 tgl 364 CBC 24111 : metadata->head = metadata->tail = InvalidBlockNumber;
365 24111 : metadata->tailFreeSize = 0;
5129 tgl 366 GIC 24111 : metadata->nPendingPages = 0;
367 24111 : metadata->nPendingHeapTuples = 0;
4557 368 24111 : metadata->nTotalPages = 0;
369 24111 : metadata->nEntryPages = 0;
370 24111 : metadata->nDataPages = 0;
371 24111 : metadata->nEntries = 0;
4475 tgl 372 CBC 24111 : metadata->ginVersion = GIN_CURRENT_VERSION;
1984 tgl 373 ECB :
374 : /*
375 : * Set pd_lower just past the end of the metadata. This is essential,
376 : * because without doing so, metadata will be lost if xlog.c compresses
377 : * the page.
378 : */
1984 tgl 379 GIC 24111 : ((PageHeader) page)->pd_lower =
1984 tgl 380 CBC 24111 : ((char *) metadata + sizeof(GinMetaPageData)) - (char *) page;
5129 tgl 381 GIC 24111 : }
382 :
383 : /*
384 : * Compare two keys of the same index column
4475 tgl 385 ECB : */
6186 teodor 386 : int
4475 tgl 387 GIC 15768535 : ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
388 : Datum a, GinNullCategory categorya,
4475 tgl 389 ECB : Datum b, GinNullCategory categoryb)
6031 bruce 390 : {
391 : /* if not of same null category, sort by that first */
4475 tgl 392 GIC 15768535 : if (categorya != categoryb)
4475 tgl 393 CBC 311907 : return (categorya < categoryb) ? -1 : 1;
4475 tgl 394 ECB :
395 : /* all null items in same category are equal */
4475 tgl 396 GIC 15456628 : if (categorya != GIN_CAT_NORM_KEY)
397 4148 : return 0;
398 :
399 : /* both not null, so safe to call the compareFn */
4380 400 15452480 : return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
2118 401 15452480 : ginstate->supportCollation[attnum - 1],
4380 tgl 402 ECB : a, b));
403 : }
404 :
405 : /*
406 : * Compare two keys of possibly different index columns
4475 407 : */
5385 408 : int
4475 tgl 409 GIC 16035167 : ginCompareAttEntries(GinState *ginstate,
4475 tgl 410 ECB : OffsetNumber attnuma, Datum a, GinNullCategory categorya,
411 : OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
412 : {
413 : /* attribute number is the first sort key */
4475 tgl 414 GIC 16035167 : if (attnuma != attnumb)
415 268890 : return (attnuma < attnumb) ? -1 : 1;
416 :
417 15766277 : return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb);
418 : }
419 :
420 :
421 : /*
422 : * Support for sorting key datums in ginExtractEntries
423 : *
424 : * Note: we only have to worry about null and not-null keys here;
425 : * ginExtractEntries never generates more than one placeholder null,
426 : * so it doesn't have to sort those.
427 : */
428 : typedef struct
429 : {
430 : Datum datum;
431 : bool isnull;
432 : } keyEntryData;
433 :
434 : typedef struct
6030 tgl 435 ECB : {
436 : FmgrInfo *cmpDatumFunc;
4380 437 : Oid collation;
4475 438 : bool haveDups;
439 : } cmpEntriesArg;
440 :
441 : static int
4475 tgl 442 CBC 928437 : cmpEntries(const void *a, const void *b, void *arg)
443 : {
4475 tgl 444 GBC 928437 : const keyEntryData *aa = (const keyEntryData *) a;
445 928437 : const keyEntryData *bb = (const keyEntryData *) b;
4475 tgl 446 GIC 928437 : cmpEntriesArg *data = (cmpEntriesArg *) arg;
4475 tgl 447 EUB : int res;
448 :
4475 tgl 449 CBC 928437 : if (aa->isnull)
4475 tgl 450 EUB : {
4475 tgl 451 UIC 0 : if (bb->isnull)
4475 tgl 452 LBC 0 : res = 0; /* NULL "=" NULL */
453 : else
454 0 : res = 1; /* NULL ">" not-NULL */
455 : }
4475 tgl 456 GIC 928437 : else if (bb->isnull)
4475 tgl 457 UIC 0 : res = -1; /* not-NULL "<" NULL */
458 : else
4380 tgl 459 GIC 928437 : res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
460 : data->collation,
4380 tgl 461 CBC 928437 : aa->datum, bb->datum));
4475 tgl 462 ECB :
463 : /*
4382 bruce 464 : * Detect if we have any duplicates. If there are equal keys, qsort must
465 : * compare them at some point, else it wouldn't know whether one should go
466 : * before or after the other.
467 : */
6031 bruce 468 GIC 928437 : if (res == 0)
4475 tgl 469 15502 : data->haveDups = true;
470 :
6186 teodor 471 928437 : return res;
472 : }
473 :
474 :
4475 tgl 475 ECB : /*
476 : * Extract the index key values from an indexable item
477 : *
478 : * The resulting key values are sorted, and any duplicates are removed.
479 : * This avoids generating redundant index entries.
480 : */
481 : Datum *
4475 tgl 482 GIC 642573 : ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
483 : Datum value, bool isNull,
484 : int32 *nentries, GinNullCategory **categories)
485 : {
486 : Datum *entries;
4475 tgl 487 ECB : bool *nullFlags;
488 : int32 i;
489 :
490 : /*
491 : * We don't call the extractValueFn on a null item. Instead generate a
492 : * placeholder.
493 : */
4475 tgl 494 CBC 642573 : if (isNull)
495 : {
4475 tgl 496 GIC 3311 : *nentries = 1;
497 3311 : entries = (Datum *) palloc(sizeof(Datum));
4475 tgl 498 CBC 3311 : entries[0] = (Datum) 0;
4475 tgl 499 GIC 3311 : *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
4475 tgl 500 CBC 3311 : (*categories)[0] = GIN_CAT_NULL_ITEM;
501 3311 : return entries;
502 : }
503 :
504 : /* OK, call the opclass's extractValueFn */
4475 tgl 505 GIC 639262 : nullFlags = NULL; /* in case extractValue doesn't set it */
506 : entries = (Datum *)
4370 507 1278524 : DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
2118 508 639262 : ginstate->supportCollation[attnum - 1],
4370 tgl 509 ECB : value,
510 : PointerGetDatum(nentries),
511 : PointerGetDatum(&nullFlags)));
4475 512 :
513 : /*
514 : * Generate a placeholder if the item contained no keys.
515 : */
4475 tgl 516 CBC 639262 : if (entries == NULL || *nentries <= 0)
517 : {
4475 tgl 518 GIC 888 : *nentries = 1;
519 888 : entries = (Datum *) palloc(sizeof(Datum));
520 888 : entries[0] = (Datum) 0;
521 888 : *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
522 888 : (*categories)[0] = GIN_CAT_EMPTY_ITEM;
4475 tgl 523 CBC 888 : return entries;
4475 tgl 524 ECB : }
525 :
526 : /*
527 : * If the extractValueFn didn't create a nullFlags array, create one,
528 : * assuming that everything's non-null.
529 : */
4475 tgl 530 GIC 638374 : if (nullFlags == NULL)
531 110371 : nullFlags = (bool *) palloc0(*nentries * sizeof(bool));
532 :
4475 tgl 533 ECB : /*
534 : * If there's more than one key, sort and unique-ify.
535 : *
536 : * XXX Using qsort here is notationally painful, and the overhead is
537 : * pretty bad too. For small numbers of keys it'd likely be better to use
4382 bruce 538 : * a simple insertion sort.
4475 tgl 539 : */
4475 tgl 540 GIC 638374 : if (*nentries > 1)
6031 bruce 541 ECB : {
4475 tgl 542 : keyEntryData *keydata;
543 : cmpEntriesArg arg;
544 :
4475 tgl 545 CBC 259268 : keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData));
546 1179622 : for (i = 0; i < *nentries; i++)
4475 tgl 547 ECB : {
4475 tgl 548 CBC 920354 : keydata[i].datum = entries[i];
4475 tgl 549 GIC 920354 : keydata[i].isnull = nullFlags[i];
550 : }
4475 tgl 551 ECB :
4475 tgl 552 GIC 259268 : arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
4370 553 259268 : arg.collation = ginstate->supportCollation[attnum - 1];
4475 554 259268 : arg.haveDups = false;
555 259268 : qsort_arg(keydata, *nentries, sizeof(keyEntryData),
556 : cmpEntries, &arg);
6186 teodor 557 ECB :
4475 tgl 558 CBC 259268 : if (arg.haveDups)
4475 tgl 559 ECB : {
560 : /* there are duplicates, must get rid of 'em */
561 : int32 j;
562 :
4475 tgl 563 CBC 7514 : entries[0] = keydata[0].datum;
564 7514 : nullFlags[0] = keydata[0].isnull;
565 7514 : j = 1;
4475 tgl 566 GIC 33965 : for (i = 1; i < *nentries; i++)
567 : {
4382 bruce 568 CBC 26451 : if (cmpEntries(&keydata[i - 1], &keydata[i], &arg) != 0)
569 : {
4475 tgl 570 GIC 18745 : entries[j] = keydata[i].datum;
571 18745 : nullFlags[j] = keydata[i].isnull;
572 18745 : j++;
4475 tgl 573 ECB : }
574 : }
4475 tgl 575 CBC 7514 : *nentries = j;
4475 tgl 576 ECB : }
577 : else
578 : {
579 : /* easy, no duplicates */
4475 tgl 580 CBC 1138143 : for (i = 0; i < *nentries; i++)
581 : {
4475 tgl 582 GIC 886389 : entries[i] = keydata[i].datum;
583 886389 : nullFlags[i] = keydata[i].isnull;
584 : }
585 : }
6186 teodor 586 ECB :
4475 tgl 587 CBC 259268 : pfree(keydata);
6186 teodor 588 ECB : }
589 :
1930 peter_e 590 : /*
591 : * Create GinNullCategory representation from nullFlags.
592 : */
1930 peter_e 593 GIC 638374 : *categories = (GinNullCategory *) palloc0(*nentries * sizeof(GinNullCategory));
1930 peter_e 594 CBC 1930128 : for (i = 0; i < *nentries; i++)
1930 peter_e 595 GIC 1291754 : (*categories)[i] = (nullFlags[i] ? GIN_CAT_NULL_KEY : GIN_CAT_NORM_KEY);
596 :
6186 teodor 597 638374 : return entries;
598 : }
599 :
600 : bytea *
2639 tgl 601 253 : ginoptions(Datum reloptions, bool validate)
6125 bruce 602 ECB : {
603 : static const relopt_parse_elt tab[] = {
604 : {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
605 : {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
606 : pendingListCleanupSize)}
607 : };
608 :
1251 michael 609 GIC 253 : return (bytea *) build_reloptions(reloptions, validate,
610 : RELOPT_KIND_GIN,
611 : sizeof(GinOptions),
612 : tab, lengthof(tab));
613 : }
614 :
4557 tgl 615 ECB : /*
616 : * Fetch index's statistical data into *stats
617 : *
618 : * Note: in the result, nPendingPages can be trusted to be up-to-date,
619 : * as can ginVersion; but the other fields are as of the last VACUUM.
620 : */
621 : void
4557 tgl 622 CBC 1088 : ginGetStats(Relation index, GinStatsData *stats)
4557 tgl 623 ECB : {
4382 bruce 624 : Buffer metabuffer;
625 : Page metapage;
626 : GinMetaPageData *metadata;
4557 tgl 627 :
4557 tgl 628 CBC 1088 : metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
629 1088 : LockBuffer(metabuffer, GIN_SHARE);
2545 kgrittn 630 1088 : metapage = BufferGetPage(metabuffer);
4557 tgl 631 1088 : metadata = GinPageGetMeta(metapage);
632 :
633 1088 : stats->nPendingPages = metadata->nPendingPages;
634 1088 : stats->nTotalPages = metadata->nTotalPages;
4557 tgl 635 GIC 1088 : stats->nEntryPages = metadata->nEntryPages;
636 1088 : stats->nDataPages = metadata->nDataPages;
637 1088 : stats->nEntries = metadata->nEntries;
4475 638 1088 : stats->ginVersion = metadata->ginVersion;
639 :
4557 640 1088 : UnlockReleaseBuffer(metabuffer);
641 1088 : }
4557 tgl 642 ECB :
643 : /*
644 : * Write the given statistics to the index's metapage
645 : *
646 : * Note: nPendingPages and ginVersion are *not* copied over
647 : */
648 : void
1467 heikki.linnakangas 649 CBC 179 : ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
4557 tgl 650 ECB : {
4382 bruce 651 : Buffer metabuffer;
652 : Page metapage;
653 : GinMetaPageData *metadata;
654 :
4557 tgl 655 CBC 179 : metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
656 179 : LockBuffer(metabuffer, GIN_EXCLUSIVE);
2545 kgrittn 657 179 : metapage = BufferGetPage(metabuffer);
4557 tgl 658 179 : metadata = GinPageGetMeta(metapage);
659 :
4557 tgl 660 GIC 179 : START_CRIT_SECTION();
661 :
662 179 : metadata->nTotalPages = stats->nTotalPages;
663 179 : metadata->nEntryPages = stats->nEntryPages;
664 179 : metadata->nDataPages = stats->nDataPages;
665 179 : metadata->nEntries = stats->nEntries;
666 :
1984 tgl 667 ECB : /*
668 : * Set pd_lower just past the end of the metadata. This is essential,
669 : * because without doing so, metadata will be lost if xlog.c compresses
670 : * the page. (We must do this here because pre-v11 versions of PG did not
671 : * set the metapage's pd_lower correctly, so a pg_upgraded index might
672 : * contain the wrong value.)
673 : */
1984 tgl 674 GIC 179 : ((PageHeader) metapage)->pd_lower =
675 179 : ((char *) metadata + sizeof(GinMetaPageData)) - (char *) metapage;
676 :
4557 tgl 677 CBC 179 : MarkBufferDirty(metabuffer);
4557 tgl 678 ECB :
1467 heikki.linnakangas 679 CBC 179 : if (RelationNeedsWAL(index) && !is_build)
4557 tgl 680 ECB : {
681 : XLogRecPtr recptr;
4382 bruce 682 : ginxlogUpdateMeta data;
4557 tgl 683 :
277 rhaas 684 GNC 24 : data.locator = index->rd_locator;
4557 tgl 685 GIC 24 : data.ntuples = 0;
4557 tgl 686 CBC 24 : data.newRightlink = data.prevTail = InvalidBlockNumber;
687 24 : memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
688 :
3062 heikki.linnakangas 689 GIC 24 : XLogBeginInsert();
3062 heikki.linnakangas 690 CBC 24 : XLogRegisterData((char *) &data, sizeof(ginxlogUpdateMeta));
1984 tgl 691 GIC 24 : XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
4557 tgl 692 ECB :
3062 heikki.linnakangas 693 CBC 24 : recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE);
4557 tgl 694 GIC 24 : PageSetLSN(metapage, recptr);
695 : }
696 :
697 179 : UnlockReleaseBuffer(metabuffer);
698 :
699 179 : END_CRIT_SECTION();
700 179 : }
|