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