Line data Source code
1 : /*--------------------------------------------------------------------------
2 : * gin_private.h
3 : * header file for postgres inverted index access method implementation.
4 : *
5 : * Copyright (c) 2006-2026, PostgreSQL Global Development Group
6 : *
7 : * src/include/access/gin_private.h
8 : *--------------------------------------------------------------------------
9 : */
10 : #ifndef GIN_PRIVATE_H
11 : #define GIN_PRIVATE_H
12 :
13 : #include "access/amapi.h"
14 : #include "access/gin.h"
15 : #include "access/ginblock.h"
16 : #include "access/htup_details.h"
17 : #include "access/itup.h"
18 : #include "common/int.h"
19 : #include "catalog/pg_am_d.h"
20 : #include "fmgr.h"
21 : #include "lib/rbtree.h"
22 : #include "storage/bufmgr.h"
23 :
24 : /*
25 : * Storage type for GIN's reloptions
26 : */
27 : typedef struct GinOptions
28 : {
29 : int32 vl_len_; /* varlena header (do not touch directly!) */
30 : bool useFastUpdate; /* use fast updates? */
31 : int pendingListCleanupSize; /* maximum size of pending list */
32 : } GinOptions;
33 :
34 : #define GIN_DEFAULT_USE_FASTUPDATE true
35 : #define GinGetUseFastUpdate(relation) \
36 : (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
37 : relation->rd_rel->relam == GIN_AM_OID), \
38 : (relation)->rd_options ? \
39 : ((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)
40 : #define GinGetPendingListCleanupSize(relation) \
41 : (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
42 : relation->rd_rel->relam == GIN_AM_OID), \
43 : (relation)->rd_options && \
44 : ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize != -1 ? \
45 : ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize : \
46 : gin_pending_list_limit)
47 :
48 :
49 : /* Macros for buffer lock/unlock operations */
50 : #define GIN_UNLOCK BUFFER_LOCK_UNLOCK
51 : #define GIN_SHARE BUFFER_LOCK_SHARE
52 : #define GIN_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE
53 :
54 :
55 : /*
56 : * GinState: working data structure describing the index being worked on
57 : */
58 : typedef struct GinState
59 : {
60 : Relation index;
61 : bool oneCol; /* true if single-column index */
62 :
63 : /*
64 : * origTupdesc is the nominal tuple descriptor of the index, ie, the i'th
65 : * attribute shows the key type (not the input data type!) of the i'th
66 : * index column. In a single-column index this describes the actual leaf
67 : * index tuples. In a multi-column index, the actual leaf tuples contain
68 : * a smallint column number followed by a key datum of the appropriate
69 : * type for that column. We set up tupdesc[i] to describe the actual
70 : * rowtype of the index tuples for the i'th column, ie, (int2, keytype).
71 : * Note that in any case, leaf tuples contain more data than is known to
72 : * the TupleDesc; see access/gin/README for details.
73 : */
74 : TupleDesc origTupdesc;
75 : TupleDesc tupdesc[INDEX_MAX_KEYS];
76 :
77 : /*
78 : * Per-index-column opclass support functions
79 : */
80 : FmgrInfo compareFn[INDEX_MAX_KEYS];
81 : FmgrInfo extractValueFn[INDEX_MAX_KEYS];
82 : FmgrInfo extractQueryFn[INDEX_MAX_KEYS];
83 : FmgrInfo consistentFn[INDEX_MAX_KEYS];
84 : FmgrInfo triConsistentFn[INDEX_MAX_KEYS];
85 : FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */
86 : /* canPartialMatch[i] is true if comparePartialFn[i] is valid */
87 : bool canPartialMatch[INDEX_MAX_KEYS];
88 : /* Collations to pass to the support functions */
89 : Oid supportCollation[INDEX_MAX_KEYS];
90 : } GinState;
91 :
92 :
93 : /* ginutil.c */
94 : extern bytea *ginoptions(Datum reloptions, bool validate);
95 : extern void initGinState(GinState *state, Relation index);
96 : extern Buffer GinNewBuffer(Relation index);
97 : extern void GinInitBuffer(Buffer b, uint32 f);
98 : extern void GinInitPage(Page page, uint32 f, Size pageSize);
99 : extern void GinInitMetabuffer(Buffer b);
100 : extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
101 : Datum value, bool isNull,
102 : int32 *nentries, GinNullCategory **categories);
103 :
104 : extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
105 : extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
106 : GinNullCategory *category);
107 : extern char *ginbuildphasename(int64 phasenum);
108 :
109 : /* gininsert.c */
110 : extern IndexBuildResult *ginbuild(Relation heap, Relation index,
111 : struct IndexInfo *indexInfo);
112 : extern void ginbuildempty(Relation index);
113 : extern bool gininsert(Relation index, Datum *values, bool *isnull,
114 : ItemPointer ht_ctid, Relation heapRel,
115 : IndexUniqueCheck checkUnique,
116 : bool indexUnchanged,
117 : struct IndexInfo *indexInfo);
118 : extern void ginEntryInsert(GinState *ginstate,
119 : OffsetNumber attnum, Datum key, GinNullCategory category,
120 : ItemPointerData *items, uint32 nitem,
121 : GinStatsData *buildStats);
122 :
123 : /* ginbtree.c */
124 :
125 : typedef struct GinBtreeStack
126 : {
127 : BlockNumber blkno;
128 : Buffer buffer;
129 : OffsetNumber off;
130 : ItemPointerData iptr;
131 : /* predictNumber contains predicted number of pages on current level */
132 : uint32 predictNumber;
133 : struct GinBtreeStack *parent;
134 : } GinBtreeStack;
135 :
136 : typedef struct GinBtreeData *GinBtree;
137 :
138 : /* Return codes for GinBtreeData.beginPlaceToPage method */
139 : typedef enum
140 : {
141 : GPTP_NO_WORK,
142 : GPTP_INSERT,
143 : GPTP_SPLIT,
144 : } GinPlaceToPageRC;
145 :
146 : typedef struct GinBtreeData
147 : {
148 : /* search methods */
149 : BlockNumber (*findChildPage) (GinBtree, GinBtreeStack *);
150 : BlockNumber (*getLeftMostChild) (GinBtree, Page);
151 : bool (*isMoveRight) (GinBtree, Page);
152 : bool (*findItem) (GinBtree, GinBtreeStack *);
153 :
154 : /* insert methods */
155 : OffsetNumber (*findChildPtr) (GinBtree, Page, BlockNumber, OffsetNumber);
156 : GinPlaceToPageRC (*beginPlaceToPage) (GinBtree, Buffer, GinBtreeStack *, void *, BlockNumber, void **, Page *, Page *);
157 : void (*execPlaceToPage) (GinBtree, Buffer, GinBtreeStack *, void *, BlockNumber, void *);
158 : void *(*prepareDownlink) (GinBtree, Buffer);
159 : void (*fillRoot) (GinBtree, Page, BlockNumber, Page, BlockNumber, Page);
160 :
161 : bool isData;
162 :
163 : Relation index;
164 : BlockNumber rootBlkno;
165 : GinState *ginstate; /* not valid in a data scan */
166 : bool fullScan;
167 : bool isBuild;
168 :
169 : /* Search key for Entry tree */
170 : OffsetNumber entryAttnum;
171 : Datum entryKey;
172 : GinNullCategory entryCategory;
173 :
174 : /* Search key for data tree (posting tree) */
175 : ItemPointerData itemptr;
176 : } GinBtreeData;
177 :
178 : /* This represents a tuple to be inserted to entry tree. */
179 : typedef struct
180 : {
181 : IndexTuple entry; /* tuple to insert */
182 : bool isDelete; /* delete old tuple at same offset? */
183 : } GinBtreeEntryInsertData;
184 :
185 : /*
186 : * This represents an itempointer, or many itempointers, to be inserted to
187 : * a data (posting tree) leaf page
188 : */
189 : typedef struct
190 : {
191 : ItemPointerData *items;
192 : uint32 nitem;
193 : uint32 curitem;
194 : } GinBtreeDataLeafInsertData;
195 :
196 : /*
197 : * For internal data (posting tree) pages, the insertion payload is a
198 : * PostingItem
199 : */
200 :
201 : extern GinBtreeStack *ginFindLeafPage(GinBtree btree, bool searchMode,
202 : bool rootConflictCheck);
203 : extern Buffer ginStepRight(Buffer buffer, Relation index, int lockmode);
204 : extern void freeGinBtreeStack(GinBtreeStack *stack);
205 : extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack,
206 : void *insertdata, GinStatsData *buildStats);
207 :
208 : /* ginentrypage.c */
209 : extern IndexTuple GinFormTuple(GinState *ginstate,
210 : OffsetNumber attnum, Datum key, GinNullCategory category,
211 : Pointer data, Size dataSize, int nipd, bool errorTooBig);
212 : extern void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
213 : Datum key, GinNullCategory category,
214 : GinState *ginstate);
215 : extern void ginEntryFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage);
216 : extern ItemPointer ginReadTuple(GinState *ginstate, OffsetNumber attnum,
217 : IndexTuple itup, int *nitems);
218 :
219 : /* gindatapage.c */
220 : extern ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast);
221 : extern int GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm);
222 : extern BlockNumber createPostingTree(Relation index,
223 : ItemPointerData *items, uint32 nitems,
224 : GinStatsData *buildStats, Buffer entrybuffer);
225 : extern void GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset);
226 : extern void GinPageDeletePostingItem(Page page, OffsetNumber offset);
227 : extern void ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
228 : ItemPointerData *items, uint32 nitem,
229 : GinStatsData *buildStats);
230 : extern GinBtreeStack *ginScanBeginPostingTree(GinBtree btree, Relation index, BlockNumber rootBlkno);
231 : extern void ginDataFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage);
232 :
233 : /*
234 : * This is declared in ginvacuum.c, but is passed between ginVacuumItemPointers
235 : * and ginVacuumPostingTreeLeaf and as an opaque struct, so we need a forward
236 : * declaration for it.
237 : */
238 : typedef struct GinVacuumState GinVacuumState;
239 :
240 : extern void ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer,
241 : GinVacuumState *gvs);
242 :
243 : /* ginscan.c */
244 :
245 : /*
246 : * GinScanKeyData describes a single GIN index qualifier expression.
247 : *
248 : * From each qual expression, we extract one or more specific index search
249 : * conditions, which are represented by GinScanEntryData. It's quite
250 : * possible for identical search conditions to be requested by more than
251 : * one qual expression, in which case we merge such conditions to have just
252 : * one unique GinScanEntry --- this is particularly important for efficiency
253 : * when dealing with full-index-scan entries. So there can be multiple
254 : * GinScanKeyData.scanEntry pointers to the same GinScanEntryData.
255 : *
256 : * In each GinScanKeyData, nentries is the true number of entries, while
257 : * nuserentries is the number that extractQueryFn returned (which is what
258 : * we report to consistentFn). The "user" entries must come first.
259 : */
260 : typedef struct GinScanKeyData *GinScanKey;
261 :
262 : typedef struct GinScanEntryData *GinScanEntry;
263 :
264 : typedef struct GinScanKeyData
265 : {
266 : /* Real number of entries in scanEntry[] (always > 0) */
267 : uint32 nentries;
268 : /* Number of entries that extractQueryFn and consistentFn know about */
269 : uint32 nuserentries;
270 :
271 : /* array of GinScanEntry pointers, one per extracted search condition */
272 : GinScanEntry *scanEntry;
273 :
274 : /*
275 : * At least one of the entries in requiredEntries must be present for a
276 : * tuple to match the overall qual.
277 : *
278 : * additionalEntries contains entries that are needed by the consistent
279 : * function to decide if an item matches, but are not sufficient to
280 : * satisfy the qual without entries from requiredEntries.
281 : */
282 : GinScanEntry *requiredEntries;
283 : int nrequired;
284 : GinScanEntry *additionalEntries;
285 : int nadditional;
286 :
287 : /* array of check flags, reported to consistentFn */
288 : GinTernaryValue *entryRes;
289 : bool (*boolConsistentFn) (GinScanKey key);
290 : GinTernaryValue (*triConsistentFn) (GinScanKey key);
291 : FmgrInfo *consistentFmgrInfo;
292 : FmgrInfo *triConsistentFmgrInfo;
293 : Oid collation;
294 :
295 : /* other data needed for calling consistentFn */
296 : Datum query;
297 : /* NB: these three arrays have only nuserentries elements! */
298 : Datum *queryValues;
299 : GinNullCategory *queryCategories;
300 : Pointer *extra_data;
301 : StrategyNumber strategy;
302 : int32 searchMode;
303 : OffsetNumber attnum;
304 :
305 : /*
306 : * An excludeOnly scan key is not able to enumerate all matching tuples.
307 : * That is, to be semantically correct on its own, it would need to have a
308 : * GIN_CAT_EMPTY_QUERY scanEntry, but it doesn't. Such a key can still be
309 : * used to filter tuples returned by other scan keys, so we will get the
310 : * right answers as long as there's at least one non-excludeOnly scan key
311 : * for each index attribute considered by the search. For efficiency
312 : * reasons we don't want to have unnecessary GIN_CAT_EMPTY_QUERY entries,
313 : * so we will convert an excludeOnly scan key to non-excludeOnly (by
314 : * adding a GIN_CAT_EMPTY_QUERY scanEntry) only if there are no other
315 : * non-excludeOnly scan keys.
316 : */
317 : bool excludeOnly;
318 :
319 : /*
320 : * Match status data. curItem is the TID most recently tested (could be a
321 : * lossy-page pointer). curItemMatches is true if it passes the
322 : * consistentFn test; if so, recheckCurItem is the recheck flag.
323 : * isFinished means that all the input entry streams are finished, so this
324 : * key cannot succeed for any later TIDs.
325 : */
326 : ItemPointerData curItem;
327 : bool curItemMatches;
328 : bool recheckCurItem;
329 : bool isFinished;
330 : } GinScanKeyData;
331 :
332 : typedef struct GinScanEntryData
333 : {
334 : /* query key and other information from extractQueryFn */
335 : Datum queryKey;
336 : GinNullCategory queryCategory;
337 : bool isPartialMatch;
338 : Pointer extra_data;
339 : StrategyNumber strategy;
340 : int32 searchMode;
341 : OffsetNumber attnum;
342 :
343 : /* Current page in posting tree */
344 : Buffer buffer;
345 :
346 : /* current ItemPointer to heap */
347 : ItemPointerData curItem;
348 :
349 : /* for a partial-match or full-scan query, we accumulate all TIDs here */
350 : TIDBitmap *matchBitmap;
351 : TBMPrivateIterator *matchIterator;
352 :
353 : /*
354 : * If blockno is InvalidBlockNumber, all of the other fields in the
355 : * matchResult are meaningless.
356 : */
357 : TBMIterateResult matchResult;
358 : OffsetNumber matchOffsets[TBM_MAX_TUPLES_PER_PAGE];
359 : int matchNtuples;
360 :
361 : /* used for Posting list and one page in Posting tree */
362 : ItemPointerData *list;
363 : int nlist;
364 : OffsetNumber offset;
365 :
366 : bool isFinished;
367 : bool reduceResult;
368 : uint32 predictNumberResult;
369 : GinBtreeData btree;
370 : } GinScanEntryData;
371 :
372 : typedef struct GinScanOpaqueData
373 : {
374 : MemoryContext tempCtx;
375 : GinState ginstate;
376 :
377 : GinScanKey keys; /* one per scan qualifier expr */
378 : uint32 nkeys;
379 :
380 : GinScanEntry *entries; /* one per index search condition */
381 : uint32 totalentries;
382 : uint32 allocentries; /* allocated length of entries[] */
383 :
384 : MemoryContext keyCtx; /* used to hold key and entry data */
385 :
386 : bool isVoidRes; /* true if query is unsatisfiable */
387 : } GinScanOpaqueData;
388 :
389 : typedef GinScanOpaqueData *GinScanOpaque;
390 :
391 : extern IndexScanDesc ginbeginscan(Relation rel, int nkeys, int norderbys);
392 : extern void ginendscan(IndexScanDesc scan);
393 : extern void ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
394 : ScanKey orderbys, int norderbys);
395 : extern void ginNewScanKey(IndexScanDesc scan);
396 : extern void ginFreeScanKeys(GinScanOpaque so);
397 :
398 : /* ginget.c */
399 : extern int64 gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
400 :
401 : /* ginlogic.c */
402 : extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key);
403 :
404 : /* ginvacuum.c */
405 : extern IndexBulkDeleteResult *ginbulkdelete(IndexVacuumInfo *info,
406 : IndexBulkDeleteResult *stats,
407 : IndexBulkDeleteCallback callback,
408 : void *callback_state);
409 : extern IndexBulkDeleteResult *ginvacuumcleanup(IndexVacuumInfo *info,
410 : IndexBulkDeleteResult *stats);
411 : extern ItemPointer ginVacuumItemPointers(GinVacuumState *gvs,
412 : ItemPointerData *items, int nitem, int *nremaining);
413 :
414 : /* ginvalidate.c */
415 : extern bool ginvalidate(Oid opclassoid);
416 : extern void ginadjustmembers(Oid opfamilyoid,
417 : Oid opclassoid,
418 : List *operators,
419 : List *functions);
420 :
421 : /* ginbulk.c */
422 : typedef struct GinEntryAccumulator
423 : {
424 : RBTNode rbtnode;
425 : Datum key;
426 : GinNullCategory category;
427 : OffsetNumber attnum;
428 : bool shouldSort;
429 : ItemPointerData *list;
430 : uint32 maxcount; /* allocated size of list[] */
431 : uint32 count; /* current number of list[] entries */
432 : } GinEntryAccumulator;
433 :
434 : typedef struct
435 : {
436 : GinState *ginstate;
437 : Size allocatedMemory;
438 : GinEntryAccumulator *entryallocator;
439 : uint32 eas_used;
440 : RBTree *tree;
441 : RBTreeIterator tree_walk;
442 : } BuildAccumulator;
443 :
444 : extern void ginInitBA(BuildAccumulator *accum);
445 : extern void ginInsertBAEntries(BuildAccumulator *accum,
446 : ItemPointer heapptr, OffsetNumber attnum,
447 : Datum *entries, GinNullCategory *categories,
448 : int32 nentries);
449 : extern void ginBeginBAScan(BuildAccumulator *accum);
450 : extern ItemPointerData *ginGetBAEntry(BuildAccumulator *accum,
451 : OffsetNumber *attnum, Datum *key, GinNullCategory *category,
452 : uint32 *n);
453 :
454 : /* ginfast.c */
455 :
456 : typedef struct GinTupleCollector
457 : {
458 : IndexTuple *tuples;
459 : uint32 ntuples;
460 : uint32 lentuples;
461 : uint32 sumsize;
462 : } GinTupleCollector;
463 :
464 : extern void ginHeapTupleFastInsert(GinState *ginstate,
465 : GinTupleCollector *collector);
466 : extern void ginHeapTupleFastCollect(GinState *ginstate,
467 : GinTupleCollector *collector,
468 : OffsetNumber attnum, Datum value, bool isNull,
469 : ItemPointer ht_ctid);
470 : extern void ginInsertCleanup(GinState *ginstate, bool full_clean,
471 : bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats);
472 :
473 : /* ginpostinglist.c */
474 :
475 : extern GinPostingList *ginCompressPostingList(const ItemPointerData *ipd, int nipd,
476 : int maxsize, int *nwritten);
477 : extern int ginPostingListDecodeAllSegmentsToTbm(GinPostingList *ptr, int len, TIDBitmap *tbm);
478 :
479 : extern ItemPointer ginPostingListDecodeAllSegments(GinPostingList *segment, int len,
480 : int *ndecoded_out);
481 : extern ItemPointer ginPostingListDecode(GinPostingList *plist, int *ndecoded_out);
482 : extern ItemPointer ginMergeItemPointers(ItemPointerData *a, uint32 na,
483 : ItemPointerData *b, uint32 nb,
484 : int *nmerged);
485 :
486 : /*
487 : * Merging the results of several gin scans compares item pointers a lot,
488 : * so we want this to be inlined.
489 : */
490 : static inline int
491 12705100 : ginCompareItemPointers(ItemPointer a, ItemPointer b)
492 : {
493 12705100 : uint64 ia = (uint64) GinItemPointerGetBlockNumber(a) << 32 | GinItemPointerGetOffsetNumber(a);
494 12705100 : uint64 ib = (uint64) GinItemPointerGetBlockNumber(b) << 32 | GinItemPointerGetOffsetNumber(b);
495 :
496 12705100 : return pg_cmp_u64(ia, ib);
497 : }
498 :
499 : /*
500 : * Compare two keys of the same index column
501 : */
502 : static inline int
503 39332386 : ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
504 : Datum a, GinNullCategory categorya,
505 : Datum b, GinNullCategory categoryb)
506 : {
507 : /* if not of same null category, sort by that first */
508 39332386 : if (categorya != categoryb)
509 623816 : return (categorya < categoryb) ? -1 : 1;
510 :
511 : /* all null items in same category are equal */
512 38708570 : if (categorya != GIN_CAT_NORM_KEY)
513 8296 : return 0;
514 :
515 : /* both not null, so safe to call the compareFn */
516 38700274 : return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
517 38700274 : ginstate->supportCollation[attnum - 1],
518 : a, b));
519 : }
520 :
521 : /*
522 : * Compare two keys of possibly different index columns
523 : */
524 : static inline int
525 39816002 : ginCompareAttEntries(GinState *ginstate,
526 : OffsetNumber attnuma, Datum a, GinNullCategory categorya,
527 : OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
528 : {
529 : /* attribute number is the first sort key */
530 39816002 : if (attnuma != attnumb)
531 537806 : return (attnuma < attnumb) ? -1 : 1;
532 :
533 39278196 : return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb);
534 :
535 : }
536 :
537 : extern int ginTraverseLock(Buffer buffer, bool searchMode);
538 :
539 : #endif /* GIN_PRIVATE_H */
|