PostgreSQL Source Code  git master
spginsert.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * spginsert.c
4  * Externally visible index creation/insertion routines
5  *
6  * All the actual insertion logic is in spgdoinsert.c.
7  *
8  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
9  * Portions Copyright (c) 1994, Regents of the University of California
10  *
11  * IDENTIFICATION
12  * src/backend/access/spgist/spginsert.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #include "postgres.h"
18 
19 #include "access/genam.h"
20 #include "access/spgist_private.h"
21 #include "access/spgxlog.h"
22 #include "access/tableam.h"
23 #include "access/xlog.h"
24 #include "access/xloginsert.h"
25 #include "catalog/index.h"
26 #include "miscadmin.h"
27 #include "storage/bufmgr.h"
28 #include "storage/smgr.h"
29 #include "utils/memutils.h"
30 #include "utils/rel.h"
31 
32 
33 typedef struct
34 {
35  SpGistState spgstate; /* SPGiST's working state */
36  int64 indtuples; /* total number of tuples indexed */
37  MemoryContext tmpCtx; /* per-tuple temporary context */
39 
40 
41 /* Callback to process one heap tuple during table_index_build_scan */
42 static void
44  bool *isnull, bool tupleIsAlive, void *state)
45 {
46  SpGistBuildState *buildstate = (SpGistBuildState *) state;
47  MemoryContext oldCtx;
48 
49  /* Work in temp context, and reset it after each tuple */
50  oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
51 
52  /*
53  * Even though no concurrent insertions can be happening, we still might
54  * get a buffer-locking failure due to bgwriter or checkpointer taking a
55  * lock on some buffer. So we need to be willing to retry. We can flush
56  * any temp data when retrying.
57  */
58  while (!spgdoinsert(index, &buildstate->spgstate, tid,
59  values, isnull))
60  {
61  MemoryContextReset(buildstate->tmpCtx);
62  }
63 
64  /* Update total tuple count */
65  buildstate->indtuples += 1;
66 
67  MemoryContextSwitchTo(oldCtx);
68  MemoryContextReset(buildstate->tmpCtx);
69 }
70 
71 /*
72  * Build an SP-GiST index.
73  */
76 {
77  IndexBuildResult *result;
78  double reltuples;
79  SpGistBuildState buildstate;
80  Buffer metabuffer,
81  rootbuffer,
82  nullbuffer;
83 
85  elog(ERROR, "index \"%s\" already contains data",
87 
88  /*
89  * Initialize the meta page and root pages
90  */
91  metabuffer = SpGistNewBuffer(index);
92  rootbuffer = SpGistNewBuffer(index);
93  nullbuffer = SpGistNewBuffer(index);
94 
98 
100 
101  SpGistInitMetapage(BufferGetPage(metabuffer));
102  MarkBufferDirty(metabuffer);
103  SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
104  MarkBufferDirty(rootbuffer);
106  MarkBufferDirty(nullbuffer);
107 
108 
110 
111  UnlockReleaseBuffer(metabuffer);
112  UnlockReleaseBuffer(rootbuffer);
113  UnlockReleaseBuffer(nullbuffer);
114 
115  /*
116  * Now insert all the heap data into the index
117  */
118  initSpGistState(&buildstate.spgstate, index);
119  buildstate.spgstate.isBuild = true;
120  buildstate.indtuples = 0;
121 
123  "SP-GiST build temporary context",
125 
126  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
127  spgistBuildCallback, (void *) &buildstate,
128  NULL);
129 
130  MemoryContextDelete(buildstate.tmpCtx);
131 
133 
134  /*
135  * We didn't write WAL records as we built the index, so if WAL-logging is
136  * required, write all pages to the WAL now.
137  */
138  if (RelationNeedsWAL(index))
139  {
142  true);
143  }
144 
145  result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
146  result->heap_tuples = reltuples;
147  result->index_tuples = buildstate.indtuples;
148 
149  return result;
150 }
151 
152 /*
153  * Build an empty SPGiST index in the initialization fork
154  */
155 void
157 {
158  Page page;
159 
160  /* Construct metapage. */
161  page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
162  SpGistInitMetapage(page);
163 
164  /*
165  * Write the page and log it unconditionally. This is important
166  * particularly for indexes created on tablespaces and databases whose
167  * creation happened after the last redo pointer as recovery removes any
168  * of their existing content when the corresponding create records are
169  * replayed.
170  */
173  page, true);
174  log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
175  SPGIST_METAPAGE_BLKNO, page, true);
176 
177  /* Likewise for the root page. */
179 
182  page, true);
183  log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
184  SPGIST_ROOT_BLKNO, page, true);
185 
186  /* Likewise for the null-tuples root page. */
188 
191  page, true);
192  log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
193  SPGIST_NULL_BLKNO, page, true);
194 
195  /*
196  * An immediate sync is required even if we xlog'd the pages, because the
197  * writes did not go through shared buffers and therefore a concurrent
198  * checkpoint may have moved the redo pointer past our xlog record.
199  */
201 }
202 
203 /*
204  * Insert one new tuple into an SPGiST index.
205  */
206 bool
208  ItemPointer ht_ctid, Relation heapRel,
209  IndexUniqueCheck checkUnique,
210  bool indexUnchanged,
211  IndexInfo *indexInfo)
212 {
213  SpGistState spgstate;
214  MemoryContext oldCtx;
215  MemoryContext insertCtx;
216 
218  "SP-GiST insert temporary context",
220  oldCtx = MemoryContextSwitchTo(insertCtx);
221 
222  initSpGistState(&spgstate, index);
223 
224  /*
225  * We might have to repeat spgdoinsert() multiple times, if conflicts
226  * occur with concurrent insertions. If so, reset the insertCtx each time
227  * to avoid cumulative memory consumption. That means we also have to
228  * redo initSpGistState(), but it's cheap enough not to matter.
229  */
230  while (!spgdoinsert(index, &spgstate, ht_ctid, values, isnull))
231  {
232  MemoryContextReset(insertCtx);
233  initSpGistState(&spgstate, index);
234  }
235 
237 
238  MemoryContextSwitchTo(oldCtx);
239  MemoryContextDelete(insertCtx);
240 
241  /* return false since we've not done any unique check */
242  return false;
243 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3290
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4497
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2111
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:227
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:355
void PageSetChecksumInplace(Page page, BlockNumber blkno)
Definition: bufpage.c:1542
Pointer Page
Definition: bufpage.h:78
#define ERROR
Definition: elog.h:39
IndexUniqueCheck
Definition: genam.h:116
Assert(fmt[strlen(fmt) - 1] !='\n')
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:330
void * palloc0(Size size)
Definition: mcxt.c:1257
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void * palloc_aligned(Size size, Size alignto, int flags)
Definition: mcxt.c:1446
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define START_CRIT_SECTION()
Definition: miscadmin.h:148
#define END_CRIT_SECTION()
Definition: miscadmin.h:150
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
#define PG_IO_ALIGN_SIZE
uintptr_t Datum
Definition: postgres.h:64
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:572
#define RelationGetRelationName(relation)
Definition: rel.h:538
#define RelationNeedsWAL(relation)
Definition: rel.h:629
@ MAIN_FORKNUM
Definition: relpath.h:50
@ INIT_FORKNUM
Definition: relpath.h:53
void smgrwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, const void *buffer, bool skipFsync)
Definition: smgr.c:583
void smgrimmedsync(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:720
bool spgdoinsert(Relation index, SpGistState *state, ItemPointer heapPtr, Datum *datums, bool *isnulls)
Definition: spgdoinsert.c:1915
static void spgistBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: spginsert.c:43
bool spginsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: spginsert.c:207
void spgbuildempty(Relation index)
Definition: spginsert.c:156
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: spginsert.c:75
#define SPGIST_NULL_BLKNO
#define SPGIST_METAPAGE_BLKNO
#define SPGIST_NULLS
#define SPGIST_LEAF
#define SPGIST_ROOT_BLKNO
void initSpGistState(SpGistState *state, Relation index)
Definition: spgutils.c:331
void SpGistUpdateMetaPage(Relation index)
Definition: spgutils.c:422
Buffer SpGistNewBuffer(Relation index)
Definition: spgutils.c:366
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:694
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:680
void SpGistInitMetapage(Page page)
Definition: spgutils.c:704
double heap_tuples
Definition: genam.h:32
double index_tuples
Definition: genam.h:33
MemoryContext tmpCtx
Definition: spginsert.c:37
SpGistState spgstate
Definition: spginsert.c:35
Definition: type.h:95
Definition: regguts.h:323
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:1772
XLogRecPtr log_newpage(RelFileLocator *rlocator, ForkNumber forknum, BlockNumber blkno, Page page, bool page_std)
Definition: xloginsert.c:1131
void log_newpage_range(Relation rel, ForkNumber forknum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1258