PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gist_private.h File Reference
#include "access/amapi.h"
#include "access/gist.h"
#include "access/itup.h"
#include "lib/pairingheap.h"
#include "storage/bufmgr.h"
#include "storage/buffile.h"
#include "utils/hsearch.h"
#include "access/genam.h"
Include dependency graph for gist_private.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  GISTNodeBufferPage
 
struct  GISTSTATE
 
struct  GISTSearchHeapItem
 
struct  GISTSearchItem
 
struct  GISTScanOpaqueData
 
struct  gistxlogPage
 
struct  SplitPageLayout
 
struct  GISTInsertStack
 
struct  GistSplitVector
 
struct  GISTInsertState
 
struct  GISTNodeBuffer
 
struct  GISTBuildBuffers
 
struct  GiSTOptions
 
struct  GISTPageSplitInfo
 

Macros

#define GIST_MAX_SPLIT_PAGES   75
 
#define GIST_SHARE   BUFFER_LOCK_SHARE
 
#define GIST_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE
 
#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK
 
#define BUFFER_PAGE_DATA_OFFSET   MAXALIGN(offsetof(GISTNodeBufferPage, tupledata))
 
#define PAGE_FREE_SPACE(nbp)   (nbp->freespace)
 
#define PAGE_IS_EMPTY(nbp)   (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
 
#define PAGE_NO_SPACE(nbp, itup)
 
#define GISTSearchItemIsHeap(item)   ((item).blkno == InvalidBlockNumber)
 
#define SizeOfGISTSearchItem(n_distances)
 
#define GIST_ROOT_BLKNO   0
 
#define TUPLE_IS_VALID   0xffff
 
#define TUPLE_IS_INVALID   0xfffe
 
#define GistTupleIsInvalid(itup)   ( ItemPointerGetOffsetNumber( &((itup)->t_tid) ) == TUPLE_IS_INVALID )
 
#define GistTupleSetValid(itup)   ItemPointerSetOffsetNumber( &((itup)->t_tid), TUPLE_IS_VALID )
 
#define LEVEL_HAS_BUFFERS(nlevel, gfbb)
 
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb)    ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer / 2)
 
#define BUFFER_OVERFLOWED(nodeBuffer, gfbb)    ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)
 
#define GiSTPageSize    ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
 
#define GIST_MIN_FILLFACTOR   10
 
#define GIST_DEFAULT_FILLFACTOR   90
 

Typedefs

typedef struct GISTSTATE GISTSTATE
 
typedef struct GISTSearchHeapItem GISTSearchHeapItem
 
typedef struct GISTSearchItem GISTSearchItem
 
typedef struct GISTScanOpaqueData GISTScanOpaqueData
 
typedef GISTScanOpaqueDataGISTScanOpaque
 
typedef struct gistxlogPage gistxlogPage
 
typedef struct SplitPageLayout SplitPageLayout
 
typedef struct GISTInsertStack GISTInsertStack
 
typedef struct GistSplitVector GistSplitVector
 
typedef struct GISTBuildBuffers GISTBuildBuffers
 
typedef enum GistOptBufferingMode GistOptBufferingMode
 
typedef struct GiSTOptions GiSTOptions
 

Enumerations

enum  GistOptBufferingMode { GIST_OPTION_BUFFERING_AUTO , GIST_OPTION_BUFFERING_ON , GIST_OPTION_BUFFERING_OFF }
 

Functions

void gistbuildempty (Relation index)
 
bool gistinsert (Relation r, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, struct IndexInfo *indexInfo)
 
MemoryContext createTempGistContext (void)
 
GISTSTATEinitGISTstate (Relation index)
 
void freeGISTstate (GISTSTATE *giststate)
 
void gistdoinsert (Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
 
bool gistplacetopage (Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markfollowright, Relation heapRel, bool is_build)
 
SplitPageLayoutgistSplit (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
 
XLogRecPtr gistXLogPageDelete (Buffer buffer, FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset)
 
void gistXLogPageReuse (Relation rel, Relation heaprel, BlockNumber blkno, FullTransactionId deleteXid)
 
XLogRecPtr gistXLogUpdate (Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
 
XLogRecPtr gistXLogDelete (Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId snapshotConflictHorizon, Relation heaprel)
 
XLogRecPtr gistXLogSplit (bool page_is_leaf, SplitPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
 
XLogRecPtr gistXLogAssignLSN (void)
 
bool gistgettuple (IndexScanDesc scan, ScanDirection dir)
 
int64 gistgetbitmap (IndexScanDesc scan, TIDBitmap *tbm)
 
bool gistcanreturn (Relation index, int attno)
 
bool gistvalidate (Oid opclassoid)
 
void gistadjustmembers (Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
 
byteagistoptions (Datum reloptions, bool validate)
 
bool gistproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
 
bool gistfitpage (IndexTuple *itvec, int len)
 
bool gistnospace (Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
 
void gistcheckpage (Relation rel, Buffer buf)
 
Buffer gistNewBuffer (Relation r, Relation heaprel)
 
bool gistPageRecyclable (Page page)
 
void gistfillbuffer (Page page, IndexTuple *itup, int len, OffsetNumber off)
 
IndexTuplegistextractpage (Page page, int *len)
 
IndexTuplegistjoinvector (IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
 
IndexTupleDatagistfillitupvec (IndexTuple *vec, int veclen, int *memlen)
 
IndexTuple gistunion (Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
 
IndexTuple gistgetadjusted (Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
 
IndexTuple gistFormTuple (GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf)
 
void gistCompressValues (GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
 
OffsetNumber gistchoose (Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
 
void GISTInitBuffer (Buffer b, uint32 f)
 
void gistinitpage (Page page, uint32 f)
 
void gistdentryinit (GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
 
float gistpenalty (GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
 
void gistMakeUnionItVec (GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
 
bool gistKeyIsEQ (GISTSTATE *giststate, int attno, Datum a, Datum b)
 
void gistDeCompressAtt (GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
 
HeapTuple gistFetchTuple (GISTSTATE *giststate, Relation r, IndexTuple tuple)
 
void gistMakeUnionKey (GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
 
XLogRecPtr gistGetFakeLSN (Relation rel)
 
IndexBulkDeleteResultgistbulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
 
IndexBulkDeleteResultgistvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 
void gistSplitByKey (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
 
IndexBuildResultgistbuild (Relation heap, Relation index, struct IndexInfo *indexInfo)
 
GISTBuildBuffersgistInitBuildBuffers (int pagesPerBuffer, int levelStep, int maxLevel)
 
GISTNodeBuffergistGetNodeBuffer (GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
 
void gistPushItupToNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple itup)
 
bool gistPopItupFromNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *itup)
 
void gistFreeBuildBuffers (GISTBuildBuffers *gfbb)
 
void gistRelocateBuildBuffersOnSplit (GISTBuildBuffers *gfbb, GISTSTATE *giststate, Relation r, int level, Buffer buffer, List *splitinfo)
 
void gistUnloadNodeBuffers (GISTBuildBuffers *gfbb)
 

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

#define BUFFER_HALF_FILLED (   nodeBuffer,
  gfbb 
)     ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer / 2)

Definition at line 324 of file gist_private.h.

◆ BUFFER_OVERFLOWED

#define BUFFER_OVERFLOWED (   nodeBuffer,
  gfbb 
)     ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)

Definition at line 332 of file gist_private.h.

◆ BUFFER_PAGE_DATA_OFFSET

#define BUFFER_PAGE_DATA_OFFSET   MAXALIGN(offsetof(GISTNodeBufferPage, tupledata))

Definition at line 53 of file gist_private.h.

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 480 of file gist_private.h.

◆ GIST_EXCLUSIVE

#define GIST_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE

Definition at line 43 of file gist_private.h.

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 479 of file gist_private.h.

◆ GIST_ROOT_BLKNO

#define GIST_ROOT_BLKNO   0

Definition at line 262 of file gist_private.h.

◆ GIST_SHARE

#define GIST_SHARE   BUFFER_LOCK_SHARE

Definition at line 42 of file gist_private.h.

◆ GIST_UNLOCK

#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK

Definition at line 44 of file gist_private.h.

◆ GiSTPageSize

#define GiSTPageSize    ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )

Definition at line 476 of file gist_private.h.

◆ GISTSearchItemIsHeap

#define GISTSearchItemIsHeap (   item)    ((item).blkno == InvalidBlockNumber)

Definition at line 145 of file gist_private.h.

◆ GistTupleIsInvalid

#define GistTupleIsInvalid (   itup)    ( ItemPointerGetOffsetNumber( &((itup)->t_tid) ) == TUPLE_IS_INVALID )

Definition at line 288 of file gist_private.h.

◆ GistTupleSetValid

#define GistTupleSetValid (   itup)    ItemPointerSetOffsetNumber( &((itup)->t_tid), TUPLE_IS_VALID )

Definition at line 289 of file gist_private.h.

◆ LEVEL_HAS_BUFFERS

#define LEVEL_HAS_BUFFERS (   nlevel,
  gfbb 
)
Value:
((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
(nlevel) != (gfbb)->rootlevel)
Datum nlevel(PG_FUNCTION_ARGS)
Definition: ltree_op.c:203

Definition at line 319 of file gist_private.h.

◆ PAGE_FREE_SPACE

#define PAGE_FREE_SPACE (   nbp)    (nbp->freespace)

Definition at line 55 of file gist_private.h.

◆ PAGE_IS_EMPTY

#define PAGE_IS_EMPTY (   nbp)    (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)

Definition at line 57 of file gist_private.h.

◆ PAGE_NO_SPACE

#define PAGE_NO_SPACE (   nbp,
  itup 
)
Value:
(PAGE_FREE_SPACE(nbp) < \
MAXALIGN(IndexTupleSize(itup)))
#define PAGE_FREE_SPACE(nbp)
Definition: gist_private.h:55
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71

Definition at line 59 of file gist_private.h.

◆ SizeOfGISTSearchItem

#define SizeOfGISTSearchItem (   n_distances)
Value:
(offsetof(GISTSearchItem, distances) + \
sizeof(IndexOrderByDistance) * (n_distances))

Definition at line 147 of file gist_private.h.

◆ TUPLE_IS_INVALID

#define TUPLE_IS_INVALID   0xfffe

Definition at line 286 of file gist_private.h.

◆ TUPLE_IS_VALID

#define TUPLE_IS_VALID   0xffff

Definition at line 285 of file gist_private.h.

Typedef Documentation

◆ GISTBuildBuffers

◆ GISTInsertStack

◆ GistOptBufferingMode

◆ GiSTOptions

typedef struct GiSTOptions GiSTOptions

◆ GISTScanOpaque

Definition at line 181 of file gist_private.h.

◆ GISTScanOpaqueData

◆ GISTSearchHeapItem

◆ GISTSearchItem

◆ GistSplitVector

◆ GISTSTATE

typedef struct GISTSTATE GISTSTATE

◆ gistxlogPage

typedef struct gistxlogPage gistxlogPage

◆ SplitPageLayout

Enumeration Type Documentation

◆ GistOptBufferingMode

Enumerator
GIST_OPTION_BUFFERING_AUTO 
GIST_OPTION_BUFFERING_ON 
GIST_OPTION_BUFFERING_OFF 

Definition at line 384 of file gist_private.h.

385{
GistOptBufferingMode
Definition: gist_private.h:385
@ GIST_OPTION_BUFFERING_OFF
Definition: gist_private.h:388
@ GIST_OPTION_BUFFERING_AUTO
Definition: gist_private.h:386
@ GIST_OPTION_BUFFERING_ON
Definition: gist_private.h:387

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 128 of file gist.c.

129{
131 "GiST temporary context",
133}
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

Referenced by gist_xlog_startup(), gistbeginscan(), gistbuild(), and gistinsert().

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1664 of file gist.c.

1665{
1666 /* It's sufficient to delete the scanCxt */
1667 MemoryContextDelete(giststate->scanCxt);
1668}
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
MemoryContext scanCxt
Definition: gist_private.h:77

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

◆ gistadjustmembers()

void gistadjustmembers ( Oid  opfamilyoid,
Oid  opclassoid,
List operators,
List functions 
)

Definition at line 288 of file gistvalidate.c.

292{
293 ListCell *lc;
294
295 /*
296 * Operator members of a GiST opfamily should never have hard
297 * dependencies, since their connection to the opfamily depends only on
298 * what the support functions think, and that can be altered. For
299 * consistency, we make all soft dependencies point to the opfamily,
300 * though a soft dependency on the opclass would work as well in the
301 * CREATE OPERATOR CLASS case.
302 */
303 foreach(lc, operators)
304 {
306
307 op->ref_is_hard = false;
308 op->ref_is_family = true;
309 op->refobjid = opfamilyoid;
310 }
311
312 /*
313 * Required support functions should have hard dependencies. Preferably
314 * those are just dependencies on the opclass, but if we're in ALTER
315 * OPERATOR FAMILY, we leave the dependency pointing at the whole
316 * opfamily. (Given that GiST opclasses generally don't share opfamilies,
317 * it seems unlikely to be worth working harder.)
318 */
319 foreach(lc, functions)
320 {
322
323 switch (op->number)
324 {
326 case GIST_UNION_PROC:
329 case GIST_EQUAL_PROC:
330 /* Required support function */
331 op->ref_is_hard = true;
332 break;
336 case GIST_FETCH_PROC:
340 /* Optional, so force it to be a soft family dependency */
341 op->ref_is_hard = false;
342 op->ref_is_family = true;
343 op->refobjid = opfamilyoid;
344 break;
345 default:
347 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
348 errmsg("support function number %d is invalid for access method %s",
349 op->number, "gist")));
350 break;
351 }
352 }
353}
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define GIST_STRATNUM_PROC
Definition: gist.h:43
#define GIST_DECOMPRESS_PROC
Definition: gist.h:35
#define GIST_PICKSPLIT_PROC
Definition: gist.h:37
#define GIST_CONSISTENT_PROC
Definition: gist.h:32
#define GIST_UNION_PROC
Definition: gist.h:33
#define GIST_FETCH_PROC
Definition: gist.h:40
#define GIST_SORTSUPPORT_PROC
Definition: gist.h:42
#define GIST_COMPRESS_PROC
Definition: gist.h:34
#define GIST_PENALTY_PROC
Definition: gist.h:36
#define GIST_OPTIONS_PROC
Definition: gist.h:41
#define GIST_DISTANCE_PROC
Definition: gist.h:39
#define GIST_EQUAL_PROC
Definition: gist.h:38
#define lfirst(lc)
Definition: pg_list.h:172
static const struct fns functions
Definition: regcomp.c:358
Oid refobjid
Definition: amapi.h:96
bool ref_is_family
Definition: amapi.h:95
int number
Definition: amapi.h:90
bool ref_is_hard
Definition: amapi.h:94

References ereport, errcode(), errmsg(), ERROR, functions, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_OPTIONS_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_SORTSUPPORT_PROC, GIST_STRATNUM_PROC, GIST_UNION_PROC, lfirst, OpFamilyMember::number, OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, and OpFamilyMember::refobjid.

Referenced by gisthandler().

◆ gistbuild()

IndexBuildResult * gistbuild ( Relation  heap,
Relation  index,
struct IndexInfo indexInfo 
)

Definition at line 179 of file gistbuild.c.

180{
181 IndexBuildResult *result;
182 double reltuples;
183 GISTBuildState buildstate;
185 int fillfactor;
186 Oid SortSupportFnOids[INDEX_MAX_KEYS];
187 GiSTOptions *options = (GiSTOptions *) index->rd_options;
188
189 /*
190 * We expect to be called exactly once for any index relation. If that's
191 * not the case, big trouble's what we have.
192 */
194 elog(ERROR, "index \"%s\" already contains data",
196
197 buildstate.indexrel = index;
198 buildstate.heaprel = heap;
199 buildstate.sortstate = NULL;
200 buildstate.giststate = initGISTstate(index);
201
202 /*
203 * Create a temporary memory context that is reset once for each tuple
204 * processed. (Note: we don't bother to make this a child of the
205 * giststate's scanCxt, so we have to delete it separately at the end.)
206 */
208
209 /*
210 * Choose build strategy. First check whether the user specified to use
211 * buffering mode. (The use-case for that in the field is somewhat
212 * questionable perhaps, but it's important for testing purposes.)
213 */
214 if (options)
215 {
216 if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
217 buildstate.buildMode = GIST_BUFFERING_STATS;
218 else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
220 else /* must be "auto" */
221 buildstate.buildMode = GIST_BUFFERING_AUTO;
222 }
223 else
224 {
225 buildstate.buildMode = GIST_BUFFERING_AUTO;
226 }
227
228 /*
229 * Unless buffering mode was forced, see if we can use sorting instead.
230 */
231 if (buildstate.buildMode != GIST_BUFFERING_STATS)
232 {
233 bool hasallsortsupports = true;
235
236 for (int i = 0; i < keyscount; i++)
237 {
238 SortSupportFnOids[i] = index_getprocid(index, i + 1,
240 if (!OidIsValid(SortSupportFnOids[i]))
241 {
242 hasallsortsupports = false;
243 break;
244 }
245 }
246 if (hasallsortsupports)
247 buildstate.buildMode = GIST_SORTED_BUILD;
248 }
249
250 /*
251 * Calculate target amount of free space to leave on pages.
252 */
254 buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
255
256 /*
257 * Build the index using the chosen strategy.
258 */
259 buildstate.indtuples = 0;
260 buildstate.indtuplesSize = 0;
261
262 if (buildstate.buildMode == GIST_SORTED_BUILD)
263 {
264 /*
265 * Sort all data, build the index from bottom up.
266 */
267 buildstate.sortstate = tuplesort_begin_index_gist(heap,
268 index,
270 NULL,
272
273 /* Scan the table, adding all tuples to the tuplesort */
274 reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
276 &buildstate, NULL);
277
278 /*
279 * Perform the sort and build index pages.
280 */
282
283 gist_indexsortbuild(&buildstate);
284
285 tuplesort_end(buildstate.sortstate);
286 }
287 else
288 {
289 /*
290 * Initialize an empty index and insert all tuples, possibly using
291 * buffers on intermediate levels.
292 */
293 Buffer buffer;
294 Page page;
295
296 /* initialize the root page */
297 buffer = gistNewBuffer(index, heap);
299 page = BufferGetPage(buffer);
300
302
303 GISTInitBuffer(buffer, F_LEAF);
304
305 MarkBufferDirty(buffer);
307
308 UnlockReleaseBuffer(buffer);
309
311
312 /* Scan the table, inserting all the tuples to the index. */
313 reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
315 &buildstate, NULL);
316
317 /*
318 * If buffering was used, flush out all the tuples that are still in
319 * the buffers.
320 */
321 if (buildstate.buildMode == GIST_BUFFERING_ACTIVE)
322 {
323 elog(DEBUG1, "all tuples processed, emptying buffers");
324 gistEmptyAllBuffers(&buildstate);
325 gistFreeBuildBuffers(buildstate.gfbb);
326 }
327
328 /*
329 * We didn't write WAL records as we built the index, so if
330 * WAL-logging is required, write all pages to the WAL now.
331 */
333 {
336 true);
337 }
338 }
339
340 /* okay, all heap tuples are indexed */
341 MemoryContextSwitchTo(oldcxt);
343
344 freeGISTstate(buildstate.giststate);
345
346 /*
347 * Return statistics
348 */
349 result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
350
351 result->heap_tuples = reltuples;
352 result->index_tuples = (double) buildstate.indtuples;
353
354 return result;
355}
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4231
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5390
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2952
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:283
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:417
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:391
PageData * Page
Definition: bufpage.h:82
#define OidIsValid(objectId)
Definition: c.h:746
#define DEBUG1
Definition: elog.h:30
#define elog(elevel,...)
Definition: elog.h:226
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1537
MemoryContext createTempGistContext(void)
Definition: gist.c:128
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1664
#define F_LEAF
Definition: gist.h:49
#define GistBuildLSN
Definition: gist.h:70
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:480
@ GIST_BUFFERING_AUTO
Definition: gistbuild.c:72
@ GIST_SORTED_BUILD
Definition: gistbuild.c:69
@ GIST_BUFFERING_ACTIVE
Definition: gistbuild.c:78
@ GIST_BUFFERING_STATS
Definition: gistbuild.c:75
@ GIST_BUFFERING_DISABLED
Definition: gistbuild.c:70
static void gist_indexsortbuild(GISTBuildState *state)
Definition: gistbuild.c:400
static void gistBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:822
static void gistSortedBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:366
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:1372
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
Buffer gistNewBuffer(Relation r, Relation heaprel)
Definition: gistutil.c:824
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
int maintenance_work_mem
Definition: globals.c:134
Assert(PointerIsAligned(start, uint64))
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:873
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void * palloc(Size size)
Definition: mcxt.c:1943
#define START_CRIT_SECTION()
Definition: miscadmin.h:150
#define END_CRIT_SECTION()
Definition: miscadmin.h:152
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define INDEX_MAX_KEYS
static int fillfactor
Definition: pgbench.c:188
unsigned int Oid
Definition: postgres_ext.h:30
#define RelationGetRelationName(relation)
Definition: rel.h:550
#define RelationNeedsWAL(relation)
Definition: rel.h:639
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:535
@ MAIN_FORKNUM
Definition: relpath.h:58
GISTBuildBuffers * gfbb
Definition: gistbuild.c:100
Relation indexrel
Definition: gistbuild.c:84
GISTSTATE * giststate
Definition: gistbuild.c:86
int64 indtuples
Definition: gistbuild.c:92
Size freespace
Definition: gistbuild.c:88
Relation heaprel
Definition: gistbuild.c:85
int64 indtuplesSize
Definition: gistbuild.c:99
Tuplesortstate * sortstate
Definition: gistbuild.c:106
GistBuildMode buildMode
Definition: gistbuild.c:90
MemoryContext tempCxt
Definition: gist_private.h:78
double heap_tuples
Definition: genam.h:55
double index_tuples
Definition: genam.h:56
Definition: type.h:96
static double table_index_build_scan(Relation table_rel, Relation index_rel, struct IndexInfo *index_info, bool allow_sync, bool progress, IndexBuildCallback callback, void *callback_state, TableScanDesc scan)
Definition: tableam.h:1735
void tuplesort_performsort(Tuplesortstate *state)
Definition: tuplesort.c:1363
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:951
#define TUPLESORT_NONE
Definition: tuplesort.h:94
Tuplesortstate * tuplesort_begin_index_gist(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
void log_newpage_range(Relation rel, ForkNumber forknum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1270

References Assert(), BufferGetBlockNumber(), BufferGetPage(), GISTBuildState::buildMode, createTempGistContext(), CurrentMemoryContext, DEBUG1, elog, END_CRIT_SECTION, ERROR, F_LEAF, fillfactor, freeGISTstate(), GISTBuildState::freespace, GISTBuildState::gfbb, GIST_BUFFERING_ACTIVE, GIST_BUFFERING_AUTO, GIST_BUFFERING_DISABLED, GIST_BUFFERING_STATS, GIST_DEFAULT_FILLFACTOR, gist_indexsortbuild(), GIST_OPTION_BUFFERING_OFF, GIST_OPTION_BUFFERING_ON, GIST_ROOT_BLKNO, GIST_SORTED_BUILD, GIST_SORTSUPPORT_PROC, gistBuildCallback(), GistBuildLSN, gistEmptyAllBuffers(), gistFreeBuildBuffers(), GISTInitBuffer(), gistNewBuffer(), gistSortedBuildCallback(), GISTBuildState::giststate, IndexBuildResult::heap_tuples, GISTBuildState::heaprel, i, if(), index_getprocid(), INDEX_MAX_KEYS, IndexBuildResult::index_tuples, GISTBuildState::indexrel, IndexRelationGetNumberOfKeyAttributes, GISTBuildState::indtuples, GISTBuildState::indtuplesSize, initGISTstate(), log_newpage_range(), MAIN_FORKNUM, maintenance_work_mem, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), OidIsValid, PageSetLSN(), palloc(), RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, GISTBuildState::sortstate, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, tuplesort_begin_index_gist(), tuplesort_end(), TUPLESORT_NONE, tuplesort_performsort(), and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 139 of file gist.c.

140{
141 Buffer buffer;
142
143 /* Initialize the root page */
146
147 /* Initialize and xlog buffer */
149 GISTInitBuffer(buffer, F_LEAF);
150 MarkBufferDirty(buffer);
151 log_newpage_buffer(buffer, true);
153
154 /* Unlock and release the buffer */
155 UnlockReleaseBuffer(buffer);
156}
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:858
@ EB_SKIP_EXTENSION_LOCK
Definition: bufmgr.h:75
@ EB_LOCK_FIRST
Definition: bufmgr.h:87
#define BMR_REL(p_rel)
Definition: bufmgr.h:108
@ INIT_FORKNUM
Definition: relpath.h:61
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1237

References BMR_REL, EB_LOCK_FIRST, EB_SKIP_EXTENSION_LOCK, END_CRIT_SECTION, ExtendBufferedRel(), F_LEAF, GISTInitBuffer(), INIT_FORKNUM, log_newpage_buffer(), MarkBufferDirty(), START_CRIT_SECTION, and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistbulkdelete()

IndexBulkDeleteResult * gistbulkdelete ( IndexVacuumInfo info,
IndexBulkDeleteResult stats,
IndexBulkDeleteCallback  callback,
void *  callback_state 
)

Definition at line 59 of file gistvacuum.c.

61{
62 /* allocate stats if first time through, else re-use existing struct */
63 if (stats == NULL)
65
66 gistvacuumscan(info, stats, callback, callback_state);
67
68 return stats;
69}
static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:125
void * palloc0(Size size)
Definition: mcxt.c:1973
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:46

References callback(), gistvacuumscan(), and palloc0().

Referenced by gisthandler().

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 797 of file gistget.c.

798{
802 return true;
803 else
804 return false;
805}

References GIST_COMPRESS_PROC, GIST_FETCH_PROC, index_getprocid(), IndexRelationGetNumberOfKeyAttributes, and OidIsValid.

Referenced by gisthandler().

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 785 of file gistutil.c.

786{
787 Page page = BufferGetPage(buf);
788
789 /*
790 * ReadBuffer verifies that every newly-read page passes
791 * PageHeaderIsValid, which means it either contains a reasonably sane
792 * page header or is all-zero. We have to defend against the all-zero
793 * case, however.
794 */
795 if (PageIsNew(page))
797 (errcode(ERRCODE_INDEX_CORRUPTED),
798 errmsg("index \"%s\" contains unexpected zero page at block %u",
801 errhint("Please REINDEX it.")));
802
803 /*
804 * Additionally check that the special area looks sane.
805 */
806 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
808 (errcode(ERRCODE_INDEX_CORRUPTED),
809 errmsg("index \"%s\" contains corrupted page at block %u",
812 errhint("Please REINDEX it.")));
813}
static uint16 PageGetSpecialSize(const PageData *page)
Definition: bufpage.h:317
static bool PageIsNew(const PageData *page)
Definition: bufpage.h:234
#define MAXALIGN(LEN)
Definition: c.h:782
int errhint(const char *fmt,...)
Definition: elog.c:1318
static char * buf
Definition: pg_test_fsync.c:72

References buf, BufferGetBlockNumber(), BufferGetPage(), ereport, errcode(), errhint(), errmsg(), ERROR, MAXALIGN, PageGetSpecialSize(), PageIsNew(), and RelationGetRelationName.

Referenced by gistBufferingFindCorrectParent(), gistdoinsert(), gistFindCorrectParent(), gistFindPath(), gistkillitems(), gistNewBuffer(), gistScanPage(), gistvacuum_delete_empty_pages(), and pgstat_gist_page().

◆ gistchoose()

OffsetNumber gistchoose ( Relation  r,
Page  p,
IndexTuple  it,
GISTSTATE giststate 
)

Definition at line 374 of file gistutil.c.

376{
377 OffsetNumber result;
378 OffsetNumber maxoff;
380 float best_penalty[INDEX_MAX_KEYS];
381 GISTENTRY entry,
382 identry[INDEX_MAX_KEYS];
383 bool isnull[INDEX_MAX_KEYS];
384 int keep_current_best;
385
387
388 gistDeCompressAtt(giststate, r,
389 it, NULL, (OffsetNumber) 0,
390 identry, isnull);
391
392 /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
393 result = FirstOffsetNumber;
394
395 /*
396 * The index may have multiple columns, and there's a penalty value for
397 * each column. The penalty associated with a column that appears earlier
398 * in the index definition is strictly more important than the penalty of
399 * a column that appears later in the index definition.
400 *
401 * best_penalty[j] is the best penalty we have seen so far for column j,
402 * or -1 when we haven't yet examined column j. Array entries to the
403 * right of the first -1 are undefined.
404 */
405 best_penalty[0] = -1;
406
407 /*
408 * If we find a tuple that's exactly as good as the currently best one, we
409 * could use either one. When inserting a lot of tuples with the same or
410 * similar keys, it's preferable to descend down the same path when
411 * possible, as that's more cache-friendly. On the other hand, if all
412 * inserts land on the same leaf page after a split, we're never going to
413 * insert anything to the other half of the split, and will end up using
414 * only 50% of the available space. Distributing the inserts evenly would
415 * lead to better space usage, but that hurts cache-locality during
416 * insertion. To get the best of both worlds, when we find a tuple that's
417 * exactly as good as the previous best, choose randomly whether to stick
418 * to the old best, or use the new one. Once we decide to stick to the
419 * old best, we keep sticking to it for any subsequent equally good tuples
420 * we might find. This favors tuples with low offsets, but still allows
421 * some inserts to go to other equally-good subtrees.
422 *
423 * keep_current_best is -1 if we haven't yet had to make a random choice
424 * whether to keep the current best tuple. If we have done so, and
425 * decided to keep it, keep_current_best is 1; if we've decided to
426 * replace, keep_current_best is 0. (This state will be reset to -1 as
427 * soon as we've made the replacement, but sometimes we make the choice in
428 * advance of actually finding a replacement best tuple.)
429 */
430 keep_current_best = -1;
431
432 /*
433 * Loop over tuples on page.
434 */
435 maxoff = PageGetMaxOffsetNumber(p);
436 Assert(maxoff >= FirstOffsetNumber);
437
438 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
439 {
441 bool zero_penalty;
442 int j;
443
444 zero_penalty = true;
445
446 /* Loop over index attributes. */
447 for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
448 {
449 Datum datum;
450 float usize;
451 bool IsNull;
452
453 /* Compute penalty for this column. */
454 datum = index_getattr(itup, j + 1, giststate->leafTupdesc,
455 &IsNull);
456 gistdentryinit(giststate, j, &entry, datum, r, p, i,
457 false, IsNull);
458 usize = gistpenalty(giststate, j, &entry, IsNull,
459 &identry[j], isnull[j]);
460 if (usize > 0)
461 zero_penalty = false;
462
463 if (best_penalty[j] < 0 || usize < best_penalty[j])
464 {
465 /*
466 * New best penalty for column. Tentatively select this tuple
467 * as the target, and record the best penalty. Then reset the
468 * next column's penalty to "unknown" (and indirectly, the
469 * same for all the ones to its right). This will force us to
470 * adopt this tuple's penalty values as the best for all the
471 * remaining columns during subsequent loop iterations.
472 */
473 result = i;
474 best_penalty[j] = usize;
475
477 best_penalty[j + 1] = -1;
478
479 /* we have new best, so reset keep-it decision */
480 keep_current_best = -1;
481 }
482 else if (best_penalty[j] == usize)
483 {
484 /*
485 * The current tuple is exactly as good for this column as the
486 * best tuple seen so far. The next iteration of this loop
487 * will compare the next column.
488 */
489 }
490 else
491 {
492 /*
493 * The current tuple is worse for this column than the best
494 * tuple seen so far. Skip the remaining columns and move on
495 * to the next tuple, if any.
496 */
497 zero_penalty = false; /* so outer loop won't exit */
498 break;
499 }
500 }
501
502 /*
503 * If we looped past the last column, and did not update "result",
504 * then this tuple is exactly as good as the prior best tuple.
505 */
506 if (j == IndexRelationGetNumberOfKeyAttributes(r) && result != i)
507 {
508 if (keep_current_best == -1)
509 {
510 /* we didn't make the random choice yet for this old best */
511 keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
512 }
513 if (keep_current_best == 0)
514 {
515 /* we choose to use the new tuple */
516 result = i;
517 /* choose again if there are even more exactly-as-good ones */
518 keep_current_best = -1;
519 }
520 }
521
522 /*
523 * If we find a tuple with zero penalty for all columns, and we've
524 * decided we don't want to search for another tuple with equal
525 * penalty, there's no need to examine remaining tuples; just break
526 * out of the loop and return it.
527 */
528 if (zero_penalty)
529 {
530 if (keep_current_best == -1)
531 {
532 /* we didn't make the random choice yet for this old best */
533 keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
534 }
535 if (keep_current_best == 1)
536 break;
537 }
538 }
539
540 return result;
541}
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
#define GistPageIsLeaf(page)
Definition: gist.h:170
void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
Definition: gistutil.c:296
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:547
float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
Definition: gistutil.c:724
int j
Definition: isn.c:78
IndexTupleData * IndexTuple
Definition: itup.h:53
static Datum index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: itup.h:131
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
pg_prng_state pg_global_prng_state
Definition: pg_prng.c:34
bool pg_prng_bool(pg_prng_state *state)
Definition: pg_prng.c:313
uintptr_t Datum
Definition: postgres.h:69
TupleDesc leafTupdesc
Definition: gist_private.h:80

References Assert(), FirstOffsetNumber, gistDeCompressAtt(), gistdentryinit(), GistPageIsLeaf, gistpenalty(), i, index_getattr(), INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, j, GISTSTATE::leafTupdesc, OffsetNumberNext, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), pg_global_prng_state, and pg_prng_bool().

Referenced by gistdoinsert(), and gistProcessItup().

◆ gistCompressValues()

void gistCompressValues ( GISTSTATE giststate,
Relation  r,
const Datum attdata,
const bool *  isnull,
bool  isleaf,
Datum compatt 
)

Definition at line 596 of file gistutil.c.

598{
599 int i;
600
601 /*
602 * Call the compress method on each attribute.
603 */
604 for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
605 {
606 if (isnull[i])
607 compatt[i] = (Datum) 0;
608 else
609 {
610 GISTENTRY centry;
611 GISTENTRY *cep;
612
613 gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
614 isleaf);
615 /* there may not be a compress function in opclass */
616 if (OidIsValid(giststate->compressFn[i].fn_oid))
617 cep = (GISTENTRY *)
619 giststate->supportCollation[i],
620 PointerGetDatum(&centry)));
621 else
622 cep = &centry;
623 compatt[i] = cep->key;
624 }
625 }
626
627 if (isleaf)
628 {
629 /*
630 * Emplace each included attribute if any.
631 */
632 for (; i < r->rd_att->natts; i++)
633 {
634 if (isnull[i])
635 compatt[i] = (Datum) 0;
636 else
637 compatt[i] = attdata[i];
638 }
639 }
640}
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1129
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:245
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
Oid fn_oid
Definition: fmgr.h:59
Datum key
Definition: gist.h:161
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
TupleDesc rd_att
Definition: rel.h:112

References GISTSTATE::compressFn, DatumGetPointer(), FmgrInfo::fn_oid, FunctionCall1Coll(), gistentryinit, i, IndexRelationGetNumberOfKeyAttributes, GISTENTRY::key, TupleDescData::natts, OidIsValid, PointerGetDatum(), RelationData::rd_att, and GISTSTATE::supportCollation.

Referenced by gistFormTuple(), and gistSortedBuildCallback().

◆ gistDeCompressAtt()

void gistDeCompressAtt ( GISTSTATE giststate,
Relation  r,
IndexTuple  tuple,
Page  p,
OffsetNumber  o,
GISTENTRY attdata,
bool *  isnull 
)

Definition at line 296 of file gistutil.c.

298{
299 int i;
300
301 for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
302 {
303 Datum datum;
304
305 datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
306 gistdentryinit(giststate, i, &attdata[i],
307 datum, r, p, o,
308 false, isnull[i]);
309 }
310}

References gistdentryinit(), i, index_getattr(), IndexRelationGetNumberOfKeyAttributes, and GISTSTATE::leafTupdesc.

Referenced by gistchoose(), gistgetadjusted(), gistRelocateBuildBuffersOnSplit(), and placeOne().

◆ gistdentryinit()

void gistdentryinit ( GISTSTATE giststate,
int  nkey,
GISTENTRY e,
Datum  k,
Relation  r,
Page  pg,
OffsetNumber  o,
bool  l,
bool  isNull 
)

Definition at line 547 of file gistutil.c.

550{
551 if (!isNull)
552 {
553 GISTENTRY *dep;
554
555 gistentryinit(*e, k, r, pg, o, l);
556
557 /* there may not be a decompress function in opclass */
558 if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
559 return;
560
561 dep = (GISTENTRY *)
563 giststate->supportCollation[nkey],
565 /* decompressFn may just return the given pointer */
566 if (dep != e)
567 gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
568 dep->leafkey);
569 }
570 else
571 gistentryinit(*e, (Datum) 0, r, pg, o, l);
572}
e
Definition: preproc-init.c:82
OffsetNumber offset
Definition: gist.h:164
Page page
Definition: gist.h:163
Relation rel
Definition: gist.h:162
bool leafkey
Definition: gist.h:165
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89

References DatumGetPointer(), GISTSTATE::decompressFn, FmgrInfo::fn_oid, FunctionCall1Coll(), gistentryinit, GISTENTRY::key, GISTENTRY::leafkey, GISTENTRY::offset, OidIsValid, GISTENTRY::page, PointerGetDatum(), GISTENTRY::rel, and GISTSTATE::supportCollation.

Referenced by gistchoose(), gistDeCompressAtt(), gistindex_keytest(), gistMakeUnionItVec(), and gistSplitByKey().

◆ gistdoinsert()

void gistdoinsert ( Relation  r,
IndexTuple  itup,
Size  freespace,
GISTSTATE giststate,
Relation  heapRel,
bool  is_build 
)

Definition at line 639 of file gist.c.

641{
642 ItemId iid;
643 IndexTuple idxtuple;
644 GISTInsertStack firststack;
645 GISTInsertStack *stack;
647 bool xlocked = false;
648
649 memset(&state, 0, sizeof(GISTInsertState));
650 state.freespace = freespace;
651 state.r = r;
652 state.heapRel = heapRel;
653 state.is_build = is_build;
654
655 /* Start from the root */
656 firststack.blkno = GIST_ROOT_BLKNO;
657 firststack.lsn = 0;
658 firststack.retry_from_parent = false;
659 firststack.parent = NULL;
661 state.stack = stack = &firststack;
662
663 /*
664 * Walk down along the path of smallest penalty, updating the parent
665 * pointers with the key we're inserting as we go. If we crash in the
666 * middle, the tree is consistent, although the possible parent updates
667 * were a waste.
668 */
669 for (;;)
670 {
671 /*
672 * If we split an internal page while descending the tree, we have to
673 * retry at the parent. (Normally, the LSN-NSN interlock below would
674 * also catch this and cause us to retry. But LSNs are not updated
675 * during index build.)
676 */
677 while (stack->retry_from_parent)
678 {
679 if (xlocked)
681 xlocked = false;
682 ReleaseBuffer(stack->buffer);
683 state.stack = stack = stack->parent;
684 }
685
686 if (XLogRecPtrIsInvalid(stack->lsn))
687 stack->buffer = ReadBuffer(state.r, stack->blkno);
688
689 /*
690 * Be optimistic and grab shared lock first. Swap it for an exclusive
691 * lock later if we need to update the page.
692 */
693 if (!xlocked)
694 {
696 gistcheckpage(state.r, stack->buffer);
697 }
698
699 stack->page = (Page) BufferGetPage(stack->buffer);
700 stack->lsn = xlocked ?
701 PageGetLSN(stack->page) : BufferGetLSNAtomic(stack->buffer);
703
704 /*
705 * If this page was split but the downlink was never inserted to the
706 * parent because the inserting backend crashed before doing that, fix
707 * that now.
708 */
709 if (GistFollowRight(stack->page))
710 {
711 if (!xlocked)
712 {
715 xlocked = true;
716 /* someone might've completed the split when we unlocked */
717 if (!GistFollowRight(stack->page))
718 continue;
719 }
720 gistfixsplit(&state, giststate);
721
723 xlocked = false;
724 state.stack = stack = stack->parent;
725 continue;
726 }
727
728 if ((stack->blkno != GIST_ROOT_BLKNO &&
729 stack->parent->lsn < GistPageGetNSN(stack->page)) ||
730 GistPageIsDeleted(stack->page))
731 {
732 /*
733 * Concurrent split or page deletion detected. There's no
734 * guarantee that the downlink for this page is consistent with
735 * the tuple we're inserting anymore, so go back to parent and
736 * rechoose the best child.
737 */
739 xlocked = false;
740 state.stack = stack = stack->parent;
741 continue;
742 }
743
744 if (!GistPageIsLeaf(stack->page))
745 {
746 /*
747 * This is an internal page so continue to walk down the tree.
748 * Find the child node that has the minimum insertion penalty.
749 */
750 BlockNumber childblkno;
751 IndexTuple newtup;
752 GISTInsertStack *item;
753 OffsetNumber downlinkoffnum;
754
755 downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate);
756 iid = PageGetItemId(stack->page, downlinkoffnum);
757 idxtuple = (IndexTuple) PageGetItem(stack->page, iid);
758 childblkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
759
760 /*
761 * Check that it's not a leftover invalid tuple from pre-9.1
762 */
763 if (GistTupleIsInvalid(idxtuple))
765 (errmsg("index \"%s\" contains an inner tuple marked as invalid",
767 errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
768 errhint("Please REINDEX it.")));
769
770 /*
771 * Check that the key representing the target child node is
772 * consistent with the key we're inserting. Update it if it's not.
773 */
774 newtup = gistgetadjusted(state.r, idxtuple, itup, giststate);
775 if (newtup)
776 {
777 /*
778 * Swap shared lock for an exclusive one. Beware, the page may
779 * change while we unlock/lock the page...
780 */
781 if (!xlocked)
782 {
785 xlocked = true;
786 stack->page = (Page) BufferGetPage(stack->buffer);
787
788 if (PageGetLSN(stack->page) != stack->lsn)
789 {
790 /* the page was changed while we unlocked it, retry */
791 continue;
792 }
793 }
794
795 /*
796 * Update the tuple.
797 *
798 * We still hold the lock after gistinserttuple(), but it
799 * might have to split the page to make the updated tuple fit.
800 * In that case the updated tuple might migrate to the other
801 * half of the split, so we have to go back to the parent and
802 * descend back to the half that's a better fit for the new
803 * tuple.
804 */
805 if (gistinserttuple(&state, stack, giststate, newtup,
806 downlinkoffnum))
807 {
808 /*
809 * If this was a root split, the root page continues to be
810 * the parent and the updated tuple went to one of the
811 * child pages, so we just need to retry from the root
812 * page.
813 */
814 if (stack->blkno != GIST_ROOT_BLKNO)
815 {
817 xlocked = false;
818 state.stack = stack = stack->parent;
819 }
820 continue;
821 }
822 }
824 xlocked = false;
825
826 /* descend to the chosen child */
827 item = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
828 item->blkno = childblkno;
829 item->parent = stack;
830 item->downlinkoffnum = downlinkoffnum;
831 state.stack = stack = item;
832 }
833 else
834 {
835 /*
836 * Leaf page. Insert the new key. We've already updated all the
837 * parents on the way down, but we might have to split the page if
838 * it doesn't fit. gistinserttuple() will take care of that.
839 */
840
841 /*
842 * Swap shared lock for an exclusive one. Be careful, the page may
843 * change while we unlock/lock the page...
844 */
845 if (!xlocked)
846 {
849 xlocked = true;
850 stack->page = (Page) BufferGetPage(stack->buffer);
851 stack->lsn = PageGetLSN(stack->page);
852
853 if (stack->blkno == GIST_ROOT_BLKNO)
854 {
855 /*
856 * the only page that can become inner instead of leaf is
857 * the root page, so for root we should recheck it
858 */
859 if (!GistPageIsLeaf(stack->page))
860 {
861 /*
862 * very rare situation: during unlock/lock index with
863 * number of pages = 1 was increased
864 */
866 xlocked = false;
867 continue;
868 }
869
870 /*
871 * we don't need to check root split, because checking
872 * leaf/inner is enough to recognize split for root
873 */
874 }
875 else if ((GistFollowRight(stack->page) ||
876 stack->parent->lsn < GistPageGetNSN(stack->page)) ||
877 GistPageIsDeleted(stack->page))
878 {
879 /*
880 * The page was split or deleted while we momentarily
881 * unlocked the page. Go back to parent.
882 */
884 xlocked = false;
885 state.stack = stack = stack->parent;
886 continue;
887 }
888 }
889
890 /* now state.stack->(page, buffer and blkno) points to leaf page */
891
892 gistinserttuple(&state, stack, giststate, itup,
895
896 /* Release any pins we might still hold before exiting */
897 for (; stack; stack = stack->parent)
898 ReleaseBuffer(stack->buffer);
899 break;
900 }
901 }
902}
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5373
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:4493
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5607
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:758
static XLogRecPtr PageGetLSN(const PageData *page)
Definition: bufpage.h:386
int errdetail(const char *fmt,...)
Definition: elog.c:1204
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1200
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1260
#define GistFollowRight(page)
Definition: gist.h:183
#define GistPageIsDeleted(page)
Definition: gist.h:173
#define GistPageGetNSN(page)
Definition: gist.h:187
#define GIST_UNLOCK
Definition: gist_private.h:44
#define GIST_EXCLUSIVE
Definition: gist_private.h:43
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:288
#define GIST_SHARE
Definition: gist_private.h:42
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:316
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:374
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:785
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
#define InvalidOffsetNumber
Definition: off.h:26
BlockNumber blkno
Definition: gist_private.h:210
OffsetNumber downlinkoffnum
Definition: gist_private.h:228
struct GISTInsertStack * parent
Definition: gist_private.h:231
ItemPointerData t_tid
Definition: itup.h:37
Definition: regguts.h:323
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29

References Assert(), GISTInsertStack::blkno, GISTInsertStack::buffer, BufferGetLSNAtomic(), BufferGetPage(), GISTInsertStack::downlinkoffnum, ereport, errdetail(), errhint(), errmsg(), ERROR, GIST_EXCLUSIVE, GIST_ROOT_BLKNO, GIST_SHARE, GIST_UNLOCK, gistcheckpage(), gistchoose(), gistfixsplit(), GistFollowRight, gistgetadjusted(), gistinserttuple(), GistPageGetNSN, GistPageIsDeleted, GistPageIsLeaf, GistTupleIsInvalid, InvalidOffsetNumber, ItemPointerGetBlockNumber(), LockBuffer(), GISTInsertStack::lsn, GISTInsertStack::page, PageGetItem(), PageGetItemId(), PageGetLSN(), palloc0(), GISTInsertStack::parent, ReadBuffer(), RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), GISTInsertStack::retry_from_parent, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogRecPtrIsInvalid.

Referenced by gistBuildCallback(), and gistinsert().

◆ gistextractpage()

IndexTuple * gistextractpage ( Page  page,
int *  len 
)

Definition at line 95 of file gistutil.c.

96{
98 maxoff;
99 IndexTuple *itvec;
100
101 maxoff = PageGetMaxOffsetNumber(page);
102 *len = maxoff;
103 itvec = palloc(sizeof(IndexTuple) * maxoff);
104 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
105 itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
106
107 return itvec;
108}
const void size_t len

References FirstOffsetNumber, i, len, OffsetNumberNext, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), and palloc().

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistFetchTuple()

HeapTuple gistFetchTuple ( GISTSTATE giststate,
Relation  r,
IndexTuple  tuple 
)

Definition at line 667 of file gistutil.c.

668{
669 MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
671 bool isnull[INDEX_MAX_KEYS];
672 int i;
673
674 for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
675 {
676 Datum datum;
677
678 datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
679
680 if (giststate->fetchFn[i].fn_oid != InvalidOid)
681 {
682 if (!isnull[i])
683 fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
684 else
685 fetchatt[i] = (Datum) 0;
686 }
687 else if (giststate->compressFn[i].fn_oid == InvalidOid)
688 {
689 /*
690 * If opclass does not provide compress method that could change
691 * original value, att is necessarily stored in original form.
692 */
693 if (!isnull[i])
694 fetchatt[i] = datum;
695 else
696 fetchatt[i] = (Datum) 0;
697 }
698 else
699 {
700 /*
701 * Index-only scans not supported for this column. Since the
702 * planner chose an index-only scan anyway, it is not interested
703 * in this column, and we can replace it with a NULL.
704 */
705 isnull[i] = true;
706 fetchatt[i] = (Datum) 0;
707 }
708 }
709
710 /*
711 * Get each included attribute.
712 */
713 for (; i < r->rd_att->natts; i++)
714 {
715 fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
716 &isnull[i]);
717 }
718 MemoryContextSwitchTo(oldcxt);
719
720 return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
721}
static Datum gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
Definition: gistutil.c:646
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
#define InvalidOid
Definition: postgres_ext.h:35
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:94
TupleDesc fetchTupdesc
Definition: gist_private.h:83
#define fetchatt(A, T)
Definition: tupmacs.h:47

References GISTSTATE::compressFn, fetchatt, GISTSTATE::fetchFn, GISTSTATE::fetchTupdesc, FmgrInfo::fn_oid, gistFetchAtt(), heap_form_tuple(), i, index_getattr(), INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, InvalidOid, GISTSTATE::leafTupdesc, MemoryContextSwitchTo(), TupleDescData::natts, RelationData::rd_att, and GISTSTATE::tempCxt.

Referenced by gistScanPage().

◆ gistfillbuffer()

void gistfillbuffer ( Page  page,
IndexTuple itup,
int  len,
OffsetNumber  off 
)

Definition at line 34 of file gistutil.c.

35{
36 int i;
37
38 if (off == InvalidOffsetNumber)
39 off = (PageIsEmpty(page)) ? FirstOffsetNumber :
41
42 for (i = 0; i < len; i++)
43 {
44 Size sz = IndexTupleSize(itup[i]);
46
47 l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
48 if (l == InvalidOffsetNumber)
49 elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
50 i, len, (int) sz);
51 off++;
52 }
53}
static bool PageIsEmpty(const PageData *page)
Definition: bufpage.h:224
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:472
size_t Size
Definition: c.h:576
Pointer Item
Definition: item.h:17

References elog, ERROR, FirstOffsetNumber, i, IndexTupleSize(), InvalidOffsetNumber, len, OffsetNumberNext, PageAddItem, PageGetMaxOffsetNumber(), and PageIsEmpty().

Referenced by gist_indexsortbuild_levelstate_add(), gistplacetopage(), and gistRedoPageSplitRecord().

◆ gistfillitupvec()

IndexTupleData * gistfillitupvec ( IndexTuple vec,
int  veclen,
int *  memlen 
)

Definition at line 127 of file gistutil.c.

128{
129 char *ptr,
130 *ret;
131 int i;
132
133 *memlen = 0;
134
135 for (i = 0; i < veclen; i++)
136 *memlen += IndexTupleSize(vec[i]);
137
138 ptr = ret = palloc(*memlen);
139
140 for (i = 0; i < veclen; i++)
141 {
142 memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
143 ptr += IndexTupleSize(vec[i]);
144 }
145
146 return (IndexTupleData *) ret;
147}

References i, IndexTupleSize(), and palloc().

Referenced by gist_indexsortbuild_levelstate_flush(), gistplacetopage(), and gistSplit().

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 79 of file gistutil.c.

80{
81 int i;
82 Size size = 0;
83
84 for (i = 0; i < len; i++)
85 size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
86
87 /* TODO: Consider fillfactor */
88 return (size <= GiSTPageSize);
89}
#define GiSTPageSize
Definition: gist_private.h:476
struct ItemIdData ItemIdData

References GiSTPageSize, i, IndexTupleSize(), and len.

Referenced by gistSplit().

◆ gistFormTuple()

IndexTuple gistFormTuple ( GISTSTATE giststate,
Relation  r,
const Datum attdata,
const bool *  isnull,
bool  isleaf 
)

Definition at line 575 of file gistutil.c.

577{
578 Datum compatt[INDEX_MAX_KEYS];
579 IndexTuple res;
580
581 gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt);
582
583 res = index_form_tuple(isleaf ? giststate->leafTupdesc :
584 giststate->nonLeafTupdesc,
585 compatt, isnull);
586
587 /*
588 * The offset number on tuples on internal pages is unused. For historical
589 * reasons, it is set to 0xffff.
590 */
591 ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
592 return res;
593}
void gistCompressValues(GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
Definition: gistutil.c:596
IndexTuple index_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: indextuple.c:44
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition: itemptr.h:158
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81

References gistCompressValues(), index_form_tuple(), INDEX_MAX_KEYS, ItemPointerSetOffsetNumber(), GISTSTATE::leafTupdesc, GISTSTATE::nonLeafTupdesc, and IndexTupleData::t_tid.

Referenced by gistBuildCallback(), gistgetadjusted(), gistinsert(), gistSplit(), and gistunion().

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 507 of file gistbuildbuffers.c.

508{
509 /* Close buffers file. */
510 BufFileClose(gfbb->pfile);
511
512 /* All other things will be freed on memory context release */
513}
void BufFileClose(BufFile *file)
Definition: buffile.c:412

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

◆ gistgetadjusted()

IndexTuple gistgetadjusted ( Relation  r,
IndexTuple  oldtup,
IndexTuple  addtup,
GISTSTATE giststate 
)

Definition at line 316 of file gistutil.c.

317{
318 bool neednew = false;
319 GISTENTRY oldentries[INDEX_MAX_KEYS],
320 addentries[INDEX_MAX_KEYS];
321 bool oldisnull[INDEX_MAX_KEYS],
322 addisnull[INDEX_MAX_KEYS];
323 Datum attr[INDEX_MAX_KEYS];
324 bool isnull[INDEX_MAX_KEYS];
325 IndexTuple newtup = NULL;
326 int i;
327
328 gistDeCompressAtt(giststate, r, oldtup, NULL,
329 (OffsetNumber) 0, oldentries, oldisnull);
330
331 gistDeCompressAtt(giststate, r, addtup, NULL,
332 (OffsetNumber) 0, addentries, addisnull);
333
334 for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
335 {
336 gistMakeUnionKey(giststate, i,
337 oldentries + i, oldisnull[i],
338 addentries + i, addisnull[i],
339 attr + i, isnull + i);
340
341 if (neednew)
342 /* we already need new key, so we can skip check */
343 continue;
344
345 if (isnull[i])
346 /* union of key may be NULL if and only if both keys are NULL */
347 continue;
348
349 if (!addisnull[i])
350 {
351 if (oldisnull[i] ||
352 !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i]))
353 neednew = true;
354 }
355 }
356
357 if (neednew)
358 {
359 /* need to update key */
360 newtup = gistFormTuple(giststate, r, attr, isnull, false);
361 newtup->t_tid = oldtup->t_tid;
362 }
363
364 return newtup;
365}
void gistMakeUnionKey(GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
Definition: gistutil.c:233
bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
Definition: gistutil.c:281
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf)
Definition: gistutil.c:575

References gistDeCompressAtt(), gistFormTuple(), gistKeyIsEQ(), gistMakeUnionKey(), i, INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, sort-test::key, and IndexTupleData::t_tid.

Referenced by gistdoinsert(), gistformdownlink(), gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

746{
748 int64 ntids = 0;
749 GISTSearchItem fakeItem;
750
751 if (!so->qual_ok)
752 return 0;
753
755 if (scan->instrument)
756 scan->instrument->nsearches++;
757
758 /* Begin the scan by processing the root page */
759 so->curPageData = so->nPageData = 0;
760 scan->xs_hitup = NULL;
761 if (so->pageDataCxt)
763
764 fakeItem.blkno = GIST_ROOT_BLKNO;
765 memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
766 gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
767
768 /*
769 * While scanning a leaf page, ItemPointers of matching heap tuples will
770 * be stored directly into tbm, so we don't need to deal with them here.
771 */
772 for (;;)
773 {
775
776 if (!item)
777 break;
778
780
781 gistScanPage(scan, item, item->distances, tbm, &ntids);
782
783 pfree(item);
784 }
785
786 return ntids;
787}
int64_t int64
Definition: c.h:499
XLogRecPtr GistNSN
Definition: gist.h:63
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:538
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:328
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:414
void pfree(void *pointer)
Definition: mcxt.c:2150
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:694
OffsetNumber nPageData
Definition: gist_private.h:175
OffsetNumber curPageData
Definition: gist_private.h:176
MemoryContext pageDataCxt
Definition: gist_private.h:177
IndexOrderByDistance distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:142
HeapTuple xs_hitup
Definition: relscan.h:169
struct IndexScanInstrumentation * instrument
Definition: relscan.h:159
Relation indexRelation
Definition: relscan.h:137

References CHECK_FOR_INTERRUPTS, GISTScanOpaqueData::curPageData, GISTSearchItem::distances, getNextGISTSearchItem(), GIST_ROOT_BLKNO, gistScanPage(), if(), IndexScanDescData::indexRelation, IndexScanDescData::instrument, MemoryContextReset(), GISTScanOpaqueData::nPageData, IndexScanInstrumentation::nsearches, IndexScanDescData::opaque, GISTScanOpaqueData::pageDataCxt, pfree(), pgstat_count_index_scan, GISTScanOpaqueData::qual_ok, and IndexScanDescData::xs_hitup.

Referenced by gisthandler().

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1016 of file gistutil.c.

1017{
1018 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1019 {
1020 /*
1021 * Temporary relations are only accessible in our session, so a simple
1022 * backend-local counter will do.
1023 */
1024 static XLogRecPtr counter = FirstNormalUnloggedLSN;
1025
1026 return counter++;
1027 }
1028 else if (RelationIsPermanent(rel))
1029 {
1030 /*
1031 * WAL-logging on this relation will start after commit, so its LSNs
1032 * must be distinct numbers smaller than the LSN at the next commit.
1033 * Emit a dummy WAL record if insert-LSN hasn't advanced after the
1034 * last call.
1035 */
1036 static XLogRecPtr lastlsn = InvalidXLogRecPtr;
1037 XLogRecPtr currlsn = GetXLogInsertRecPtr();
1038
1039 /* Shouldn't be called for WAL-logging relations */
1040 Assert(!RelationNeedsWAL(rel));
1041
1042 /* No need for an actual record if we already have a distinct LSN */
1043 if (!XLogRecPtrIsInvalid(lastlsn) && lastlsn == currlsn)
1044 currlsn = gistXLogAssignLSN();
1045
1046 lastlsn = currlsn;
1047 return currlsn;
1048 }
1049 else
1050 {
1051 /*
1052 * Unlogged relations are accessible from other backends, and survive
1053 * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
1054 */
1055 Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
1056 return GetFakeLSNForUnloggedRel();
1057 }
1058}
XLogRecPtr gistXLogAssignLSN(void)
Definition: gistxlog.c:576
#define RelationIsPermanent(relation)
Definition: rel.h:628
Form_pg_class rd_rel
Definition: rel.h:111
XLogRecPtr GetXLogInsertRecPtr(void)
Definition: xlog.c:9612
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4783
#define FirstNormalUnloggedLSN
Definition: xlogdefs.h:36
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28

References Assert(), FirstNormalUnloggedLSN, GetFakeLSNForUnloggedRel(), GetXLogInsertRecPtr(), gistXLogAssignLSN(), InvalidXLogRecPtr, RelationData::rd_rel, RelationIsPermanent, RelationNeedsWAL, and XLogRecPtrIsInvalid.

Referenced by gistdeletepage(), gistplacetopage(), gistprunepage(), gistvacuumpage(), and gistvacuumscan().

◆ gistGetNodeBuffer()

GISTNodeBuffer * gistGetNodeBuffer ( GISTBuildBuffers gfbb,
GISTSTATE giststate,
BlockNumber  nodeBlocknum,
int  level 
)

Definition at line 113 of file gistbuildbuffers.c.

115{
116 GISTNodeBuffer *nodeBuffer;
117 bool found;
118
119 /* Find node buffer in hash table */
120 nodeBuffer = (GISTNodeBuffer *) hash_search(gfbb->nodeBuffersTab,
121 &nodeBlocknum,
123 &found);
124 if (!found)
125 {
126 /*
127 * Node buffer wasn't found. Initialize the new buffer as empty.
128 */
130
131 /* nodeBuffer->nodeBlocknum is the hash key and was filled in already */
132 nodeBuffer->blocksCount = 0;
133 nodeBuffer->pageBlocknum = InvalidBlockNumber;
134 nodeBuffer->pageBuffer = NULL;
135 nodeBuffer->queuedForEmptying = false;
136 nodeBuffer->isTemp = false;
137 nodeBuffer->level = level;
138
139 /*
140 * Add this buffer to the list of buffers on this level. Enlarge
141 * buffersOnLevels array if needed.
142 */
143 if (level >= gfbb->buffersOnLevelsLen)
144 {
145 int i;
146
147 gfbb->buffersOnLevels =
148 (List **) repalloc(gfbb->buffersOnLevels,
149 (level + 1) * sizeof(List *));
150
151 /* initialize the enlarged portion */
152 for (i = gfbb->buffersOnLevelsLen; i <= level; i++)
153 gfbb->buffersOnLevels[i] = NIL;
154 gfbb->buffersOnLevelsLen = level + 1;
155 }
156
157 /*
158 * Prepend the new buffer to the list of buffers on this level. It's
159 * not arbitrary that the new buffer is put to the beginning of the
160 * list: in the final emptying phase we loop through all buffers at
161 * each level, and flush them. If a page is split during the emptying,
162 * it's more efficient to flush the new split pages first, before
163 * moving on to pre-existing pages on the level. The buffers just
164 * created during the page split are likely still in cache, so
165 * flushing them immediately is more efficient than putting them to
166 * the end of the queue.
167 */
168 gfbb->buffersOnLevels[level] = lcons(nodeBuffer,
169 gfbb->buffersOnLevels[level]);
170
171 MemoryContextSwitchTo(oldcxt);
172 }
173
174 return nodeBuffer;
175}
#define InvalidBlockNumber
Definition: block.h:33
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
@ HASH_ENTER
Definition: hsearch.h:114
List * lcons(void *datum, List *list)
Definition: list.c:495
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:2170
#define NIL
Definition: pg_list.h:68
List ** buffersOnLevels
Definition: gist_private.h:368
MemoryContext context
Definition: gist_private.h:341
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
BlockNumber pageBlocknum
Definition: gist_private.h:303
bool queuedForEmptying
Definition: gist_private.h:307
Definition: pg_list.h:54

References GISTNodeBuffer::blocksCount, GISTBuildBuffers::buffersOnLevels, GISTBuildBuffers::buffersOnLevelsLen, GISTBuildBuffers::context, HASH_ENTER, hash_search(), i, InvalidBlockNumber, GISTNodeBuffer::isTemp, lcons(), GISTNodeBuffer::level, MemoryContextSwitchTo(), NIL, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, GISTNodeBuffer::queuedForEmptying, and repalloc().

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 612 of file gistget.c.

613{
615
616 if (dir != ForwardScanDirection)
617 elog(ERROR, "GiST only supports forward scan direction");
618
619 if (!so->qual_ok)
620 return false;
621
622 if (so->firstCall)
623 {
624 /* Begin the scan by processing the root page */
625 GISTSearchItem fakeItem;
626
628 if (scan->instrument)
629 scan->instrument->nsearches++;
630
631 so->firstCall = false;
632 so->curPageData = so->nPageData = 0;
633 scan->xs_hitup = NULL;
634 if (so->pageDataCxt)
636
637 fakeItem.blkno = GIST_ROOT_BLKNO;
638 memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
639 gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
640 }
641
642 if (scan->numberOfOrderBys > 0)
643 {
644 /* Must fetch tuples in strict distance order */
645 return getNextNearest(scan);
646 }
647 else
648 {
649 /* Fetch tuples index-page-at-a-time */
650 for (;;)
651 {
652 if (so->curPageData < so->nPageData)
653 {
654 if (scan->kill_prior_tuple && so->curPageData > 0)
655 {
656
657 if (so->killedItems == NULL)
658 {
659 MemoryContext oldCxt =
661
662 so->killedItems =
664 * sizeof(OffsetNumber));
665
666 MemoryContextSwitchTo(oldCxt);
667 }
669 so->killedItems[so->numKilled++] =
670 so->pageData[so->curPageData - 1].offnum;
671 }
672 /* continuing to return tuples from a leaf page */
673 scan->xs_heaptid = so->pageData[so->curPageData].heapPtr;
674 scan->xs_recheck = so->pageData[so->curPageData].recheck;
675
676 /* in an index-only scan, also return the reconstructed tuple */
677 if (scan->xs_want_itup)
678 scan->xs_hitup = so->pageData[so->curPageData].recontup;
679
680 so->curPageData++;
681
682 return true;
683 }
684
685 /*
686 * Check the last returned tuple and add it to killedItems if
687 * necessary
688 */
689 if (scan->kill_prior_tuple
690 && so->curPageData > 0
691 && so->curPageData == so->nPageData)
692 {
693
694 if (so->killedItems == NULL)
695 {
696 MemoryContext oldCxt =
698
699 so->killedItems =
701 * sizeof(OffsetNumber));
702
703 MemoryContextSwitchTo(oldCxt);
704 }
706 so->killedItems[so->numKilled++] =
707 so->pageData[so->curPageData - 1].offnum;
708 }
709 /* find and process the next index page */
710 do
711 {
712 GISTSearchItem *item;
713
714 if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
715 gistkillitems(scan);
716
717 item = getNextGISTSearchItem(so);
718
719 if (!item)
720 return false;
721
723
724 /* save current item BlockNumber for next gistkillitems() call */
725 so->curBlkno = item->blkno;
726
727 /*
728 * While scanning a leaf page, ItemPointers of matching heap
729 * tuples are stored in so->pageData. If there are any on
730 * this page, we fall out of the inner "do" and loop around to
731 * return them.
732 */
733 gistScanPage(scan, item, item->distances, NULL, NULL);
734
735 pfree(item);
736 } while (so->nPageData == 0);
737 }
738 }
739}
static bool getNextNearest(IndexScanDesc scan)
Definition: gistget.c:560
static void gistkillitems(IndexScanDesc scan)
Definition: gistget.c:38
#define MaxIndexTuplesPerPage
Definition: itup.h:181
@ ForwardScanDirection
Definition: sdir.h:28
OffsetNumber * killedItems
Definition: gist_private.h:168
GISTSTATE * giststate
Definition: gist_private.h:156
GISTSearchHeapItem pageData[BLCKSZ/sizeof(IndexTupleData)]
Definition: gist_private.h:174
BlockNumber curBlkno
Definition: gist_private.h:170
ItemPointerData heapPtr
Definition: gist_private.h:120
OffsetNumber offnum
Definition: gist_private.h:125
BlockNumber blkno
Definition: gist_private.h:133
GistNSN parentlsn
Definition: gist_private.h:136
union GISTSearchItem::@45 data
int numberOfOrderBys
Definition: relscan.h:140
bool kill_prior_tuple
Definition: relscan.h:147
ItemPointerData xs_heaptid
Definition: relscan.h:172

References GISTSearchItem::blkno, CHECK_FOR_INTERRUPTS, GISTScanOpaqueData::curBlkno, GISTScanOpaqueData::curPageData, GISTSearchItem::data, GISTSearchItem::distances, elog, ERROR, GISTScanOpaqueData::firstCall, ForwardScanDirection, getNextGISTSearchItem(), getNextNearest(), GIST_ROOT_BLKNO, gistkillitems(), gistScanPage(), GISTScanOpaqueData::giststate, GISTSearchHeapItem::heapPtr, if(), IndexScanDescData::indexRelation, IndexScanDescData::instrument, InvalidBlockNumber, IndexScanDescData::kill_prior_tuple, GISTScanOpaqueData::killedItems, MaxIndexTuplesPerPage, MemoryContextReset(), MemoryContextSwitchTo(), GISTScanOpaqueData::nPageData, IndexScanInstrumentation::nsearches, IndexScanDescData::numberOfOrderBys, GISTScanOpaqueData::numKilled, GISTSearchHeapItem::offnum, IndexScanDescData::opaque, GISTScanOpaqueData::pageData, GISTScanOpaqueData::pageDataCxt, palloc(), GISTSearchItem::parentlsn, pfree(), pgstat_count_index_scan, GISTScanOpaqueData::qual_ok, GISTSearchHeapItem::recheck, GISTSearchHeapItem::recontup, GISTSTATE::scanCxt, IndexScanDescData::xs_heaptid, IndexScanDescData::xs_hitup, IndexScanDescData::xs_recheck, and IndexScanDescData::xs_want_itup.

Referenced by gisthandler().

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 773 of file gistutil.c.

774{
775 Page page;
776
777 page = BufferGetPage(b);
778 gistinitpage(page, f);
779}
void gistinitpage(Page page, uint32 f)
Definition: gistutil.c:757
int b
Definition: isn.c:74

References b, BufferGetPage(), and gistinitpage().

Referenced by gistbuild(), gistbuildempty(), gistplacetopage(), and gistRedoPageSplitRecord().

◆ gistInitBuildBuffers()

GISTBuildBuffers * gistInitBuildBuffers ( int  pagesPerBuffer,
int  levelStep,
int  maxLevel 
)

Definition at line 44 of file gistbuildbuffers.c.

45{
46 GISTBuildBuffers *gfbb;
47 HASHCTL hashCtl;
48
49 gfbb = palloc(sizeof(GISTBuildBuffers));
50 gfbb->pagesPerBuffer = pagesPerBuffer;
51 gfbb->levelStep = levelStep;
52
53 /*
54 * Create a temporary file to hold buffer pages that are swapped out of
55 * memory.
56 */
57 gfbb->pfile = BufFileCreateTemp(false);
58 gfbb->nFileBlocks = 0;
59
60 /* Initialize free page management. */
61 gfbb->nFreeBlocks = 0;
62 gfbb->freeBlocksLen = 32;
63 gfbb->freeBlocks = (long *) palloc(gfbb->freeBlocksLen * sizeof(long));
64
65 /*
66 * Current memory context will be used for all in-memory data structures
67 * of buffers which are persistent during buffering build.
68 */
70
71 /*
72 * nodeBuffersTab hash is association between index blocks and it's
73 * buffers.
74 */
75 hashCtl.keysize = sizeof(BlockNumber);
76 hashCtl.entrysize = sizeof(GISTNodeBuffer);
77 hashCtl.hcxt = CurrentMemoryContext;
78 gfbb->nodeBuffersTab = hash_create("gistbuildbuffers",
79 1024,
80 &hashCtl,
82
84
85 /*
86 * Per-level node buffers lists for final buffers emptying process. Node
87 * buffers are inserted here when they are created.
88 */
89 gfbb->buffersOnLevelsLen = 1;
90 gfbb->buffersOnLevels = (List **) palloc(sizeof(List *) *
91 gfbb->buffersOnLevelsLen);
92 gfbb->buffersOnLevels[0] = NIL;
93
94 /*
95 * Block numbers of node buffers which last pages are currently loaded
96 * into main memory.
97 */
98 gfbb->loadedBuffersLen = 32;
100 sizeof(GISTNodeBuffer *));
101 gfbb->loadedBuffersCount = 0;
102
103 gfbb->rootlevel = maxLevel;
104
105 return gfbb;
106}
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:193
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
List * bufferEmptyingQueue
Definition: gist_private.h:357
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86

References GISTBuildBuffers::bufferEmptyingQueue, GISTBuildBuffers::buffersOnLevels, GISTBuildBuffers::buffersOnLevelsLen, BufFileCreateTemp(), GISTBuildBuffers::context, CurrentMemoryContext, HASHCTL::entrysize, GISTBuildBuffers::freeBlocks, GISTBuildBuffers::freeBlocksLen, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, HASHCTL::keysize, GISTBuildBuffers::levelStep, GISTBuildBuffers::loadedBuffers, GISTBuildBuffers::loadedBuffersCount, GISTBuildBuffers::loadedBuffersLen, GISTBuildBuffers::nFileBlocks, GISTBuildBuffers::nFreeBlocks, NIL, GISTBuildBuffers::nodeBuffersTab, GISTBuildBuffers::pagesPerBuffer, palloc(), GISTBuildBuffers::pfile, and GISTBuildBuffers::rootlevel.

Referenced by gistInitBuffering().

◆ gistinitpage()

void gistinitpage ( Page  page,
uint32  f 
)

Definition at line 757 of file gistutil.c.

758{
759 GISTPageOpaque opaque;
760
761 PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData));
762
763 opaque = GistPageGetOpaque(page);
765 opaque->flags = f;
766 opaque->gist_page_id = GIST_PAGE_ID;
767}
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42
#define GIST_PAGE_ID
Definition: gist.h:112
#define GistPageGetOpaque(page)
Definition: gist.h:168
uint16 gist_page_id
Definition: gist.h:83
BlockNumber rightlink
Definition: gist.h:81
uint16 flags
Definition: gist.h:82

References GISTPageOpaqueData::flags, GISTPageOpaqueData::gist_page_id, GIST_PAGE_ID, GistPageGetOpaque, InvalidBlockNumber, PageInit(), and GISTPageOpaqueData::rightlink.

Referenced by gist_indexsortbuild(), gist_indexsortbuild_levelstate_add(), gist_indexsortbuild_levelstate_flush(), and GISTInitBuffer().

◆ gistinsert()

bool gistinsert ( Relation  r,
Datum values,
bool *  isnull,
ItemPointer  ht_ctid,
Relation  heapRel,
IndexUniqueCheck  checkUnique,
bool  indexUnchanged,
struct IndexInfo indexInfo 
)

Definition at line 165 of file gist.c.

170{
171 GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
172 IndexTuple itup;
173 MemoryContext oldCxt;
174
175 /* Initialize GISTSTATE cache if first call in this statement */
176 if (giststate == NULL)
177 {
178 oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
179 giststate = initGISTstate(r);
180 giststate->tempCxt = createTempGistContext();
181 indexInfo->ii_AmCache = giststate;
182 MemoryContextSwitchTo(oldCxt);
183 }
184
185 oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
186
187 itup = gistFormTuple(giststate, r, values, isnull, true);
188 itup->t_tid = *ht_ctid;
189
190 gistdoinsert(r, itup, 0, giststate, heapRel, false);
191
192 /* cleanup */
193 MemoryContextSwitchTo(oldCxt);
194 MemoryContextReset(giststate->tempCxt);
195
196 return false;
197}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
Definition: gist.c:639
void * ii_AmCache
Definition: execnodes.h:220
MemoryContext ii_Context
Definition: execnodes.h:221

References createTempGistContext(), gistdoinsert(), gistFormTuple(), if(), IndexInfo::ii_AmCache, IndexInfo::ii_Context, initGISTstate(), MemoryContextReset(), MemoryContextSwitchTo(), GISTSTATE::tempCxt, and values.

Referenced by gisthandler().

◆ gistjoinvector()

IndexTuple * gistjoinvector ( IndexTuple itvec,
int *  len,
IndexTuple additvec,
int  addlen 
)

Definition at line 114 of file gistutil.c.

115{
116 itvec = (IndexTuple *) repalloc(itvec, sizeof(IndexTuple) * ((*len) + addlen));
117 memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
118 *len += addlen;
119 return itvec;
120}

References len, and repalloc().

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistKeyIsEQ()

bool gistKeyIsEQ ( GISTSTATE giststate,
int  attno,
Datum  a,
Datum  b 
)

Definition at line 281 of file gistutil.c.

282{
283 bool result = false; /* silence compiler warning */
284
285 FunctionCall3Coll(&giststate->equalFn[attno],
286 giststate->supportCollation[attno],
287 a, b,
288 PointerGetDatum(&result));
289 return result;
290}
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1171
int a
Definition: isn.c:73
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92

References a, b, GISTSTATE::equalFn, FunctionCall3Coll(), PointerGetDatum(), and GISTSTATE::supportCollation.

Referenced by gistgetadjusted(), and gistUserPicksplit().

◆ gistMakeUnionItVec()

void gistMakeUnionItVec ( GISTSTATE giststate,
IndexTuple itvec,
int  len,
Datum attr,
bool *  isnull 
)

Definition at line 155 of file gistutil.c.

157{
158 int i;
159 GistEntryVector *evec;
160 int attrsize;
161
162 evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
163
164 for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
165 {
166 int j;
167
168 /* Collect non-null datums for this column */
169 evec->n = 0;
170 for (j = 0; j < len; j++)
171 {
172 Datum datum;
173 bool IsNull;
174
175 datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
176 &IsNull);
177 if (IsNull)
178 continue;
179
180 gistdentryinit(giststate, i,
181 evec->vector + evec->n,
182 datum,
183 NULL, NULL, (OffsetNumber) 0,
184 false, IsNull);
185 evec->n++;
186 }
187
188 /* If this column was all NULLs, the union is NULL */
189 if (evec->n == 0)
190 {
191 attr[i] = (Datum) 0;
192 isnull[i] = true;
193 }
194 else
195 {
196 if (evec->n == 1)
197 {
198 /* unionFn may expect at least two inputs */
199 evec->n = 2;
200 evec->vector[1] = evec->vector[0];
201 }
202
203 /* Make union and store in attr array */
204 attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
205 giststate->supportCollation[i],
206 PointerGetDatum(evec),
207 PointerGetDatum(&attrsize));
208
209 isnull[i] = false;
210 }
211 }
212}
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
struct GISTENTRY GISTENTRY
#define GEVHDRSZ
Definition: gist.h:240
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:237
int32 n
Definition: gist.h:236

References FunctionCall2Coll(), GEVHDRSZ, gistdentryinit(), i, index_getattr(), j, GISTSTATE::leafTupdesc, len, GistEntryVector::n, TupleDescData::natts, GISTSTATE::nonLeafTupdesc, palloc(), PointerGetDatum(), GISTSTATE::supportCollation, GISTSTATE::unionFn, and GistEntryVector::vector.

Referenced by gistunion(), and gistunionsubkeyvec().

◆ gistMakeUnionKey()

void gistMakeUnionKey ( GISTSTATE giststate,
int  attno,
GISTENTRY entry1,
bool  isnull1,
GISTENTRY entry2,
bool  isnull2,
Datum dst,
bool *  dstisnull 
)

Definition at line 233 of file gistutil.c.

237{
238 /* we need a GistEntryVector with room for exactly 2 elements */
239 union
240 {
241 GistEntryVector gev;
242 char padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
243 } storage;
244 GistEntryVector *evec = &storage.gev;
245 int dstsize;
246
247 evec->n = 2;
248
249 if (isnull1 && isnull2)
250 {
251 *dstisnull = true;
252 *dst = (Datum) 0;
253 }
254 else
255 {
256 if (isnull1 == false && isnull2 == false)
257 {
258 evec->vector[0] = *entry1;
259 evec->vector[1] = *entry2;
260 }
261 else if (isnull1 == false)
262 {
263 evec->vector[0] = *entry1;
264 evec->vector[1] = *entry1;
265 }
266 else
267 {
268 evec->vector[0] = *entry2;
269 evec->vector[1] = *entry2;
270 }
271
272 *dstisnull = false;
273 *dst = FunctionCall2Coll(&giststate->unionFn[attno],
274 giststate->supportCollation[attno],
275 PointerGetDatum(evec),
276 PointerGetDatum(&dstsize));
277 }
278}
#define storage
Definition: indent_codes.h:68

References FunctionCall2Coll(), GEVHDRSZ, GistEntryVector::n, PointerGetDatum(), storage, GISTSTATE::supportCollation, GISTSTATE::unionFn, and GistEntryVector::vector.

Referenced by gistgetadjusted(), and supportSecondarySplit().

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r,
Relation  heaprel 
)

Definition at line 824 of file gistutil.c.

825{
826 Buffer buffer;
827
828 /* First, try to get a page from FSM */
829 for (;;)
830 {
831 BlockNumber blkno = GetFreeIndexPage(r);
832
833 if (blkno == InvalidBlockNumber)
834 break; /* nothing left in FSM */
835
836 buffer = ReadBuffer(r, blkno);
837
838 /*
839 * We have to guard against the possibility that someone else already
840 * recycled this page; the buffer may be locked if so.
841 */
842 if (ConditionalLockBuffer(buffer))
843 {
844 Page page = BufferGetPage(buffer);
845
846 /*
847 * If the page was never initialized, it's OK to use.
848 */
849 if (PageIsNew(page))
850 return buffer;
851
852 gistcheckpage(r, buffer);
853
854 /*
855 * Otherwise, recycle it if deleted, and too old to have any
856 * processes interested in it.
857 */
858 if (gistPageRecyclable(page))
859 {
860 /*
861 * If we are generating WAL for Hot Standby then create a WAL
862 * record that will allow us to conflict with queries running
863 * on standby, in case they have snapshots older than the
864 * page's deleteXid.
865 */
867 gistXLogPageReuse(r, heaprel, blkno, GistPageGetDeleteXid(page));
868
869 return buffer;
870 }
871
872 LockBuffer(buffer, GIST_UNLOCK);
873 }
874
875 /* Can't use it, so release buffer and try again */
876 ReleaseBuffer(buffer);
877 }
878
879 /* Must extend the file */
880 buffer = ExtendBufferedRel(BMR_REL(r), MAIN_FORKNUM, NULL,
882
883 return buffer;
884}
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:5633
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:216
bool gistPageRecyclable(Page page)
Definition: gistutil.c:888
void gistXLogPageReuse(Relation rel, Relation heaprel, BlockNumber blkno, FullTransactionId deleteXid)
Definition: gistxlog.c:594
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
#define XLogStandbyInfoActive()
Definition: xlog.h:123

References BMR_REL, BufferGetPage(), ConditionalLockBuffer(), EB_LOCK_FIRST, ExtendBufferedRel(), GetFreeIndexPage(), GIST_UNLOCK, gistcheckpage(), GistPageGetDeleteXid(), gistPageRecyclable(), gistXLogPageReuse(), InvalidBlockNumber, LockBuffer(), MAIN_FORKNUM, PageIsNew(), ReadBuffer(), RelationNeedsWAL, ReleaseBuffer(), and XLogStandbyInfoActive.

Referenced by gistbuild(), and gistplacetopage().

◆ gistnospace()

bool gistnospace ( Page  page,
IndexTuple itvec,
int  len,
OffsetNumber  todelete,
Size  freespace 
)

Definition at line 59 of file gistutil.c.

60{
61 unsigned int size = freespace,
62 deleted = 0;
63 int i;
64
65 for (i = 0; i < len; i++)
66 size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
67
68 if (todelete != InvalidOffsetNumber)
69 {
70 IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
71
72 deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
73 }
74
75 return (PageGetFreeSpace(page) + deleted < size);
76}
Size PageGetFreeSpace(const PageData *page)
Definition: bufpage.c:906

References i, IndexTupleSize(), InvalidOffsetNumber, len, PageGetFreeSpace(), PageGetItem(), and PageGetItemId().

Referenced by gistplacetopage().

◆ gistoptions()

bytea * gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 912 of file gistutil.c.

913{
914 static const relopt_parse_elt tab[] = {
915 {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
916 {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
917 };
918
919 return (bytea *) build_reloptions(reloptions, validate,
921 sizeof(GiSTOptions),
922 tab, lengthof(tab));
923}
static bool validate(Port *port, const char *auth)
Definition: auth-oauth.c:638
#define lengthof(array)
Definition: c.h:759
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
Definition: reloptions.c:1934
@ RELOPT_KIND_GIST
Definition: reloptions.h:47
@ RELOPT_TYPE_ENUM
Definition: reloptions.h:34
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:658

References build_reloptions(), fillfactor, lengthof, RELOPT_KIND_GIST, RELOPT_TYPE_ENUM, RELOPT_TYPE_INT, and validate().

Referenced by gisthandler().

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 888 of file gistutil.c.

889{
890 if (PageIsNew(page))
891 return true;
892 if (GistPageIsDeleted(page))
893 {
894 /*
895 * The page was deleted, but when? If it was just deleted, a scan
896 * might have seen the downlink to it, and will read the page later.
897 * As long as that can happen, we must keep the deleted page around as
898 * a tombstone.
899 *
900 * For that check if the deletion XID could still be visible to
901 * anyone. If not, then no scan that's still in progress could have
902 * seen its downlink, and we can recycle it.
903 */
904 FullTransactionId deletexid_full = GistPageGetDeleteXid(page);
905
906 return GlobalVisCheckRemovableFullXid(NULL, deletexid_full);
907 }
908 return false;
909}
bool GlobalVisCheckRemovableFullXid(Relation rel, FullTransactionId fxid)
Definition: procarray.c:4286

References GistPageGetDeleteXid(), GistPageIsDeleted, GlobalVisCheckRemovableFullXid(), and PageIsNew().

Referenced by gistNewBuffer(), and gistvacuumpage().

◆ gistpenalty()

float gistpenalty ( GISTSTATE giststate,
int  attno,
GISTENTRY orig,
bool  isNullOrig,
GISTENTRY add,
bool  isNullAdd 
)

Definition at line 724 of file gistutil.c.

727{
728 float penalty = 0.0;
729
730 if (giststate->penaltyFn[attno].fn_strict == false ||
731 (isNullOrig == false && isNullAdd == false))
732 {
733 FunctionCall3Coll(&giststate->penaltyFn[attno],
734 giststate->supportCollation[attno],
735 PointerGetDatum(orig),
736 PointerGetDatum(add),
737 PointerGetDatum(&penalty));
738 /* disallow negative or NaN penalty */
739 if (isnan(penalty) || penalty < 0.0)
740 penalty = 0.0;
741 }
742 else if (isNullOrig && isNullAdd)
743 penalty = 0.0;
744 else
745 {
746 /* try to prevent mixing null and non-null values */
747 penalty = get_float4_infinity();
748 }
749
750 return penalty;
751}
static float4 get_float4_infinity(void)
Definition: float.h:74
bool fn_strict
Definition: fmgr.h:61
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:90

References FmgrInfo::fn_strict, FunctionCall3Coll(), get_float4_infinity(), GISTSTATE::penaltyFn, PointerGetDatum(), and GISTSTATE::supportCollation.

Referenced by findDontCares(), gistchoose(), gistRelocateBuildBuffersOnSplit(), placeOne(), and supportSecondarySplit().

◆ gistplacetopage()

bool gistplacetopage ( Relation  rel,
Size  freespace,
GISTSTATE giststate,
Buffer  buffer,
IndexTuple itup,
int  ntup,
OffsetNumber  oldoffnum,
BlockNumber newblkno,
Buffer  leftchildbuf,
List **  splitinfo,
bool  markfollowright,
Relation  heapRel,
bool  is_build 
)

Definition at line 230 of file gist.c.

239{
240 BlockNumber blkno = BufferGetBlockNumber(buffer);
241 Page page = BufferGetPage(buffer);
242 bool is_leaf = (GistPageIsLeaf(page)) ? true : false;
243 XLogRecPtr recptr;
244 bool is_split;
245
246 /*
247 * Refuse to modify a page that's incompletely split. This should not
248 * happen because we finish any incomplete splits while we walk down the
249 * tree. However, it's remotely possible that another concurrent inserter
250 * splits a parent page, and errors out before completing the split. We
251 * will just throw an error in that case, and leave any split we had in
252 * progress unfinished too. The next insert that comes along will clean up
253 * the mess.
254 */
255 if (GistFollowRight(page))
256 elog(ERROR, "concurrent GiST page split was incomplete");
257
258 /* should never try to insert to a deleted page */
260
261 *splitinfo = NIL;
262
263 /*
264 * if isupdate, remove old key: This node's key has been modified, either
265 * because a child split occurred or because we needed to adjust our key
266 * for an insert in a child node. Therefore, remove the old version of
267 * this node's key.
268 *
269 * for WAL replay, in the non-split case we handle this by setting up a
270 * one-element todelete array; in the split case, it's handled implicitly
271 * because the tuple vector passed to gistSplit won't include this tuple.
272 */
273 is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
274
275 /*
276 * If leaf page is full, try at first to delete dead tuples. And then
277 * check again.
278 */
279 if (is_split && GistPageIsLeaf(page) && GistPageHasGarbage(page))
280 {
281 gistprunepage(rel, page, buffer, heapRel);
282 is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
283 }
284
285 if (is_split)
286 {
287 /* no space for insertion */
288 IndexTuple *itvec;
289 int tlen;
290 SplitPageLayout *dist = NULL,
291 *ptr;
293 GistNSN oldnsn = 0;
294 SplitPageLayout rootpg;
295 bool is_rootsplit;
296 int npage;
297
298 is_rootsplit = (blkno == GIST_ROOT_BLKNO);
299
300 /*
301 * Form index tuples vector to split. If we're replacing an old tuple,
302 * remove the old version from the vector.
303 */
304 itvec = gistextractpage(page, &tlen);
305 if (OffsetNumberIsValid(oldoffnum))
306 {
307 /* on inner page we should remove old tuple */
308 int pos = oldoffnum - FirstOffsetNumber;
309
310 tlen--;
311 if (pos != tlen)
312 memmove(itvec + pos, itvec + pos + 1, sizeof(IndexTuple) * (tlen - pos));
313 }
314 itvec = gistjoinvector(itvec, &tlen, itup, ntup);
315 dist = gistSplit(rel, page, itvec, tlen, giststate);
316
317 /*
318 * Check that split didn't produce too many pages.
319 */
320 npage = 0;
321 for (ptr = dist; ptr; ptr = ptr->next)
322 npage++;
323 /* in a root split, we'll add one more page to the list below */
324 if (is_rootsplit)
325 npage++;
326 if (npage > GIST_MAX_SPLIT_PAGES)
327 elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
328 npage, GIST_MAX_SPLIT_PAGES);
329
330 /*
331 * Set up pages to work with. Allocate new buffers for all but the
332 * leftmost page. The original page becomes the new leftmost page, and
333 * is just replaced with the new contents.
334 *
335 * For a root-split, allocate new buffers for all child pages, the
336 * original page is overwritten with new root page containing
337 * downlinks to the new child pages.
338 */
339 ptr = dist;
340 if (!is_rootsplit)
341 {
342 /* save old rightlink and NSN */
343 oldrlink = GistPageGetOpaque(page)->rightlink;
344 oldnsn = GistPageGetNSN(page);
345
346 dist->buffer = buffer;
347 dist->block.blkno = BufferGetBlockNumber(buffer);
349
350 /* clean all flags except F_LEAF */
351 GistPageGetOpaque(dist->page)->flags = (is_leaf) ? F_LEAF : 0;
352
353 ptr = ptr->next;
354 }
355 for (; ptr; ptr = ptr->next)
356 {
357 /* Allocate new page */
358 ptr->buffer = gistNewBuffer(rel, heapRel);
359 GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
360 ptr->page = BufferGetPage(ptr->buffer);
361 ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
363 BufferGetBlockNumber(buffer),
364 BufferGetBlockNumber(ptr->buffer));
365 }
366
367 /*
368 * Now that we know which blocks the new pages go to, set up downlink
369 * tuples to point to them.
370 */
371 for (ptr = dist; ptr; ptr = ptr->next)
372 {
373 ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
374 GistTupleSetValid(ptr->itup);
375 }
376
377 /*
378 * If this is a root split, we construct the new root page with the
379 * downlinks here directly, instead of requiring the caller to insert
380 * them. Add the new root page to the list along with the child pages.
381 */
382 if (is_rootsplit)
383 {
384 IndexTuple *downlinks;
385 int ndownlinks = 0;
386 int i;
387
388 rootpg.buffer = buffer;
390 GistPageGetOpaque(rootpg.page)->flags = 0;
391
392 /* Prepare a vector of all the downlinks */
393 for (ptr = dist; ptr; ptr = ptr->next)
394 ndownlinks++;
395 downlinks = palloc(sizeof(IndexTuple) * ndownlinks);
396 for (i = 0, ptr = dist; ptr; ptr = ptr->next)
397 downlinks[i++] = ptr->itup;
398
399 rootpg.block.blkno = GIST_ROOT_BLKNO;
400 rootpg.block.num = ndownlinks;
401 rootpg.list = gistfillitupvec(downlinks, ndownlinks,
402 &(rootpg.lenlist));
403 rootpg.itup = NULL;
404
405 rootpg.next = dist;
406 dist = &rootpg;
407 }
408 else
409 {
410 /* Prepare split-info to be returned to caller */
411 for (ptr = dist; ptr; ptr = ptr->next)
412 {
414
415 si->buf = ptr->buffer;
416 si->downlink = ptr->itup;
417 *splitinfo = lappend(*splitinfo, si);
418 }
419 }
420
421 /*
422 * Fill all pages. All the pages are new, ie. freshly allocated empty
423 * pages, or a temporary copy of the old page.
424 */
425 for (ptr = dist; ptr; ptr = ptr->next)
426 {
427 char *data = (char *) (ptr->list);
428
429 for (int i = 0; i < ptr->block.num; i++)
430 {
431 IndexTuple thistup = (IndexTuple) data;
432
433 if (PageAddItem(ptr->page, (Item) data, IndexTupleSize(thistup), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
434 elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(rel));
435
436 /*
437 * If this is the first inserted/updated tuple, let the caller
438 * know which page it landed on.
439 */
440 if (newblkno && ItemPointerEquals(&thistup->t_tid, &(*itup)->t_tid))
441 *newblkno = ptr->block.blkno;
442
443 data += IndexTupleSize(thistup);
444 }
445
446 /* Set up rightlinks */
447 if (ptr->next && ptr->block.blkno != GIST_ROOT_BLKNO)
448 GistPageGetOpaque(ptr->page)->rightlink =
449 ptr->next->block.blkno;
450 else
451 GistPageGetOpaque(ptr->page)->rightlink = oldrlink;
452
453 /*
454 * Mark the all but the right-most page with the follow-right
455 * flag. It will be cleared as soon as the downlink is inserted
456 * into the parent, but this ensures that if we error out before
457 * that, the index is still consistent. (in buffering build mode,
458 * any error will abort the index build anyway, so this is not
459 * needed.)
460 */
461 if (ptr->next && !is_rootsplit && markfollowright)
462 GistMarkFollowRight(ptr->page);
463 else
464 GistClearFollowRight(ptr->page);
465
466 /*
467 * Copy the NSN of the original page to all pages. The
468 * F_FOLLOW_RIGHT flags ensure that scans will follow the
469 * rightlinks until the downlinks are inserted.
470 */
471 GistPageSetNSN(ptr->page, oldnsn);
472 }
473
474 /*
475 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
476 * insertion for that. NB: The number of pages and data segments
477 * specified here must match the calculations in gistXLogSplit()!
478 */
479 if (!is_build && RelationNeedsWAL(rel))
480 XLogEnsureRecordSpace(npage, 1 + npage * 2);
481
483
484 /*
485 * Must mark buffers dirty before XLogInsert, even though we'll still
486 * be changing their opaque fields below.
487 */
488 for (ptr = dist; ptr; ptr = ptr->next)
489 MarkBufferDirty(ptr->buffer);
490 if (BufferIsValid(leftchildbuf))
491 MarkBufferDirty(leftchildbuf);
492
493 /*
494 * The first page in the chain was a temporary working copy meant to
495 * replace the old page. Copy it over the old page.
496 */
498 dist->page = BufferGetPage(dist->buffer);
499
500 /*
501 * Write the WAL record.
502 *
503 * If we're building a new index, however, we don't WAL-log changes
504 * yet. The LSN-NSN interlock between parent and child requires that
505 * LSNs never move backwards, so set the LSNs to a value that's
506 * smaller than any real or fake unlogged LSN that might be generated
507 * later. (There can't be any concurrent scans during index build, so
508 * we don't need to be able to detect concurrent splits yet.)
509 */
510 if (is_build)
511 recptr = GistBuildLSN;
512 else
513 {
514 if (RelationNeedsWAL(rel))
515 recptr = gistXLogSplit(is_leaf,
516 dist, oldrlink, oldnsn, leftchildbuf,
517 markfollowright);
518 else
519 recptr = gistGetFakeLSN(rel);
520 }
521
522 for (ptr = dist; ptr; ptr = ptr->next)
523 PageSetLSN(ptr->page, recptr);
524
525 /*
526 * Return the new child buffers to the caller.
527 *
528 * If this was a root split, we've already inserted the downlink
529 * pointers, in the form of a new root page. Therefore we can release
530 * all the new buffers, and keep just the root page locked.
531 */
532 if (is_rootsplit)
533 {
534 for (ptr = dist->next; ptr; ptr = ptr->next)
535 UnlockReleaseBuffer(ptr->buffer);
536 }
537 }
538 else
539 {
540 /*
541 * Enough space. We always get here if ntup==0.
542 */
544
545 /*
546 * Delete old tuple if any, then insert new tuple(s) if any. If
547 * possible, use the fast path of PageIndexTupleOverwrite.
548 */
549 if (OffsetNumberIsValid(oldoffnum))
550 {
551 if (ntup == 1)
552 {
553 /* One-for-one replacement, so use PageIndexTupleOverwrite */
554 if (!PageIndexTupleOverwrite(page, oldoffnum, (Item) *itup,
555 IndexTupleSize(*itup)))
556 elog(ERROR, "failed to add item to index page in \"%s\"",
558 }
559 else
560 {
561 /* Delete old, then append new tuple(s) to page */
562 PageIndexTupleDelete(page, oldoffnum);
563 gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
564 }
565 }
566 else
567 {
568 /* Just append new tuples at the end of the page */
569 gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
570 }
571
572 MarkBufferDirty(buffer);
573
574 if (BufferIsValid(leftchildbuf))
575 MarkBufferDirty(leftchildbuf);
576
577 if (is_build)
578 recptr = GistBuildLSN;
579 else
580 {
581 if (RelationNeedsWAL(rel))
582 {
583 OffsetNumber ndeloffs = 0,
584 deloffs[1];
585
586 if (OffsetNumberIsValid(oldoffnum))
587 {
588 deloffs[0] = oldoffnum;
589 ndeloffs = 1;
590 }
591
592 recptr = gistXLogUpdate(buffer,
593 deloffs, ndeloffs, itup, ntup,
594 leftchildbuf);
595 }
596 else
597 recptr = gistGetFakeLSN(rel);
598 }
599 PageSetLSN(page, recptr);
600
601 if (newblkno)
602 *newblkno = blkno;
603 }
604
605 /*
606 * If we inserted the downlink for a child page, set NSN and clear
607 * F_FOLLOW_RIGHT flag on the left child, so that concurrent scans know to
608 * follow the rightlink if and only if they looked at the parent page
609 * before we inserted the downlink.
610 *
611 * Note that we do this *after* writing the WAL record. That means that
612 * the possible full page image in the WAL record does not include these
613 * changes, and they must be replayed even if the page is restored from
614 * the full page image. There's a chicken-and-egg problem: if we updated
615 * the child pages first, we wouldn't know the recptr of the WAL record
616 * we're about to write.
617 */
618 if (BufferIsValid(leftchildbuf))
619 {
620 Page leftpg = BufferGetPage(leftchildbuf);
621
622 GistPageSetNSN(leftpg, recptr);
623 GistClearFollowRight(leftpg);
624
625 PageSetLSN(leftpg, recptr);
626 }
627
629
630 return is_split;
631}
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:368
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:423
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1404
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1051
Page PageGetTempPageCopySpecial(const PageData *page)
Definition: bufpage.c:401
SplitPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1450
static void gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
Definition: gist.c:1675
#define GistMarkFollowRight(page)
Definition: gist.h:184
#define GistClearFollowRight(page)
Definition: gist.h:185
#define GistPageSetNSN(page, val)
Definition: gist.h:188
#define GistPageHasGarbage(page)
Definition: gist.h:179
#define GIST_MAX_SPLIT_PAGES
Definition: gist_private.h:39
#define GistTupleSetValid(itup)
Definition: gist_private.h:289
bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
Definition: gistutil.c:59
void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
Definition: gistutil.c:34
IndexTuple * gistextractpage(Page page, int *len)
Definition: gistutil.c:95
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:1016
IndexTuple * gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
Definition: gistutil.c:114
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:127
XLogRecPtr gistXLogSplit(bool page_is_leaf, SplitPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
Definition: gistxlog.c:495
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:629
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static void ItemPointerSetBlockNumber(ItemPointerData *pointer, BlockNumber blockNumber)
Definition: itemptr.h:147
List * lappend(List *list, void *datum)
Definition: list.c:339
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
const void * data
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3144
IndexTuple downlink
Definition: gist_private.h:422
gistxlogPage block
Definition: gist_private.h:193
struct SplitPageLayout * next
Definition: gist_private.h:200
IndexTuple itup
Definition: gist_private.h:196
IndexTupleData * list
Definition: gist_private.h:194
BlockNumber blkno
Definition: gist_private.h:186
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:175

References Assert(), gistxlogPage::blkno, SplitPageLayout::block, GISTPageSplitInfo::buf, SplitPageLayout::buffer, BufferGetBlockNumber(), BufferGetPage(), BufferIsValid(), data, GISTPageSplitInfo::downlink, elog, END_CRIT_SECTION, ERROR, F_LEAF, FirstOffsetNumber, GIST_MAX_SPLIT_PAGES, GIST_ROOT_BLKNO, GistBuildLSN, GistClearFollowRight, gistextractpage(), gistfillbuffer(), gistfillitupvec(), GistFollowRight, gistGetFakeLSN(), GISTInitBuffer(), gistjoinvector(), GistMarkFollowRight, gistNewBuffer(), gistnospace(), GistPageGetNSN, GistPageGetOpaque, GistPageHasGarbage, GistPageIsDeleted, GistPageIsLeaf, GistPageSetNSN, gistprunepage(), gistSplit(), GistTupleSetValid, gistXLogSplit(), gistXLogUpdate(), i, IndexTupleSize(), InvalidBlockNumber, InvalidOffsetNumber, ItemPointerEquals(), ItemPointerSetBlockNumber(), SplitPageLayout::itup, lappend(), SplitPageLayout::lenlist, SplitPageLayout::list, MarkBufferDirty(), SplitPageLayout::next, NIL, gistxlogPage::num, OffsetNumberIsValid, SplitPageLayout::page, PageAddItem, PageGetTempPageCopySpecial(), PageIndexTupleDelete(), PageIndexTupleOverwrite(), PageRestoreTempPage(), PageSetLSN(), palloc(), PredicateLockPageSplit(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogEnsureRecordSpace().

Referenced by gistbufferinginserttuples(), and gistinserttuples().

◆ gistPopItupFromNodeBuffer()

bool gistPopItupFromNodeBuffer ( GISTBuildBuffers gfbb,
GISTNodeBuffer nodeBuffer,
IndexTuple itup 
)

Definition at line 406 of file gistbuildbuffers.c.

408{
409 /*
410 * If node buffer is empty then return false.
411 */
412 if (nodeBuffer->blocksCount <= 0)
413 return false;
414
415 /* Load last page of node buffer if needed */
416 if (!nodeBuffer->pageBuffer)
417 gistLoadNodeBuffer(gfbb, nodeBuffer);
418
419 /*
420 * Get index tuple from last non-empty page.
421 */
422 gistGetItupFromPage(nodeBuffer->pageBuffer, itup);
423
424 /*
425 * If we just removed the last tuple from the page, fetch previous page on
426 * this node buffer (if any).
427 */
428 if (PAGE_IS_EMPTY(nodeBuffer->pageBuffer))
429 {
430 BlockNumber prevblkno;
431
432 /*
433 * blocksCount includes the page in pageBuffer, so decrease it now.
434 */
435 nodeBuffer->blocksCount--;
436
437 /*
438 * If there's more pages, fetch previous one.
439 */
440 prevblkno = nodeBuffer->pageBuffer->prev;
441 if (prevblkno != InvalidBlockNumber)
442 {
443 /* There is a previous page. Fetch it. */
444 Assert(nodeBuffer->blocksCount > 0);
445 ReadTempFileBlock(gfbb->pfile, prevblkno, nodeBuffer->pageBuffer);
446
447 /*
448 * Now that we've read the block in memory, we can release its
449 * on-disk block for reuse.
450 */
451 gistBuffersReleaseBlock(gfbb, prevblkno);
452 }
453 else
454 {
455 /* No more pages. Free memory. */
456 Assert(nodeBuffer->blocksCount == 0);
457 pfree(nodeBuffer->pageBuffer);
458 nodeBuffer->pageBuffer = NULL;
459 }
460 }
461 return true;
462}
#define PAGE_IS_EMPTY(nbp)
Definition: gist_private.h:57
static void gistBuffersReleaseBlock(GISTBuildBuffers *gfbb, long blocknum)
static void gistLoadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
static void ReadTempFileBlock(BufFile *file, long blknum, void *ptr)
static void gistGetItupFromPage(GISTNodeBufferPage *pageBuffer, IndexTuple *itup)
BlockNumber prev
Definition: gist_private.h:48

References Assert(), GISTNodeBuffer::blocksCount, gistBuffersReleaseBlock(), gistGetItupFromPage(), gistLoadNodeBuffer(), InvalidBlockNumber, PAGE_IS_EMPTY, GISTNodeBuffer::pageBuffer, GISTBuildBuffers::pfile, pfree(), GISTNodeBufferPage::prev, and ReadTempFileBlock().

Referenced by gistProcessEmptyingQueue(), and gistRelocateBuildBuffersOnSplit().

◆ gistproperty()

bool gistproperty ( Oid  index_oid,
int  attno,
IndexAMProperty  prop,
const char *  propname,
bool *  res,
bool *  isnull 
)

Definition at line 933 of file gistutil.c.

936{
937 Oid opclass,
938 opfamily,
939 opcintype;
940 int16 procno;
941
942 /* Only answer column-level inquiries */
943 if (attno == 0)
944 return false;
945
946 /*
947 * Currently, GiST distance-ordered scans require that there be a distance
948 * function in the opclass with the default types (i.e. the one loaded
949 * into the relcache entry, see initGISTstate). So we assume that if such
950 * a function exists, then there's a reason for it (rather than grubbing
951 * through all the opfamily's operators to find an ordered one).
952 *
953 * Essentially the same code can test whether we support returning the
954 * column data, since that's true if the opclass provides a fetch proc.
955 */
956
957 switch (prop)
958 {
960 procno = GIST_DISTANCE_PROC;
961 break;
963 procno = GIST_FETCH_PROC;
964 break;
965 default:
966 return false;
967 }
968
969 /* First we need to know the column's opclass. */
970 opclass = get_index_column_opclass(index_oid, attno);
971 if (!OidIsValid(opclass))
972 {
973 *isnull = true;
974 return true;
975 }
976
977 /* Now look up the opclass family and input datatype. */
978 if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
979 {
980 *isnull = true;
981 return true;
982 }
983
984 /* And now we can check whether the function is provided. */
985
986 *res = SearchSysCacheExists4(AMPROCNUM,
987 ObjectIdGetDatum(opfamily),
988 ObjectIdGetDatum(opcintype),
989 ObjectIdGetDatum(opcintype),
990 Int16GetDatum(procno));
991
992 /*
993 * Special case: even without a fetch function, AMPROP_RETURNABLE is true
994 * if the opclass has no compress function.
995 */
996 if (prop == AMPROP_RETURNABLE && !*res)
997 {
998 *res = !SearchSysCacheExists4(AMPROCNUM,
999 ObjectIdGetDatum(opfamily),
1000 ObjectIdGetDatum(opcintype),
1001 ObjectIdGetDatum(opcintype),
1003 }
1004
1005 *isnull = false;
1006
1007 return true;
1008}
@ AMPROP_DISTANCE_ORDERABLE
Definition: amapi.h:44
@ AMPROP_RETURNABLE
Definition: amapi.h:45
int16_t int16
Definition: c.h:497
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1327
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3652
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:177
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
#define SearchSysCacheExists4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:106

References AMPROP_DISTANCE_ORDERABLE, AMPROP_RETURNABLE, get_index_column_opclass(), get_opclass_opfamily_and_input_type(), GIST_COMPRESS_PROC, GIST_DISTANCE_PROC, GIST_FETCH_PROC, Int16GetDatum(), ObjectIdGetDatum(), OidIsValid, and SearchSysCacheExists4.

Referenced by gisthandler().

◆ gistPushItupToNodeBuffer()

void gistPushItupToNodeBuffer ( GISTBuildBuffers gfbb,
GISTNodeBuffer nodeBuffer,
IndexTuple  itup 
)

Definition at line 336 of file gistbuildbuffers.c.

338{
339 /*
340 * Most part of memory operations will be in buffering build persistent
341 * context. So, let's switch to it.
342 */
344
345 /*
346 * If the buffer is currently empty, create the first page.
347 */
348 if (nodeBuffer->blocksCount == 0)
349 {
350 nodeBuffer->pageBuffer = gistAllocateNewPageBuffer(gfbb);
351 nodeBuffer->blocksCount = 1;
352 gistAddLoadedBuffer(gfbb, nodeBuffer);
353 }
354
355 /* Load last page of node buffer if it wasn't in memory already */
356 if (!nodeBuffer->pageBuffer)
357 gistLoadNodeBuffer(gfbb, nodeBuffer);
358
359 /*
360 * Check if there is enough space on the last page for the tuple.
361 */
362 if (PAGE_NO_SPACE(nodeBuffer->pageBuffer, itup))
363 {
364 /*
365 * Nope. Swap previous block to disk and allocate a new one.
366 */
367 BlockNumber blkno;
368
369 /* Write filled page to the disk */
370 blkno = gistBuffersGetFreeBlock(gfbb);
371 WriteTempFileBlock(gfbb->pfile, blkno, nodeBuffer->pageBuffer);
372
373 /*
374 * Reset the in-memory page as empty, and link the previous block to
375 * the new page by storing its block number in the prev-link.
376 */
377 PAGE_FREE_SPACE(nodeBuffer->pageBuffer) =
378 BLCKSZ - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
379 nodeBuffer->pageBuffer->prev = blkno;
380
381 /* We've just added one more page */
382 nodeBuffer->blocksCount++;
383 }
384
385 gistPlaceItupToPage(nodeBuffer->pageBuffer, itup);
386
387 /*
388 * If the buffer just overflowed, add it to the emptying queue.
389 */
390 if (BUFFER_HALF_FILLED(nodeBuffer, gfbb) && !nodeBuffer->queuedForEmptying)
391 {
392 gfbb->bufferEmptyingQueue = lcons(nodeBuffer,
393 gfbb->bufferEmptyingQueue);
394 nodeBuffer->queuedForEmptying = true;
395 }
396
397 /* Restore memory context */
398 MemoryContextSwitchTo(oldcxt);
399}
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb)
Definition: gist_private.h:324
#define PAGE_NO_SPACE(nbp, itup)
Definition: gist_private.h:59
static void WriteTempFileBlock(BufFile *file, long blknum, const void *ptr)
static void gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
static void gistPlaceItupToPage(GISTNodeBufferPage *pageBuffer, IndexTuple itup)
static GISTNodeBufferPage * gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
static long gistBuffersGetFreeBlock(GISTBuildBuffers *gfbb)

References GISTNodeBuffer::blocksCount, BUFFER_HALF_FILLED, GISTBuildBuffers::bufferEmptyingQueue, GISTBuildBuffers::context, gistAddLoadedBuffer(), gistAllocateNewPageBuffer(), gistBuffersGetFreeBlock(), gistLoadNodeBuffer(), gistPlaceItupToPage(), lcons(), MAXALIGN, MemoryContextSwitchTo(), PAGE_FREE_SPACE, PAGE_NO_SPACE, GISTNodeBuffer::pageBuffer, GISTBuildBuffers::pfile, GISTNodeBufferPage::prev, GISTNodeBuffer::queuedForEmptying, and WriteTempFileBlock().

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistRelocateBuildBuffersOnSplit()

void gistRelocateBuildBuffersOnSplit ( GISTBuildBuffers gfbb,
GISTSTATE giststate,
Relation  r,
int  level,
Buffer  buffer,
List splitinfo 
)

Definition at line 533 of file gistbuildbuffers.c.

536{
537 RelocationBufferInfo *relocationBuffersInfos;
538 bool found;
539 GISTNodeBuffer *nodeBuffer;
540 BlockNumber blocknum;
541 IndexTuple itup;
542 int splitPagesCount = 0;
544 bool isnull[INDEX_MAX_KEYS];
545 GISTNodeBuffer oldBuf;
546 ListCell *lc;
547
548 /* If the split page doesn't have buffers, we have nothing to do. */
549 if (!LEVEL_HAS_BUFFERS(level, gfbb))
550 return;
551
552 /*
553 * Get the node buffer of the split page.
554 */
555 blocknum = BufferGetBlockNumber(buffer);
556 nodeBuffer = hash_search(gfbb->nodeBuffersTab, &blocknum,
557 HASH_FIND, &found);
558 if (!found)
559 {
560 /* The page has no buffer, so we have nothing to do. */
561 return;
562 }
563
564 /*
565 * Make a copy of the old buffer, as we're going reuse it as the buffer
566 * for the new left page, which is on the same block as the old page.
567 * That's not true for the root page, but that's fine because we never
568 * have a buffer on the root page anyway. The original algorithm as
569 * described by Arge et al did, but it's of no use, as you might as well
570 * read the tuples straight from the heap instead of the root buffer.
571 */
572 Assert(blocknum != GIST_ROOT_BLKNO);
573 memcpy(&oldBuf, nodeBuffer, sizeof(GISTNodeBuffer));
574 oldBuf.isTemp = true;
575
576 /* Reset the old buffer, used for the new left page from now on */
577 nodeBuffer->blocksCount = 0;
578 nodeBuffer->pageBuffer = NULL;
579 nodeBuffer->pageBlocknum = InvalidBlockNumber;
580
581 /*
582 * Allocate memory for information about relocation buffers.
583 */
584 splitPagesCount = list_length(splitinfo);
585 relocationBuffersInfos =
587 splitPagesCount);
588
589 /*
590 * Fill relocation buffers information for node buffers of pages produced
591 * by split.
592 */
593 foreach(lc, splitinfo)
594 {
596 GISTNodeBuffer *newNodeBuffer;
597 int i = foreach_current_index(lc);
598
599 /* Decompress parent index tuple of node buffer page. */
600 gistDeCompressAtt(giststate, r,
601 si->downlink, NULL, (OffsetNumber) 0,
602 relocationBuffersInfos[i].entry,
603 relocationBuffersInfos[i].isnull);
604
605 /*
606 * Create a node buffer for the page. The leftmost half is on the same
607 * block as the old page before split, so for the leftmost half this
608 * will return the original buffer. The tuples on the original buffer
609 * were relinked to the temporary buffer, so the original one is now
610 * empty.
611 */
612 newNodeBuffer = gistGetNodeBuffer(gfbb, giststate, BufferGetBlockNumber(si->buf), level);
613
614 relocationBuffersInfos[i].nodeBuffer = newNodeBuffer;
615 relocationBuffersInfos[i].splitinfo = si;
616 }
617
618 /*
619 * Loop through all index tuples in the buffer of the page being split,
620 * moving them to buffers for the new pages. We try to move each tuple to
621 * the page that will result in the lowest penalty for the leading column
622 * or, in the case of a tie, the lowest penalty for the earliest column
623 * that is not tied.
624 *
625 * The page searching logic is very similar to gistchoose().
626 */
627 while (gistPopItupFromNodeBuffer(gfbb, &oldBuf, &itup))
628 {
629 float best_penalty[INDEX_MAX_KEYS];
630 int i,
631 which;
632 IndexTuple newtup;
633 RelocationBufferInfo *targetBufferInfo;
634
635 gistDeCompressAtt(giststate, r,
636 itup, NULL, (OffsetNumber) 0, entry, isnull);
637
638 /* default to using first page (shouldn't matter) */
639 which = 0;
640
641 /*
642 * best_penalty[j] is the best penalty we have seen so far for column
643 * j, or -1 when we haven't yet examined column j. Array entries to
644 * the right of the first -1 are undefined.
645 */
646 best_penalty[0] = -1;
647
648 /*
649 * Loop over possible target pages, looking for one to move this tuple
650 * to.
651 */
652 for (i = 0; i < splitPagesCount; i++)
653 {
654 RelocationBufferInfo *splitPageInfo = &relocationBuffersInfos[i];
655 bool zero_penalty;
656 int j;
657
658 zero_penalty = true;
659
660 /* Loop over index attributes. */
661 for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
662 {
663 float usize;
664
665 /* Compute penalty for this column. */
666 usize = gistpenalty(giststate, j,
667 &splitPageInfo->entry[j],
668 splitPageInfo->isnull[j],
669 &entry[j], isnull[j]);
670 if (usize > 0)
671 zero_penalty = false;
672
673 if (best_penalty[j] < 0 || usize < best_penalty[j])
674 {
675 /*
676 * New best penalty for column. Tentatively select this
677 * page as the target, and record the best penalty. Then
678 * reset the next column's penalty to "unknown" (and
679 * indirectly, the same for all the ones to its right).
680 * This will force us to adopt this page's penalty values
681 * as the best for all the remaining columns during
682 * subsequent loop iterations.
683 */
684 which = i;
685 best_penalty[j] = usize;
686
688 best_penalty[j + 1] = -1;
689 }
690 else if (best_penalty[j] == usize)
691 {
692 /*
693 * The current page is exactly as good for this column as
694 * the best page seen so far. The next iteration of this
695 * loop will compare the next column.
696 */
697 }
698 else
699 {
700 /*
701 * The current page is worse for this column than the best
702 * page seen so far. Skip the remaining columns and move
703 * on to the next page, if any.
704 */
705 zero_penalty = false; /* so outer loop won't exit */
706 break;
707 }
708 }
709
710 /*
711 * If we find a page with zero penalty for all columns, there's no
712 * need to examine remaining pages; just break out of the loop and
713 * return it.
714 */
715 if (zero_penalty)
716 break;
717 }
718
719 /* OK, "which" is the page index to push the tuple to */
720 targetBufferInfo = &relocationBuffersInfos[which];
721
722 /* Push item to selected node buffer */
723 gistPushItupToNodeBuffer(gfbb, targetBufferInfo->nodeBuffer, itup);
724
725 /* Adjust the downlink for this page, if needed. */
726 newtup = gistgetadjusted(r, targetBufferInfo->splitinfo->downlink,
727 itup, giststate);
728 if (newtup)
729 {
730 gistDeCompressAtt(giststate, r,
731 newtup, NULL, (OffsetNumber) 0,
732 targetBufferInfo->entry,
733 targetBufferInfo->isnull);
734
735 targetBufferInfo->splitinfo->downlink = newtup;
736 }
737 }
738
739 pfree(relocationBuffersInfos);
740}
#define LEVEL_HAS_BUFFERS(nlevel, gfbb)
Definition: gist_private.h:319
void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple itup)
bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *itup)
GISTNodeBuffer * gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
@ HASH_FIND
Definition: hsearch.h:113
static int list_length(const List *l)
Definition: pg_list.h:152
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
bool isnull[INDEX_MAX_KEYS]
GISTPageSplitInfo * splitinfo
GISTNodeBuffer * nodeBuffer
GISTENTRY entry[INDEX_MAX_KEYS]

References Assert(), GISTNodeBuffer::blocksCount, GISTPageSplitInfo::buf, BufferGetBlockNumber(), GISTPageSplitInfo::downlink, RelocationBufferInfo::entry, foreach_current_index, GIST_ROOT_BLKNO, gistDeCompressAtt(), gistgetadjusted(), gistGetNodeBuffer(), gistpenalty(), gistPopItupFromNodeBuffer(), gistPushItupToNodeBuffer(), HASH_FIND, hash_search(), i, INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, InvalidBlockNumber, RelocationBufferInfo::isnull, GISTNodeBuffer::isTemp, j, LEVEL_HAS_BUFFERS, lfirst, list_length(), RelocationBufferInfo::nodeBuffer, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, palloc(), pfree(), and RelocationBufferInfo::splitinfo.

Referenced by gistbufferinginserttuples().

◆ gistSplit()

SplitPageLayout * gistSplit ( Relation  r,
Page  page,
IndexTuple itup,
int  len,
GISTSTATE giststate 
)

Definition at line 1450 of file gist.c.

1455{
1456 IndexTuple *lvectup,
1457 *rvectup;
1459 int i;
1460 SplitPageLayout *res = NULL;
1461
1462 /* this should never recurse very deeply, but better safe than sorry */
1464
1465 /* there's no point in splitting an empty page */
1466 Assert(len > 0);
1467
1468 /*
1469 * If a single tuple doesn't fit on a page, no amount of splitting will
1470 * help.
1471 */
1472 if (len == 1)
1473 ereport(ERROR,
1474 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1475 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1476 IndexTupleSize(itup[0]), GiSTPageSize,
1478
1479 memset(v.spl_lisnull, true,
1480 sizeof(bool) * giststate->nonLeafTupdesc->natts);
1481 memset(v.spl_risnull, true,
1482 sizeof(bool) * giststate->nonLeafTupdesc->natts);
1483 gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1484
1485 /* form left and right vector */
1486 lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1487 rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1488
1489 for (i = 0; i < v.splitVector.spl_nleft; i++)
1490 lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1491
1492 for (i = 0; i < v.splitVector.spl_nright; i++)
1493 rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1494
1495 /* finalize splitting (may need another split) */
1496 if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1497 {
1498 res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1499 }
1500 else
1501 {
1502 ROTATEDIST(res);
1504 res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1505 res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1506 }
1507
1508 if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1509 {
1510 SplitPageLayout *resptr,
1511 *subres;
1512
1513 resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1514
1515 /* install on list's tail */
1516 while (resptr->next)
1517 resptr = resptr->next;
1518
1519 resptr->next = res;
1520 res = subres;
1521 }
1522 else
1523 {
1524 ROTATEDIST(res);
1525 res->block.num = v.splitVector.spl_nleft;
1526 res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1527 res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1528 }
1529
1530 return res;
1531}
#define ROTATEDIST(d)
Definition: gist.c:45
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:79
for(;;)
void check_stack_depth(void)
Definition: stack_depth.c:95
int spl_nleft
Definition: gist.h:144
OffsetNumber * spl_right
Definition: gist.h:148
int spl_nright
Definition: gist.h:149
OffsetNumber * spl_left
Definition: gist.h:143
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:243
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245

References Assert(), SplitPageLayout::block, check_stack_depth(), ereport, errcode(), errmsg(), ERROR, for(), gistfillitupvec(), gistfitpage(), gistFormTuple(), GiSTPageSize, gistSplit(), gistSplitByKey(), i, if(), IndexTupleSize(), SplitPageLayout::itup, len, SplitPageLayout::lenlist, SplitPageLayout::list, TupleDescData::natts, SplitPageLayout::next, GISTSTATE::nonLeafTupdesc, gistxlogPage::num, palloc(), RelationGetRelationName, ROTATEDIST, GistSplitVector::spl_lattr, GIST_SPLITVEC::spl_left, GistSplitVector::spl_lisnull, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GistSplitVector::spl_rattr, GIST_SPLITVEC::spl_right, GistSplitVector::spl_risnull, and GistSplitVector::splitVector.

Referenced by gist_indexsortbuild_levelstate_flush(), gistplacetopage(), and gistSplit().

◆ gistSplitByKey()

void gistSplitByKey ( Relation  r,
Page  page,
IndexTuple itup,
int  len,
GISTSTATE giststate,
GistSplitVector v,
int  attno 
)

Definition at line 623 of file gistsplit.c.

625{
626 GistEntryVector *entryvec;
627 OffsetNumber *offNullTuples;
628 int nOffNullTuples = 0;
629 int i;
630
631 /* generate the item array, and identify tuples with null keys */
632 /* note that entryvec->vector[0] goes unused in this code */
633 entryvec = palloc(GEVHDRSZ + (len + 1) * sizeof(GISTENTRY));
634 entryvec->n = len + 1;
635 offNullTuples = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
636
637 for (i = 1; i <= len; i++)
638 {
639 Datum datum;
640 bool IsNull;
641
642 datum = index_getattr(itup[i - 1], attno + 1, giststate->leafTupdesc,
643 &IsNull);
644 gistdentryinit(giststate, attno, &(entryvec->vector[i]),
645 datum, r, page, i,
646 false, IsNull);
647 if (IsNull)
648 offNullTuples[nOffNullTuples++] = i;
649 }
650
651 if (nOffNullTuples == len)
652 {
653 /*
654 * Corner case: All keys in attno column are null, so just transfer
655 * our attention to the next column. If there's no next column, just
656 * split page in half.
657 */
658 v->spl_risnull[attno] = v->spl_lisnull[attno] = true;
659
660 if (attno + 1 < giststate->nonLeafTupdesc->natts)
661 gistSplitByKey(r, page, itup, len, giststate, v, attno + 1);
662 else
664 }
665 else if (nOffNullTuples > 0)
666 {
667 int j = 0;
668
669 /*
670 * We don't want to mix NULL and not-NULL keys on one page, so split
671 * nulls to right page and not-nulls to left.
672 */
673 v->splitVector.spl_right = offNullTuples;
674 v->splitVector.spl_nright = nOffNullTuples;
675 v->spl_risnull[attno] = true;
676
678 v->splitVector.spl_nleft = 0;
679 for (i = 1; i <= len; i++)
680 if (j < v->splitVector.spl_nright && offNullTuples[j] == i)
681 j++;
682 else
684
685 /* Compute union keys, unless outer recursion level will handle it */
686 if (attno == 0 && giststate->nonLeafTupdesc->natts == 1)
687 {
688 v->spl_dontcare = NULL;
689 gistunionsubkey(giststate, itup, v);
690 }
691 }
692 else
693 {
694 /*
695 * All keys are not-null, so apply user-defined PickSplit method
696 */
697 if (gistUserPicksplit(r, entryvec, attno, v, itup, len, giststate))
698 {
699 /*
700 * Splitting on attno column is not optimal, so consider
701 * redistributing don't-care tuples according to the next column
702 */
703 Assert(attno + 1 < giststate->nonLeafTupdesc->natts);
704
705 if (v->spl_dontcare == NULL)
706 {
707 /*
708 * This split was actually degenerate, so ignore it altogether
709 * and just split according to the next column.
710 */
711 gistSplitByKey(r, page, itup, len, giststate, v, attno + 1);
712 }
713 else
714 {
715 /*
716 * Form an array of just the don't-care tuples to pass to a
717 * recursive invocation of this function for the next column.
718 */
719 IndexTuple *newitup = (IndexTuple *) palloc(len * sizeof(IndexTuple));
720 OffsetNumber *map = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
721 int newlen = 0;
722 GIST_SPLITVEC backupSplit;
723
724 for (i = 0; i < len; i++)
725 {
726 if (v->spl_dontcare[i + 1])
727 {
728 newitup[newlen] = itup[i];
729 map[newlen] = i + 1;
730 newlen++;
731 }
732 }
733
734 Assert(newlen > 0);
735
736 /*
737 * Make a backup copy of v->splitVector, since the recursive
738 * call will overwrite that with its own result.
739 */
740 backupSplit = v->splitVector;
741 backupSplit.spl_left = (OffsetNumber *) palloc(sizeof(OffsetNumber) * len);
742 memcpy(backupSplit.spl_left, v->splitVector.spl_left, sizeof(OffsetNumber) * v->splitVector.spl_nleft);
743 backupSplit.spl_right = (OffsetNumber *) palloc(sizeof(OffsetNumber) * len);
744 memcpy(backupSplit.spl_right, v->splitVector.spl_right, sizeof(OffsetNumber) * v->splitVector.spl_nright);
745
746 /* Recursively decide how to split the don't-care tuples */
747 gistSplitByKey(r, page, newitup, newlen, giststate, v, attno + 1);
748
749 /* Merge result of subsplit with non-don't-care tuples */
750 for (i = 0; i < v->splitVector.spl_nleft; i++)
751 backupSplit.spl_left[backupSplit.spl_nleft++] = map[v->splitVector.spl_left[i] - 1];
752 for (i = 0; i < v->splitVector.spl_nright; i++)
753 backupSplit.spl_right[backupSplit.spl_nright++] = map[v->splitVector.spl_right[i] - 1];
754
755 v->splitVector = backupSplit;
756 }
757 }
758 }
759
760 /*
761 * If we're handling a multicolumn index, at the end of the recursion
762 * recompute the left and right union datums for all index columns. This
763 * makes sure we hand back correct union datums in all corner cases,
764 * including when we haven't processed all columns to start with, or when
765 * a secondary split moved "don't care" tuples from one side to the other
766 * (we really shouldn't assume that that didn't change the union datums).
767 *
768 * Note: when we're in an internal recursion (attno > 0), we do not worry
769 * about whether the union datums we return with are sensible, since
770 * calling levels won't care. Also, in a single-column index, we expect
771 * that PickSplit (or the special cases above) produced correct union
772 * datums.
773 */
774 if (attno == 0 && giststate->nonLeafTupdesc->natts > 1)
775 {
776 v->spl_dontcare = NULL;
777 gistunionsubkey(giststate, itup, v);
778 }
779}
static void gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GistSplitVector *spl)
Definition: gistsplit.c:80
static bool gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVector *v, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gistsplit.c:415
static void gistSplitHalf(GIST_SPLITVEC *v, int len)
Definition: gistsplit.c:585

References Assert(), for(), GEVHDRSZ, gistdentryinit(), gistSplitByKey(), gistSplitHalf(), gistunionsubkey(), gistUserPicksplit(), i, index_getattr(), j, GISTSTATE::leafTupdesc, len, GistEntryVector::n, TupleDescData::natts, GISTSTATE::nonLeafTupdesc, palloc(), GistSplitVector::spl_dontcare, GIST_SPLITVEC::spl_left, GistSplitVector::spl_lisnull, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GIST_SPLITVEC::spl_right, GistSplitVector::spl_risnull, GistSplitVector::splitVector, and GistEntryVector::vector.

Referenced by gistSplit(), and gistSplitByKey().

◆ gistunion()

IndexTuple gistunion ( Relation  r,
IndexTuple itvec,
int  len,
GISTSTATE giststate 
)

Definition at line 219 of file gistutil.c.

220{
221 Datum attr[INDEX_MAX_KEYS];
222 bool isnull[INDEX_MAX_KEYS];
223
224 gistMakeUnionItVec(giststate, itvec, len, attr, isnull);
225
226 return gistFormTuple(giststate, r, attr, isnull, false);
227}
void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
Definition: gistutil.c:155

References gistFormTuple(), gistMakeUnionItVec(), INDEX_MAX_KEYS, and len.

Referenced by gist_indexsortbuild_levelstate_flush().

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 272 of file gistbuildbuffers.c.

273{
274 int i;
275
276 /* Unload all the buffers that have a page loaded in memory. */
277 for (i = 0; i < gfbb->loadedBuffersCount; i++)
279
280 /* Now there are no node buffers with loaded last page */
281 gfbb->loadedBuffersCount = 0;
282}
static void gistUnloadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)

References gistUnloadNodeBuffer(), i, GISTBuildBuffers::loadedBuffers, and GISTBuildBuffers::loadedBuffersCount.

Referenced by gistProcessEmptyingQueue().

◆ gistvacuumcleanup()

IndexBulkDeleteResult * gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 75 of file gistvacuum.c.

76{
77 /* No-op in ANALYZE ONLY mode */
78 if (info->analyze_only)
79 return stats;
80
81 /*
82 * If gistbulkdelete was called, we need not do anything, just return the
83 * stats from the latest gistbulkdelete call. If it wasn't called, we
84 * still need to do a pass over the index, to obtain index statistics.
85 */
86 if (stats == NULL)
87 {
89 gistvacuumscan(info, stats, NULL, NULL);
90 }
91
92 /*
93 * It's quite possible for us to be fooled by concurrent page splits into
94 * double-counting some index tuples, so disbelieve any total that exceeds
95 * the underlying heap's count ... if we know that accurately. Otherwise
96 * this might just make matters worse.
97 */
98 if (!info->estimated_count)
99 {
100 if (stats->num_index_tuples > info->num_heap_tuples)
101 stats->num_index_tuples = info->num_heap_tuples;
102 }
103
104 return stats;
105}
double num_index_tuples
Definition: genam.h:102
double num_heap_tuples
Definition: genam.h:75
bool analyze_only
Definition: genam.h:71
bool estimated_count
Definition: genam.h:73

References IndexVacuumInfo::analyze_only, IndexVacuumInfo::estimated_count, gistvacuumscan(), IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, and palloc0().

Referenced by gisthandler().

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 32 of file gistvalidate.c.

33{
34 bool result = true;
35 HeapTuple classtup;
36 Form_pg_opclass classform;
37 Oid opfamilyoid;
38 Oid opcintype;
39 Oid opckeytype;
40 char *opclassname;
41 char *opfamilyname;
42 CatCList *proclist,
43 *oprlist;
44 List *grouplist;
45 OpFamilyOpFuncGroup *opclassgroup;
46 int i;
47 ListCell *lc;
48
49 /* Fetch opclass information */
50 classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
51 if (!HeapTupleIsValid(classtup))
52 elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
53 classform = (Form_pg_opclass) GETSTRUCT(classtup);
54
55 opfamilyoid = classform->opcfamily;
56 opcintype = classform->opcintype;
57 opckeytype = classform->opckeytype;
58 if (!OidIsValid(opckeytype))
59 opckeytype = opcintype;
60 opclassname = NameStr(classform->opcname);
61
62 /* Fetch opfamily information */
63 opfamilyname = get_opfamily_name(opfamilyoid, false);
64
65 /* Fetch all operators and support functions of the opfamily */
66 oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
67 proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
68
69 /* Check individual support functions */
70 for (i = 0; i < proclist->n_members; i++)
71 {
72 HeapTuple proctup = &proclist->members[i]->tuple;
73 Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
74 bool ok;
75
76 /*
77 * All GiST support functions should be registered with matching
78 * left/right types
79 */
80 if (procform->amproclefttype != procform->amprocrighttype)
81 {
83 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
84 errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
85 opfamilyname, "gist",
86 format_procedure(procform->amproc))));
87 result = false;
88 }
89
90 /*
91 * We can't check signatures except within the specific opclass, since
92 * we need to know the associated opckeytype in many cases.
93 */
94 if (procform->amproclefttype != opcintype)
95 continue;
96
97 /* Check procedure numbers and function signatures */
98 switch (procform->amprocnum)
99 {
101 ok = check_amproc_signature(procform->amproc, BOOLOID, false,
102 5, 5, INTERNALOID, opcintype,
103 INT2OID, OIDOID, INTERNALOID);
104 break;
105 case GIST_UNION_PROC:
106 ok = check_amproc_signature(procform->amproc, opckeytype, false,
107 2, 2, INTERNALOID, INTERNALOID);
108 break;
111 case GIST_FETCH_PROC:
112 ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
113 1, 1, INTERNALOID);
114 break;
116 ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
117 3, 3, INTERNALOID,
118 INTERNALOID, INTERNALOID);
119 break;
121 ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
122 2, 2, INTERNALOID, INTERNALOID);
123 break;
124 case GIST_EQUAL_PROC:
125 ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
126 3, 3, opckeytype, opckeytype,
127 INTERNALOID);
128 break;
130 ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
131 5, 5, INTERNALOID, opcintype,
132 INT2OID, OIDOID, INTERNALOID);
133 break;
135 ok = check_amoptsproc_signature(procform->amproc);
136 break;
138 ok = check_amproc_signature(procform->amproc, VOIDOID, true,
139 1, 1, INTERNALOID);
140 break;
142 ok = check_amproc_signature(procform->amproc, INT2OID, true,
143 1, 1, INT4OID) &&
144 procform->amproclefttype == ANYOID &&
145 procform->amprocrighttype == ANYOID;
146 break;
147 default:
149 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
150 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
151 opfamilyname, "gist",
152 format_procedure(procform->amproc),
153 procform->amprocnum)));
154 result = false;
155 continue; /* don't want additional message */
156 }
157
158 if (!ok)
159 {
161 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
162 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
163 opfamilyname, "gist",
164 format_procedure(procform->amproc),
165 procform->amprocnum)));
166 result = false;
167 }
168 }
169
170 /* Check individual operators */
171 for (i = 0; i < oprlist->n_members; i++)
172 {
173 HeapTuple oprtup = &oprlist->members[i]->tuple;
174 Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
175 Oid op_rettype;
176
177 /* TODO: Check that only allowed strategy numbers exist */
178 if (oprform->amopstrategy < 1)
179 {
181 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
182 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
183 opfamilyname, "gist",
184 format_operator(oprform->amopopr),
185 oprform->amopstrategy)));
186 result = false;
187 }
188
189 /* GiST supports ORDER BY operators */
190 if (oprform->amoppurpose != AMOP_SEARCH)
191 {
192 /* ... but must have matching distance proc */
193 if (!OidIsValid(get_opfamily_proc(opfamilyoid,
194 oprform->amoplefttype,
195 oprform->amoplefttype,
197 {
199 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
200 errmsg("operator family \"%s\" of access method %s contains unsupported ORDER BY specification for operator %s",
201 opfamilyname, "gist",
202 format_operator(oprform->amopopr))));
203 result = false;
204 }
205 /* ... and operator result must match the claimed btree opfamily */
206 op_rettype = get_op_rettype(oprform->amopopr);
207 if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
208 {
210 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
211 errmsg("operator family \"%s\" of access method %s contains incorrect ORDER BY opfamily specification for operator %s",
212 opfamilyname, "gist",
213 format_operator(oprform->amopopr))));
214 result = false;
215 }
216 }
217 else
218 {
219 /* Search operators must always return bool */
220 op_rettype = BOOLOID;
221 }
222
223 /* Check operator signature */
224 if (!check_amop_signature(oprform->amopopr, op_rettype,
225 oprform->amoplefttype,
226 oprform->amoprighttype))
227 {
229 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
230 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
231 opfamilyname, "gist",
232 format_operator(oprform->amopopr))));
233 result = false;
234 }
235 }
236
237 /* Now check for inconsistent groups of operators/functions */
238 grouplist = identify_opfamily_groups(oprlist, proclist);
239 opclassgroup = NULL;
240 foreach(lc, grouplist)
241 {
243
244 /* Remember the group exactly matching the test opclass */
245 if (thisgroup->lefttype == opcintype &&
246 thisgroup->righttype == opcintype)
247 opclassgroup = thisgroup;
248
249 /*
250 * There is not a lot we can do to check the operator sets, since each
251 * GiST opclass is more or less a law unto itself, and some contain
252 * only operators that are binary-compatible with the opclass datatype
253 * (meaning that empty operator sets can be OK). That case also means
254 * that we shouldn't insist on nonempty function sets except for the
255 * opclass's own group.
256 */
257 }
258
259 /* Check that the originally-named opclass is complete */
260 for (i = 1; i <= GISTNProcs; i++)
261 {
262 if (opclassgroup &&
263 (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
264 continue; /* got it */
265 if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
269 continue; /* optional methods */
271 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
272 errmsg("operator class \"%s\" of access method %s is missing support function %d",
273 opclassname, "gist", i)));
274 result = false;
275 }
276
277 ReleaseCatCacheList(proclist);
278 ReleaseCatCacheList(oprlist);
279 ReleaseSysCache(classtup);
280
281 return result;
282}
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:152
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:206
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:43
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:271
bool check_amoptsproc_signature(Oid funcid)
Definition: amvalidate.c:192
#define NameStr(name)
Definition: c.h:717
uint64_t uint64
Definition: c.h:503
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:2093
#define INFO
Definition: elog.h:34
#define GISTNProcs
Definition: gist.h:44
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:888
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1473
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1393
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
char * format_operator(Oid operator_oid)
Definition: regproc.c:793
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
int n_members
Definition: catcache.h:178
HeapTupleData tuple
Definition: catcache.h:123
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:127

References check_amop_signature(), check_amoptsproc_signature(), check_amproc_signature(), elog, ereport, errcode(), errmsg(), ERROR, format_operator(), format_procedure(), OpFamilyOpFuncGroup::functionset, get_op_rettype(), get_opfamily_name(), get_opfamily_proc(), GETSTRUCT(), GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_OPTIONS_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_SORTSUPPORT_PROC, GIST_STRATNUM_PROC, GIST_UNION_PROC, GISTNProcs, HeapTupleIsValid, i, identify_opfamily_groups(), INFO, OpFamilyOpFuncGroup::lefttype, lfirst, catclist::members, catclist::n_members, NameStr, ObjectIdGetDatum(), OidIsValid, opfamily_can_sort_type(), ReleaseCatCacheList(), ReleaseSysCache(), OpFamilyOpFuncGroup::righttype, SearchSysCache1(), SearchSysCacheList1, and catctup::tuple.

Referenced by gisthandler().

◆ gistXLogAssignLSN()

XLogRecPtr gistXLogAssignLSN ( void  )

Definition at line 576 of file gistxlog.c.

577{
578 int dummy = 0;
579
580 /*
581 * Records other than XLOG_SWITCH must have content. We use an integer 0
582 * to follow the restriction.
583 */
586 XLogRegisterData(&dummy, sizeof(dummy));
587 return XLogInsert(RM_GIST_ID, XLOG_GIST_ASSIGN_LSN);
588}
#define XLOG_GIST_ASSIGN_LSN
Definition: gistxlog.h:27
#define XLOG_MARK_UNIMPORTANT
Definition: xlog.h:155
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:364
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456
void XLogBeginInsert(void)
Definition: xloginsert.c:149

References XLOG_GIST_ASSIGN_LSN, XLOG_MARK_UNIMPORTANT, XLogBeginInsert(), XLogInsert(), XLogRegisterData(), and XLogSetRecordFlags().

Referenced by gistGetFakeLSN().

◆ gistXLogDelete()

XLogRecPtr gistXLogDelete ( Buffer  buffer,
OffsetNumber todelete,
int  ntodelete,
TransactionId  snapshotConflictHorizon,
Relation  heaprel 
)

Definition at line 670 of file gistxlog.c.

672{
673 gistxlogDelete xlrec;
674 XLogRecPtr recptr;
675
677 xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
678 xlrec.ntodelete = ntodelete;
679
682
683 /*
684 * We need the target-offsets array whether or not we store the whole
685 * buffer, to allow us to find the snapshotConflictHorizon on a standby
686 * server.
687 */
688 XLogRegisterData(todelete, ntodelete * sizeof(OffsetNumber));
689
691
692 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_DELETE);
693
694 return recptr;
695}
#define SizeOfGistxlogDelete
Definition: gistxlog.h:59
#define XLOG_GIST_DELETE
Definition: gistxlog.h:21
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:695
bool isCatalogRel
Definition: gistxlog.h:52
TransactionId snapshotConflictHorizon
Definition: gistxlog.h:50
uint16 ntodelete
Definition: gistxlog.h:51
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:242
#define REGBUF_STANDARD
Definition: xloginsert.h:35

References gistxlogDelete::isCatalogRel, gistxlogDelete::ntodelete, REGBUF_STANDARD, RelationIsAccessibleInLogicalDecoding, SizeOfGistxlogDelete, gistxlogDelete::snapshotConflictHorizon, XLOG_GIST_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistprunepage().

◆ gistXLogPageDelete()

XLogRecPtr gistXLogPageDelete ( Buffer  buffer,
FullTransactionId  xid,
Buffer  parentBuffer,
OffsetNumber  downlinkOffset 
)

Definition at line 552 of file gistxlog.c.

554{
555 gistxlogPageDelete xlrec;
556 XLogRecPtr recptr;
557
558 xlrec.deleteXid = xid;
559 xlrec.downlinkOffset = downlinkOffset;
560
563
565 XLogRegisterBuffer(1, parentBuffer, REGBUF_STANDARD);
566
567 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE);
568
569 return recptr;
570}
#define SizeOfGistxlogPageDelete
Definition: gistxlog.h:91
#define XLOG_GIST_PAGE_DELETE
Definition: gistxlog.h:26
FullTransactionId deleteXid
Definition: gistxlog.h:86
OffsetNumber downlinkOffset
Definition: gistxlog.h:87

References gistxlogPageDelete::deleteXid, gistxlogPageDelete::downlinkOffset, REGBUF_STANDARD, SizeOfGistxlogPageDelete, XLOG_GIST_PAGE_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistdeletepage().

◆ gistXLogPageReuse()

void gistXLogPageReuse ( Relation  rel,
Relation  heaprel,
BlockNumber  blkno,
FullTransactionId  deleteXid 
)

Definition at line 594 of file gistxlog.c.

596{
597 gistxlogPageReuse xlrec_reuse;
598
599 /*
600 * Note that we don't register the buffer with the record, because this
601 * operation doesn't modify the page. This record only exists to provide a
602 * conflict point for Hot Standby.
603 */
604
605 /* XLOG stuff */
607 xlrec_reuse.locator = rel->rd_locator;
608 xlrec_reuse.block = blkno;
609 xlrec_reuse.snapshotConflictHorizon = deleteXid;
610
613
614 XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_REUSE);
615}
#define XLOG_GIST_PAGE_REUSE
Definition: gistxlog.h:22
#define SizeOfGistxlogPageReuse
Definition: gistxlog.h:106
RelFileLocator rd_locator
Definition: rel.h:57
RelFileLocator locator
Definition: gistxlog.h:99
BlockNumber block
Definition: gistxlog.h:100
FullTransactionId snapshotConflictHorizon
Definition: gistxlog.h:101

References gistxlogPageReuse::block, gistxlogPageReuse::isCatalogRel, gistxlogPageReuse::locator, RelationData::rd_locator, RelationIsAccessibleInLogicalDecoding, SizeOfGistxlogPageReuse, gistxlogPageReuse::snapshotConflictHorizon, XLOG_GIST_PAGE_REUSE, XLogBeginInsert(), XLogInsert(), and XLogRegisterData().

Referenced by gistNewBuffer().

◆ gistXLogSplit()

XLogRecPtr gistXLogSplit ( bool  page_is_leaf,
SplitPageLayout dist,
BlockNumber  origrlink,
GistNSN  orignsn,
Buffer  leftchildbuf,
bool  markfollowright 
)

Definition at line 495 of file gistxlog.c.

499{
500 gistxlogPageSplit xlrec;
501 SplitPageLayout *ptr;
502 int npage = 0;
503 XLogRecPtr recptr;
504 int i;
505
506 for (ptr = dist; ptr; ptr = ptr->next)
507 npage++;
508
509 xlrec.origrlink = origrlink;
510 xlrec.orignsn = orignsn;
511 xlrec.origleaf = page_is_leaf;
512 xlrec.npage = (uint16) npage;
513 xlrec.markfollowright = markfollowright;
514
516
517 /*
518 * Include a full page image of the child buf. (only necessary if a
519 * checkpoint happened since the child page was split)
520 */
521 if (BufferIsValid(leftchildbuf))
522 XLogRegisterBuffer(0, leftchildbuf, REGBUF_STANDARD);
523
524 /*
525 * NOTE: We register a lot of data. The caller must've called
526 * XLogEnsureRecordSpace() to prepare for that. We cannot do it here,
527 * because we're already in a critical section. If you change the number
528 * of buffer or data registrations here, make sure you modify the
529 * XLogEnsureRecordSpace() calls accordingly!
530 */
531 XLogRegisterData(&xlrec, sizeof(gistxlogPageSplit));
532
533 i = 1;
534 for (ptr = dist; ptr; ptr = ptr->next)
535 {
537 XLogRegisterBufData(i, &(ptr->block.num), sizeof(int));
538 XLogRegisterBufData(i, ptr->list, ptr->lenlist);
539 i++;
540 }
541
542 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT);
543
544 return recptr;
545}
uint16_t uint16
Definition: c.h:501
#define XLOG_GIST_PAGE_SPLIT
Definition: gistxlog.h:23
GistNSN orignsn
Definition: gistxlog.h:69
BlockNumber origrlink
Definition: gistxlog.h:68
bool markfollowright
Definition: gistxlog.h:73
void XLogRegisterBufData(uint8 block_id, const void *data, uint32 len)
Definition: xloginsert.c:405
#define REGBUF_WILL_INIT
Definition: xloginsert.h:34

References SplitPageLayout::block, SplitPageLayout::buffer, BufferIsValid(), i, SplitPageLayout::lenlist, SplitPageLayout::list, gistxlogPageSplit::markfollowright, SplitPageLayout::next, gistxlogPageSplit::npage, gistxlogPage::num, gistxlogPageSplit::origleaf, gistxlogPageSplit::orignsn, gistxlogPageSplit::origrlink, REGBUF_STANDARD, REGBUF_WILL_INIT, XLOG_GIST_PAGE_SPLIT, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistplacetopage().

◆ gistXLogUpdate()

XLogRecPtr gistXLogUpdate ( Buffer  buffer,
OffsetNumber todelete,
int  ntodelete,
IndexTuple itup,
int  ituplen,
Buffer  leftchildbuf 
)

Definition at line 629 of file gistxlog.c.

633{
634 gistxlogPageUpdate xlrec;
635 int i;
636 XLogRecPtr recptr;
637
638 xlrec.ntodelete = ntodelete;
639 xlrec.ntoinsert = ituplen;
640
642 XLogRegisterData(&xlrec, sizeof(gistxlogPageUpdate));
643
645 XLogRegisterBufData(0, todelete, sizeof(OffsetNumber) * ntodelete);
646
647 /* new tuples */
648 for (i = 0; i < ituplen; i++)
649 XLogRegisterBufData(0, itup[i], IndexTupleSize(itup[i]));
650
651 /*
652 * Include a full page image of the child buf. (only necessary if a
653 * checkpoint happened since the child page was split)
654 */
655 if (BufferIsValid(leftchildbuf))
656 XLogRegisterBuffer(1, leftchildbuf, REGBUF_STANDARD);
657
658 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE);
659
660 return recptr;
661}
#define XLOG_GIST_PAGE_UPDATE
Definition: gistxlog.h:20
uint16 ntodelete
Definition: gistxlog.h:37
uint16 ntoinsert
Definition: gistxlog.h:38

References BufferIsValid(), i, IndexTupleSize(), gistxlogPageUpdate::ntodelete, gistxlogPageUpdate::ntoinsert, REGBUF_STANDARD, XLOG_GIST_PAGE_UPDATE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistplacetopage(), and gistvacuumpage().

◆ initGISTstate()

GISTSTATE * initGISTstate ( Relation  index)

Definition at line 1537 of file gist.c.

1538{
1539 GISTSTATE *giststate;
1540 MemoryContext scanCxt;
1541 MemoryContext oldCxt;
1542 int i;
1543
1544 /* safety check to protect fixed-size arrays in GISTSTATE */
1545 if (index->rd_att->natts > INDEX_MAX_KEYS)
1546 elog(ERROR, "numberOfAttributes %d > %d",
1547 index->rd_att->natts, INDEX_MAX_KEYS);
1548
1549 /* Create the memory context that will hold the GISTSTATE */
1551 "GiST scan context",
1553 oldCxt = MemoryContextSwitchTo(scanCxt);
1554
1555 /* Create and fill in the GISTSTATE */
1556 giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
1557
1558 giststate->scanCxt = scanCxt;
1559 giststate->tempCxt = scanCxt; /* caller must change this if needed */
1560 giststate->leafTupdesc = index->rd_att;
1561
1562 /*
1563 * The truncated tupdesc for non-leaf index tuples, which doesn't contain
1564 * the INCLUDE attributes.
1565 *
1566 * It is used to form tuples during tuple adjustment and page split.
1567 * B-tree creates shortened tuple descriptor for every truncated tuple,
1568 * because it is doing this less often: it does not have to form truncated
1569 * tuples during page split. Also, B-tree is not adjusting tuples on
1570 * internal pages the way GiST does.
1571 */
1574
1576 {
1577 fmgr_info_copy(&(giststate->consistentFn[i]),
1579 scanCxt);
1580 fmgr_info_copy(&(giststate->unionFn[i]),
1582 scanCxt);
1583
1584 /* opclasses are not required to provide a Compress method */
1586 fmgr_info_copy(&(giststate->compressFn[i]),
1588 scanCxt);
1589 else
1590 giststate->compressFn[i].fn_oid = InvalidOid;
1591
1592 /* opclasses are not required to provide a Decompress method */
1594 fmgr_info_copy(&(giststate->decompressFn[i]),
1596 scanCxt);
1597 else
1598 giststate->decompressFn[i].fn_oid = InvalidOid;
1599
1600 fmgr_info_copy(&(giststate->penaltyFn[i]),
1602 scanCxt);
1603 fmgr_info_copy(&(giststate->picksplitFn[i]),
1605 scanCxt);
1606 fmgr_info_copy(&(giststate->equalFn[i]),
1608 scanCxt);
1609
1610 /* opclasses are not required to provide a Distance method */
1612 fmgr_info_copy(&(giststate->distanceFn[i]),
1614 scanCxt);
1615 else
1616 giststate->distanceFn[i].fn_oid = InvalidOid;
1617
1618 /* opclasses are not required to provide a Fetch method */
1620 fmgr_info_copy(&(giststate->fetchFn[i]),
1622 scanCxt);
1623 else
1624 giststate->fetchFn[i].fn_oid = InvalidOid;
1625
1626 /*
1627 * If the index column has a specified collation, we should honor that
1628 * while doing comparisons. However, we may have a collatable storage
1629 * type for a noncollatable indexed data type. If there's no index
1630 * collation then specify default collation in case the support
1631 * functions need collation. This is harmless if the support
1632 * functions don't care about collation, so we just do it
1633 * unconditionally. (We could alternatively call get_typcollation,
1634 * but that seems like expensive overkill --- there aren't going to be
1635 * any cases where a GiST storage type has a nondefault collation.)
1636 */
1637 if (OidIsValid(index->rd_indcollation[i]))
1638 giststate->supportCollation[i] = index->rd_indcollation[i];
1639 else
1640 giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1641 }
1642
1643 /* No opclass information for INCLUDE attributes */
1644 for (; i < index->rd_att->natts; i++)
1645 {
1646 giststate->consistentFn[i].fn_oid = InvalidOid;
1647 giststate->unionFn[i].fn_oid = InvalidOid;
1648 giststate->compressFn[i].fn_oid = InvalidOid;
1649 giststate->decompressFn[i].fn_oid = InvalidOid;
1650 giststate->penaltyFn[i].fn_oid = InvalidOid;
1651 giststate->picksplitFn[i].fn_oid = InvalidOid;
1652 giststate->equalFn[i].fn_oid = InvalidOid;
1653 giststate->distanceFn[i].fn_oid = InvalidOid;
1654 giststate->fetchFn[i].fn_oid = InvalidOid;
1655 giststate->supportCollation[i] = InvalidOid;
1656 }
1657
1658 MemoryContextSwitchTo(oldCxt);
1659
1660 return giststate;
1661}
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:580
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:907
FmgrInfo distanceFn[INDEX_MAX_KEYS]
Definition: gist_private.h:93
FmgrInfo consistentFn[INDEX_MAX_KEYS]
Definition: gist_private.h:86
FmgrInfo picksplitFn[INDEX_MAX_KEYS]
Definition: gist_private.h:91
TupleDesc CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
Definition: tupdesc.c:289

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, GISTSTATE::compressFn, GISTSTATE::consistentFn, CreateTupleDescTruncatedCopy(), CurrentMemoryContext, GISTSTATE::decompressFn, GISTSTATE::distanceFn, elog, GISTSTATE::equalFn, ERROR, GISTSTATE::fetchFn, fmgr_info_copy(), FmgrInfo::fn_oid, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_UNION_PROC, i, index_getprocid(), index_getprocinfo(), INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, InvalidOid, GISTSTATE::leafTupdesc, MemoryContextSwitchTo(), GISTSTATE::nonLeafTupdesc, OidIsValid, palloc(), GISTSTATE::penaltyFn, GISTSTATE::picksplitFn, GISTSTATE::scanCxt, GISTSTATE::supportCollation, GISTSTATE::tempCxt, and GISTSTATE::unionFn.

Referenced by gistbeginscan(), gistbuild(), and gistinsert().