]> granicus.if.org Git - postgresql/blob - src/backend/access/spgist/spginsert.c
Modify BufferGetPage() to prepare for "snapshot too old" feature
[postgresql] / src / backend / access / spgist / spginsert.c
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-2016, 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/xlog.h"
22 #include "access/xloginsert.h"
23 #include "catalog/index.h"
24 #include "miscadmin.h"
25 #include "storage/bufmgr.h"
26 #include "storage/smgr.h"
27 #include "utils/memutils.h"
28 #include "utils/rel.h"
29
30
31 typedef struct
32 {
33         SpGistState spgstate;           /* SPGiST's working state */
34         MemoryContext tmpCtx;           /* per-tuple temporary context */
35 } SpGistBuildState;
36
37
38 /* Callback to process one heap tuple during IndexBuildHeapScan */
39 static void
40 spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
41                                         bool *isnull, bool tupleIsAlive, void *state)
42 {
43         SpGistBuildState *buildstate = (SpGistBuildState *) state;
44         MemoryContext oldCtx;
45
46         /* Work in temp context, and reset it after each tuple */
47         oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
48
49         /*
50          * Even though no concurrent insertions can be happening, we still might
51          * get a buffer-locking failure due to bgwriter or checkpointer taking a
52          * lock on some buffer.  So we need to be willing to retry.  We can flush
53          * any temp data when retrying.
54          */
55         while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
56                                                 *values, *isnull))
57         {
58                 MemoryContextReset(buildstate->tmpCtx);
59         }
60
61         MemoryContextSwitchTo(oldCtx);
62         MemoryContextReset(buildstate->tmpCtx);
63 }
64
65 /*
66  * Build an SP-GiST index.
67  */
68 IndexBuildResult *
69 spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
70 {
71         IndexBuildResult *result;
72         double          reltuples;
73         SpGistBuildState buildstate;
74         Buffer          metabuffer,
75                                 rootbuffer,
76                                 nullbuffer;
77
78         if (RelationGetNumberOfBlocks(index) != 0)
79                 elog(ERROR, "index \"%s\" already contains data",
80                          RelationGetRelationName(index));
81
82         /*
83          * Initialize the meta page and root pages
84          */
85         metabuffer = SpGistNewBuffer(index);
86         rootbuffer = SpGistNewBuffer(index);
87         nullbuffer = SpGistNewBuffer(index);
88
89         Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
90         Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
91         Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
92
93         START_CRIT_SECTION();
94
95         SpGistInitMetapage(BufferGetPage(metabuffer, NULL, NULL,
96                                                                          BGP_NO_SNAPSHOT_TEST));
97         MarkBufferDirty(metabuffer);
98         SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
99         MarkBufferDirty(rootbuffer);
100         SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
101         MarkBufferDirty(nullbuffer);
102
103         if (RelationNeedsWAL(index))
104         {
105                 XLogRecPtr      recptr;
106
107                 XLogBeginInsert();
108
109                 /*
110                  * Replay will re-initialize the pages, so don't take full pages
111                  * images.  No other data to log.
112                  */
113                 XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
114                 XLogRegisterBuffer(1, rootbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
115                 XLogRegisterBuffer(2, nullbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
116
117                 recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX);
118
119                 PageSetLSN(BufferGetPage(metabuffer, NULL, NULL,
120                                                                  BGP_NO_SNAPSHOT_TEST), recptr);
121                 PageSetLSN(BufferGetPage(rootbuffer, NULL, NULL,
122                                                                  BGP_NO_SNAPSHOT_TEST), recptr);
123                 PageSetLSN(BufferGetPage(nullbuffer, NULL, NULL,
124                                                                  BGP_NO_SNAPSHOT_TEST), recptr);
125         }
126
127         END_CRIT_SECTION();
128
129         UnlockReleaseBuffer(metabuffer);
130         UnlockReleaseBuffer(rootbuffer);
131         UnlockReleaseBuffer(nullbuffer);
132
133         /*
134          * Now insert all the heap data into the index
135          */
136         initSpGistState(&buildstate.spgstate, index);
137         buildstate.spgstate.isBuild = true;
138
139         buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
140                                                                                    "SP-GiST build temporary context",
141                                                                                           ALLOCSET_DEFAULT_MINSIZE,
142                                                                                           ALLOCSET_DEFAULT_INITSIZE,
143                                                                                           ALLOCSET_DEFAULT_MAXSIZE);
144
145         reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
146                                                                    spgistBuildCallback, (void *) &buildstate);
147
148         MemoryContextDelete(buildstate.tmpCtx);
149
150         SpGistUpdateMetaPage(index);
151
152         result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
153         result->heap_tuples = result->index_tuples = reltuples;
154
155         return result;
156 }
157
158 /*
159  * Build an empty SPGiST index in the initialization fork
160  */
161 void
162 spgbuildempty(Relation index)
163 {
164         Page            page;
165
166         /* Construct metapage. */
167         page = (Page) palloc(BLCKSZ);
168         SpGistInitMetapage(page);
169
170         /* Write the page.  If archiving/streaming, XLOG it. */
171         PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
172         smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
173                           (char *) page, true);
174         if (XLogIsNeeded())
175                 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
176                                         SPGIST_METAPAGE_BLKNO, page, false);
177
178         /* Likewise for the root page. */
179         SpGistInitPage(page, SPGIST_LEAF);
180
181         PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
182         smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
183                           (char *) page, true);
184         if (XLogIsNeeded())
185                 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
186                                         SPGIST_ROOT_BLKNO, page, true);
187
188         /* Likewise for the null-tuples root page. */
189         SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
190
191         PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
192         smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
193                           (char *) page, true);
194         if (XLogIsNeeded())
195                 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
196                                         SPGIST_NULL_BLKNO, page, true);
197
198         /*
199          * An immediate sync is required even if we xlog'd the pages, because the
200          * writes did not go through shared buffers and therefore a concurrent
201          * checkpoint may have moved the redo pointer past our xlog record.
202          */
203         smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
204 }
205
206 /*
207  * Insert one new tuple into an SPGiST index.
208  */
209 bool
210 spginsert(Relation index, Datum *values, bool *isnull,
211                   ItemPointer ht_ctid, Relation heapRel,
212                   IndexUniqueCheck checkUnique)
213 {
214         SpGistState spgstate;
215         MemoryContext oldCtx;
216         MemoryContext insertCtx;
217
218         insertCtx = AllocSetContextCreate(CurrentMemoryContext,
219                                                                           "SP-GiST insert temporary context",
220                                                                           ALLOCSET_DEFAULT_MINSIZE,
221                                                                           ALLOCSET_DEFAULT_INITSIZE,
222                                                                           ALLOCSET_DEFAULT_MAXSIZE);
223         oldCtx = MemoryContextSwitchTo(insertCtx);
224
225         initSpGistState(&spgstate, index);
226
227         /*
228          * We might have to repeat spgdoinsert() multiple times, if conflicts
229          * occur with concurrent insertions.  If so, reset the insertCtx each time
230          * to avoid cumulative memory consumption.  That means we also have to
231          * redo initSpGistState(), but it's cheap enough not to matter.
232          */
233         while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
234         {
235                 MemoryContextReset(insertCtx);
236                 initSpGistState(&spgstate, index);
237         }
238
239         SpGistUpdateMetaPage(index);
240
241         MemoryContextSwitchTo(oldCtx);
242         MemoryContextDelete(insertCtx);
243
244         /* return false since we've not done any unique check */
245         return false;
246 }