Coverage Report

Created: 2025-08-12 06:43

/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 */