/src/postgres/src/include/access/gist.h
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * gist.h |
4 | | * The public API for GiST indexes. This API is exposed to |
5 | | * individuals implementing GiST indexes, so backward-incompatible |
6 | | * changes should be made with care. |
7 | | * |
8 | | * |
9 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
10 | | * Portions Copyright (c) 1994, Regents of the University of California |
11 | | * |
12 | | * src/include/access/gist.h |
13 | | * |
14 | | *------------------------------------------------------------------------- |
15 | | */ |
16 | | #ifndef GIST_H |
17 | | #define GIST_H |
18 | | |
19 | | #include "access/itup.h" |
20 | | #include "access/stratnum.h" |
21 | | #include "access/transam.h" |
22 | | #include "access/xlog.h" |
23 | | #include "access/xlogdefs.h" |
24 | | #include "nodes/primnodes.h" |
25 | | #include "storage/block.h" |
26 | | #include "storage/bufpage.h" |
27 | | #include "utils/relcache.h" |
28 | | |
29 | | /* |
30 | | * amproc indexes for GiST indexes. |
31 | | */ |
32 | 0 | #define GIST_CONSISTENT_PROC 1 |
33 | 0 | #define GIST_UNION_PROC 2 |
34 | 0 | #define GIST_COMPRESS_PROC 3 |
35 | 0 | #define GIST_DECOMPRESS_PROC 4 |
36 | 0 | #define GIST_PENALTY_PROC 5 |
37 | 0 | #define GIST_PICKSPLIT_PROC 6 |
38 | 0 | #define GIST_EQUAL_PROC 7 |
39 | 0 | #define GIST_DISTANCE_PROC 8 |
40 | 0 | #define GIST_FETCH_PROC 9 |
41 | 0 | #define GIST_OPTIONS_PROC 10 |
42 | 0 | #define GIST_SORTSUPPORT_PROC 11 |
43 | 0 | #define GIST_TRANSLATE_CMPTYPE_PROC 12 |
44 | 0 | #define GISTNProcs 12 |
45 | | |
46 | | /* |
47 | | * Page opaque data in a GiST index page. |
48 | | */ |
49 | 0 | #define F_LEAF (1 << 0) /* leaf page */ |
50 | 0 | #define F_DELETED (1 << 1) /* the page has been deleted */ |
51 | 0 | #define F_TUPLES_DELETED (1 << 2) /* some tuples on the page were |
52 | | * deleted */ |
53 | 0 | #define F_FOLLOW_RIGHT (1 << 3) /* page to the right has no downlink */ |
54 | 0 | #define F_HAS_GARBAGE (1 << 4) /* some tuples on the page are dead, |
55 | | * but not deleted yet */ |
56 | | |
57 | | /* |
58 | | * NSN (node sequence number) is a special-purpose LSN which is stored on each |
59 | | * index page in GISTPageOpaqueData and updated only during page splits. By |
60 | | * recording the parent's LSN in GISTSearchItem.parentlsn, it is possible to |
61 | | * detect concurrent child page splits by checking if parentlsn < child's NSN, |
62 | | * and handle them properly. The child page's LSN is insufficient for this |
63 | | * purpose since it is updated for every page change. |
64 | | */ |
65 | | typedef XLogRecPtr GistNSN; |
66 | | |
67 | | /* |
68 | | * A fake LSN / NSN value used during index builds. Must be smaller than any |
69 | | * real or fake (unlogged) LSN generated after the index build completes so |
70 | | * that all splits are considered complete. |
71 | | */ |
72 | 0 | #define GistBuildLSN ((XLogRecPtr) 1) |
73 | | |
74 | | /* |
75 | | * For on-disk compatibility with pre-9.3 servers, NSN is stored as two |
76 | | * 32-bit fields on disk, same as LSNs. |
77 | | */ |
78 | | typedef PageXLogRecPtr PageGistNSN; |
79 | | |
80 | | typedef struct GISTPageOpaqueData |
81 | | { |
82 | | PageGistNSN nsn; /* this value must change on page split */ |
83 | | BlockNumber rightlink; /* next page if any */ |
84 | | uint16 flags; /* see bit definitions above */ |
85 | | uint16 gist_page_id; /* for identification of GiST indexes */ |
86 | | } GISTPageOpaqueData; |
87 | | |
88 | | typedef GISTPageOpaqueData *GISTPageOpaque; |
89 | | |
90 | | /* |
91 | | * Maximum possible sizes for GiST index tuple and index key. Calculation is |
92 | | * based on assumption that GiST page should fit at least 4 tuples. In theory, |
93 | | * GiST index can be functional when page can fit 3 tuples. But that seems |
94 | | * rather inefficient, so we use a bit conservative estimate. |
95 | | * |
96 | | * The maximum size of index key is true for unicolumn index. Therefore, this |
97 | | * estimation should be used to figure out which maximum size of GiST index key |
98 | | * makes sense at all. For multicolumn indexes, user might be able to tune |
99 | | * key size using opclass parameters. |
100 | | */ |
101 | | #define GISTMaxIndexTupleSize \ |
102 | 0 | MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \ |
103 | 0 | 4 - sizeof(ItemIdData)) |
104 | | |
105 | | #define GISTMaxIndexKeySize \ |
106 | 0 | (GISTMaxIndexTupleSize - MAXALIGN(sizeof(IndexTupleData))) |
107 | | |
108 | | /* |
109 | | * The page ID is for the convenience of pg_filedump and similar utilities, |
110 | | * which otherwise would have a hard time telling pages of different index |
111 | | * types apart. It should be the last 2 bytes on the page. This is more or |
112 | | * less "free" due to alignment considerations. |
113 | | */ |
114 | 0 | #define GIST_PAGE_ID 0xFF81 |
115 | | |
116 | | /* |
117 | | * This is the Split Vector to be returned by the PickSplit method. |
118 | | * PickSplit should fill the indexes of tuples to go to the left side into |
119 | | * spl_left[], and those to go to the right into spl_right[] (note the method |
120 | | * is responsible for palloc'ing both of these arrays!). The tuple counts |
121 | | * go into spl_nleft/spl_nright, and spl_ldatum/spl_rdatum must be set to |
122 | | * the union keys for each side. |
123 | | * |
124 | | * If spl_ldatum_exists and spl_rdatum_exists are true, then we are performing |
125 | | * a "secondary split" using a non-first index column. In this case some |
126 | | * decisions have already been made about a page split, and the set of tuples |
127 | | * being passed to PickSplit is just the tuples about which we are undecided. |
128 | | * spl_ldatum/spl_rdatum then contain the union keys for the tuples already |
129 | | * chosen to go left or right. Ideally the PickSplit method should take those |
130 | | * keys into account while deciding what to do with the remaining tuples, ie |
131 | | * it should try to "build out" from those unions so as to minimally expand |
132 | | * them. If it does so, it should union the given tuples' keys into the |
133 | | * existing spl_ldatum/spl_rdatum values rather than just setting those values |
134 | | * from scratch, and then set spl_ldatum_exists/spl_rdatum_exists to false to |
135 | | * show it has done this. |
136 | | * |
137 | | * If the PickSplit method fails to clear spl_ldatum_exists/spl_rdatum_exists, |
138 | | * the core GiST code will make its own decision about how to merge the |
139 | | * secondary-split results with the previously-chosen tuples, and will then |
140 | | * recompute the union keys from scratch. This is a workable though often not |
141 | | * optimal approach. |
142 | | */ |
143 | | typedef struct GIST_SPLITVEC |
144 | | { |
145 | | OffsetNumber *spl_left; /* array of entries that go left */ |
146 | | int spl_nleft; /* size of this array */ |
147 | | Datum spl_ldatum; /* Union of keys in spl_left */ |
148 | | bool spl_ldatum_exists; /* true, if spl_ldatum already exists. */ |
149 | | |
150 | | OffsetNumber *spl_right; /* array of entries that go right */ |
151 | | int spl_nright; /* size of the array */ |
152 | | Datum spl_rdatum; /* Union of keys in spl_right */ |
153 | | bool spl_rdatum_exists; /* true, if spl_rdatum already exists. */ |
154 | | } GIST_SPLITVEC; |
155 | | |
156 | | /* |
157 | | * An entry on a GiST node. Contains the key, as well as its own |
158 | | * location (rel,page,offset) which can supply the matching pointer. |
159 | | * leafkey is a flag to tell us if the entry is in a leaf node. |
160 | | */ |
161 | | typedef struct GISTENTRY |
162 | | { |
163 | | Datum key; |
164 | | Relation rel; |
165 | | Page page; |
166 | | OffsetNumber offset; |
167 | | bool leafkey; |
168 | | } GISTENTRY; |
169 | | |
170 | 0 | #define GistPageGetOpaque(page) ( (GISTPageOpaque) PageGetSpecialPointer(page) ) |
171 | | |
172 | 0 | #define GistPageIsLeaf(page) ( GistPageGetOpaque(page)->flags & F_LEAF) |
173 | 0 | #define GIST_LEAF(entry) (GistPageIsLeaf((entry)->page)) |
174 | | |
175 | 0 | #define GistPageIsDeleted(page) ( GistPageGetOpaque(page)->flags & F_DELETED) |
176 | | |
177 | | #define GistTuplesDeleted(page) ( GistPageGetOpaque(page)->flags & F_TUPLES_DELETED) |
178 | 0 | #define GistMarkTuplesDeleted(page) ( GistPageGetOpaque(page)->flags |= F_TUPLES_DELETED) |
179 | | #define GistClearTuplesDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_TUPLES_DELETED) |
180 | | |
181 | 0 | #define GistPageHasGarbage(page) ( GistPageGetOpaque(page)->flags & F_HAS_GARBAGE) |
182 | 0 | #define GistMarkPageHasGarbage(page) ( GistPageGetOpaque(page)->flags |= F_HAS_GARBAGE) |
183 | 0 | #define GistClearPageHasGarbage(page) ( GistPageGetOpaque(page)->flags &= ~F_HAS_GARBAGE) |
184 | | |
185 | 0 | #define GistFollowRight(page) ( GistPageGetOpaque(page)->flags & F_FOLLOW_RIGHT) |
186 | 0 | #define GistMarkFollowRight(page) ( GistPageGetOpaque(page)->flags |= F_FOLLOW_RIGHT) |
187 | 0 | #define GistClearFollowRight(page) ( GistPageGetOpaque(page)->flags &= ~F_FOLLOW_RIGHT) |
188 | | |
189 | 0 | #define GistPageGetNSN(page) ( PageXLogRecPtrGet(GistPageGetOpaque(page)->nsn)) |
190 | 0 | #define GistPageSetNSN(page, val) ( PageXLogRecPtrSet(GistPageGetOpaque(page)->nsn, val)) |
191 | | |
192 | | |
193 | | /* |
194 | | * On a deleted page, we store this struct. A deleted page doesn't contain any |
195 | | * tuples, so we don't use the normal page layout with line pointers. Instead, |
196 | | * this struct is stored right after the standard page header. pd_lower points |
197 | | * to the end of this struct. If we add fields to this struct in the future, we |
198 | | * can distinguish the old and new formats by pd_lower. |
199 | | */ |
200 | | typedef struct GISTDeletedPageContents |
201 | | { |
202 | | /* last xid which could see the page in a scan */ |
203 | | FullTransactionId deleteXid; |
204 | | } GISTDeletedPageContents; |
205 | | |
206 | | static inline void |
207 | | GistPageSetDeleted(Page page, FullTransactionId deletexid) |
208 | 0 | { |
209 | 0 | Assert(PageIsEmpty(page)); |
210 | |
|
211 | 0 | GistPageGetOpaque(page)->flags |= F_DELETED; |
212 | 0 | ((PageHeader) page)->pd_lower = MAXALIGN(SizeOfPageHeaderData) + sizeof(GISTDeletedPageContents); |
213 | |
|
214 | 0 | ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid = deletexid; |
215 | 0 | } Unexecuted instantiation: reloptions.c:GistPageSetDeleted Unexecuted instantiation: gist.c:GistPageSetDeleted Unexecuted instantiation: gistbuild.c:GistPageSetDeleted Unexecuted instantiation: gistbuildbuffers.c:GistPageSetDeleted Unexecuted instantiation: gistget.c:GistPageSetDeleted Unexecuted instantiation: gistproc.c:GistPageSetDeleted Unexecuted instantiation: gistscan.c:GistPageSetDeleted Unexecuted instantiation: gistsplit.c:GistPageSetDeleted Unexecuted instantiation: gistutil.c:GistPageSetDeleted Unexecuted instantiation: gistvacuum.c:GistPageSetDeleted Unexecuted instantiation: gistvalidate.c:GistPageSetDeleted Unexecuted instantiation: gistxlog.c:GistPageSetDeleted Unexecuted instantiation: gistdesc.c:GistPageSetDeleted Unexecuted instantiation: rmgr.c:GistPageSetDeleted Unexecuted instantiation: pg_constraint.c:GistPageSetDeleted Unexecuted instantiation: indexcmds.c:GistPageSetDeleted Unexecuted instantiation: tablecmds.c:GistPageSetDeleted Unexecuted instantiation: execReplication.c:GistPageSetDeleted Unexecuted instantiation: network_gist.c:GistPageSetDeleted Unexecuted instantiation: rangetypes_gist.c:GistPageSetDeleted Unexecuted instantiation: tsgistidx.c:GistPageSetDeleted Unexecuted instantiation: tsquery_gist.c:GistPageSetDeleted Unexecuted instantiation: sortsupport.c:GistPageSetDeleted |
216 | | |
217 | | static inline FullTransactionId |
218 | | GistPageGetDeleteXid(Page page) |
219 | 0 | { |
220 | 0 | Assert(GistPageIsDeleted(page)); |
221 | | |
222 | | /* Is the deleteXid field present? */ |
223 | 0 | if (((PageHeader) page)->pd_lower >= MAXALIGN(SizeOfPageHeaderData) + |
224 | 0 | offsetof(GISTDeletedPageContents, deleteXid) + sizeof(FullTransactionId)) |
225 | 0 | { |
226 | 0 | return ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid; |
227 | 0 | } |
228 | 0 | else |
229 | 0 | return FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId); |
230 | 0 | } Unexecuted instantiation: reloptions.c:GistPageGetDeleteXid Unexecuted instantiation: gist.c:GistPageGetDeleteXid Unexecuted instantiation: gistbuild.c:GistPageGetDeleteXid Unexecuted instantiation: gistbuildbuffers.c:GistPageGetDeleteXid Unexecuted instantiation: gistget.c:GistPageGetDeleteXid Unexecuted instantiation: gistproc.c:GistPageGetDeleteXid Unexecuted instantiation: gistscan.c:GistPageGetDeleteXid Unexecuted instantiation: gistsplit.c:GistPageGetDeleteXid Unexecuted instantiation: gistutil.c:GistPageGetDeleteXid Unexecuted instantiation: gistvacuum.c:GistPageGetDeleteXid Unexecuted instantiation: gistvalidate.c:GistPageGetDeleteXid Unexecuted instantiation: gistxlog.c:GistPageGetDeleteXid Unexecuted instantiation: gistdesc.c:GistPageGetDeleteXid Unexecuted instantiation: rmgr.c:GistPageGetDeleteXid Unexecuted instantiation: pg_constraint.c:GistPageGetDeleteXid Unexecuted instantiation: indexcmds.c:GistPageGetDeleteXid Unexecuted instantiation: tablecmds.c:GistPageGetDeleteXid Unexecuted instantiation: execReplication.c:GistPageGetDeleteXid Unexecuted instantiation: network_gist.c:GistPageGetDeleteXid Unexecuted instantiation: rangetypes_gist.c:GistPageGetDeleteXid Unexecuted instantiation: tsgistidx.c:GistPageGetDeleteXid Unexecuted instantiation: tsquery_gist.c:GistPageGetDeleteXid Unexecuted instantiation: sortsupport.c:GistPageGetDeleteXid |
231 | | |
232 | | /* |
233 | | * Vector of GISTENTRY structs; user-defined methods union and picksplit |
234 | | * take it as one of their arguments |
235 | | */ |
236 | | typedef struct |
237 | | { |
238 | | int32 n; /* number of elements */ |
239 | | GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]; |
240 | | } GistEntryVector; |
241 | | |
242 | 0 | #define GEVHDRSZ (offsetof(GistEntryVector, vector)) |
243 | | |
244 | | /* |
245 | | * macro to initialize a GISTENTRY |
246 | | */ |
247 | | #define gistentryinit(e, k, r, pg, o, l) \ |
248 | 0 | do { (e).key = (k); (e).rel = (r); (e).page = (pg); \ |
249 | 0 | (e).offset = (o); (e).leafkey = (l); } while (0) |
250 | | |
251 | | extern StrategyNumber gisttranslatecmptype(CompareType cmptype, Oid opfamily); |
252 | | |
253 | | #endif /* GIST_H */ |