Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * spgxlog.c
4 : : * WAL replay logic for SP-GiST
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/spgist/spgxlog.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/bufmask.h"
18 : : #include "access/spgist_private.h"
19 : : #include "access/spgxlog.h"
20 : : #include "access/xlogutils.h"
21 : : #include "storage/standby.h"
22 : : #include "utils/memutils.h"
23 : :
24 : :
25 : : static MemoryContext opCtx; /* working memory for operations */
26 : :
27 : :
28 : : /*
29 : : * Prepare a dummy SpGistState, with just the minimum info needed for replay.
30 : : *
31 : : * At present, all we need is enough info to support spgFormDeadTuple(),
32 : : * plus the isBuild flag.
33 : : */
34 : : static void
5115 tgl@sss.pgh.pa.us 35 :CBC 526 : fillFakeState(SpGistState *state, spgxlogState stateSrc)
36 : : {
37 : 526 : memset(state, 0, sizeof(*state));
38 : :
549 39 : 526 : state->redirectXid = stateSrc.redirectXid;
5115 40 : 526 : state->isBuild = stateSrc.isBuild;
41 : 526 : state->deadTupleStorage = palloc0(SGDTSIZE);
42 : 526 : }
43 : :
44 : : /*
45 : : * Add a leaf tuple, or replace an existing placeholder tuple. This is used
46 : : * to replay SpGistPageAddNewItem() operations. If the offset points at an
47 : : * existing tuple, it had better be a placeholder tuple.
48 : : */
49 : : static void
52 peter@eisentraut.org 50 :GNC 70715 : addOrReplaceTuple(Page page, const void *tuple, int size, OffsetNumber offset)
51 : : {
5115 tgl@sss.pgh.pa.us 52 [ + + ]:CBC 70715 : if (offset <= PageGetMaxOffsetNumber(page))
53 : : {
54 : 19147 : SpGistDeadTuple dt = (SpGistDeadTuple) PageGetItem(page,
3102 55 : 19147 : PageGetItemId(page, offset));
56 : :
5115 57 [ - + ]: 19147 : if (dt->tupstate != SPGIST_PLACEHOLDER)
5115 tgl@sss.pgh.pa.us 58 [ # # ]:UBC 0 : elog(ERROR, "SPGiST tuple to be replaced is not a placeholder");
59 : :
5115 tgl@sss.pgh.pa.us 60 [ - + ]:CBC 19147 : Assert(SpGistPageGetOpaque(page)->nPlaceholder > 0);
61 : 19147 : SpGistPageGetOpaque(page)->nPlaceholder--;
62 : :
63 : 19147 : PageIndexTupleDelete(page, offset);
64 : : }
65 : :
66 [ - + ]: 70715 : Assert(offset <= PageGetMaxOffsetNumber(page) + 1);
67 : :
68 [ - + ]: 70715 : if (PageAddItem(page, tuple, size, offset, false, false) != offset)
5115 tgl@sss.pgh.pa.us 69 [ # # ]:UBC 0 : elog(ERROR, "failed to add item of size %u to SPGiST index page",
70 : : size);
5115 tgl@sss.pgh.pa.us 71 :CBC 70715 : }
72 : :
73 : : static void
4046 heikki.linnakangas@i 74 : 38968 : spgRedoAddLeaf(XLogReaderState *record)
75 : : {
76 : 38968 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 77 : 38968 : char *ptr = XLogRecGetData(record);
78 : 38968 : spgxlogAddLeaf *xldata = (spgxlogAddLeaf *) ptr;
79 : : char *leafTuple;
80 : : SpGistLeafTupleData leafTupleHdr;
81 : : Buffer buffer;
82 : : Page page;
83 : : XLogRedoAction action;
84 : :
85 : 38968 : ptr += sizeof(spgxlogAddLeaf);
4214 heikki.linnakangas@i 86 : 38968 : leafTuple = ptr;
87 : : /* the leaf tuple is unaligned, so make a copy to access its header */
88 : 38968 : memcpy(&leafTupleHdr, leafTuple, sizeof(SpGistLeafTupleData));
89 : :
90 : : /*
91 : : * In normal operation we would have both current and parent pages locked
92 : : * simultaneously; but in WAL replay it should be safe to update the leaf
93 : : * page before updating the parent.
94 : : */
4145 95 [ + + ]: 38968 : if (xldata->newPage)
96 : : {
4046 97 : 1 : buffer = XLogInitBufferForRedo(record, 0);
4145 98 : 1 : SpGistInitBuffer(buffer,
3102 tgl@sss.pgh.pa.us 99 [ - + ]: 1 : SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));
4145 heikki.linnakangas@i 100 : 1 : action = BLK_NEEDS_REDO;
101 : : }
102 : : else
4046 103 : 38967 : action = XLogReadBufferForRedo(record, 0, &buffer);
104 : :
4145 105 [ + + ]: 38968 : if (action == BLK_NEEDS_REDO)
106 : : {
3529 kgrittn@postgresql.o 107 : 38790 : page = BufferGetPage(buffer);
108 : :
109 : : /* insert new tuple */
4145 heikki.linnakangas@i 110 [ + - ]: 38790 : if (xldata->offnumLeaf != xldata->offnumHeadLeaf)
111 : : {
112 : : /* normal cases, tuple was added by SpGistPageAddNewItem */
52 peter@eisentraut.org 113 :GNC 38790 : addOrReplaceTuple(page, leafTuple, leafTupleHdr.size, xldata->offnumLeaf);
114 : :
115 : : /* update head tuple's chain link if needed */
4145 heikki.linnakangas@i 116 [ + + ]:CBC 38790 : if (xldata->offnumHeadLeaf != InvalidOffsetNumber)
117 : : {
118 : : SpGistLeafTuple head;
119 : :
120 : 38149 : head = (SpGistLeafTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 121 : 38149 : PageGetItemId(page, xldata->offnumHeadLeaf));
1718 122 [ - + ]: 38149 : Assert(SGLT_GET_NEXTOFFSET(head) == SGLT_GET_NEXTOFFSET(&leafTupleHdr));
123 : 38149 : SGLT_SET_NEXTOFFSET(head, xldata->offnumLeaf);
124 : : }
125 : : }
126 : : else
127 : : {
128 : : /* replacing a DEAD tuple */
4145 heikki.linnakangas@i 129 :UBC 0 : PageIndexTupleDelete(page, xldata->offnumLeaf);
4046 130 : 0 : if (PageAddItem(page,
131 : : leafTuple, leafTupleHdr.size,
3102 tgl@sss.pgh.pa.us 132 [ # # ]: 0 : xldata->offnumLeaf, false, false) != xldata->offnumLeaf)
4145 heikki.linnakangas@i 133 [ # # ]: 0 : elog(ERROR, "failed to add item of size %u to SPGiST index page",
134 : : leafTupleHdr.size);
135 : : }
136 : :
4145 heikki.linnakangas@i 137 :CBC 38790 : PageSetLSN(page, lsn);
138 : 38790 : MarkBufferDirty(buffer);
139 : : }
140 [ + - ]: 38968 : if (BufferIsValid(buffer))
141 : 38968 : UnlockReleaseBuffer(buffer);
142 : :
143 : : /* update parent downlink if necessary */
4046 144 [ + + ]: 38968 : if (xldata->offnumParent != InvalidOffsetNumber)
145 : : {
146 [ + - ]: 120 : if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
147 : : {
148 : : SpGistInnerTuple tuple;
149 : : BlockNumber blknoLeaf;
150 : :
151 : 120 : XLogRecGetBlockTag(record, 0, NULL, NULL, &blknoLeaf);
152 : :
3529 kgrittn@postgresql.o 153 : 120 : page = BufferGetPage(buffer);
154 : :
4145 heikki.linnakangas@i 155 : 120 : tuple = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 156 : 120 : PageGetItemId(page, xldata->offnumParent));
157 : :
4145 heikki.linnakangas@i 158 : 120 : spgUpdateNodeLink(tuple, xldata->nodeI,
4046 159 : 120 : blknoLeaf, xldata->offnumLeaf);
160 : :
4145 161 : 120 : PageSetLSN(page, lsn);
162 : 120 : MarkBufferDirty(buffer);
163 : : }
164 [ + - ]: 120 : if (BufferIsValid(buffer))
165 : 120 : UnlockReleaseBuffer(buffer);
166 : : }
5115 tgl@sss.pgh.pa.us 167 : 38968 : }
168 : :
169 : : static void
4046 heikki.linnakangas@i 170 : 76 : spgRedoMoveLeafs(XLogReaderState *record)
171 : : {
172 : 76 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 173 : 76 : char *ptr = XLogRecGetData(record);
174 : 76 : spgxlogMoveLeafs *xldata = (spgxlogMoveLeafs *) ptr;
175 : : SpGistState state;
176 : : OffsetNumber *toDelete;
177 : : OffsetNumber *toInsert;
178 : : int nInsert;
179 : : Buffer buffer;
180 : : Page page;
181 : : XLogRedoAction action;
182 : : BlockNumber blknoDst;
183 : :
4046 heikki.linnakangas@i 184 : 76 : XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoDst);
185 : :
5115 tgl@sss.pgh.pa.us 186 : 76 : fillFakeState(&state, xldata->stateSrc);
187 : :
188 [ - + ]: 76 : nInsert = xldata->replaceDead ? 1 : xldata->nMoves + 1;
189 : :
4214 heikki.linnakangas@i 190 : 76 : ptr += SizeOfSpgxlogMoveLeafs;
5115 tgl@sss.pgh.pa.us 191 : 76 : toDelete = (OffsetNumber *) ptr;
4214 heikki.linnakangas@i 192 : 76 : ptr += sizeof(OffsetNumber) * xldata->nMoves;
5115 tgl@sss.pgh.pa.us 193 : 76 : toInsert = (OffsetNumber *) ptr;
4214 heikki.linnakangas@i 194 : 76 : ptr += sizeof(OffsetNumber) * nInsert;
195 : :
196 : : /* now ptr points to the list of leaf tuples */
197 : :
198 : : /*
199 : : * In normal operation we would have all three pages (source, dest, and
200 : : * parent) locked simultaneously; but in WAL replay it should be safe to
201 : : * update them one at a time, as long as we do it in the right order.
202 : : */
203 : :
204 : : /* Insert tuples on the dest page (do first, so redirect is valid) */
4145 205 [ + + ]: 76 : if (xldata->newPage)
206 : : {
4046 207 : 32 : buffer = XLogInitBufferForRedo(record, 1);
4145 208 : 32 : SpGistInitBuffer(buffer,
3102 tgl@sss.pgh.pa.us 209 [ - + ]: 32 : SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));
4145 heikki.linnakangas@i 210 : 32 : action = BLK_NEEDS_REDO;
211 : : }
212 : : else
4046 213 : 44 : action = XLogReadBufferForRedo(record, 1, &buffer);
214 : :
4145 215 [ + + ]: 76 : if (action == BLK_NEEDS_REDO)
216 : : {
217 : : int i;
218 : :
3529 kgrittn@postgresql.o 219 : 75 : page = BufferGetPage(buffer);
220 : :
4145 heikki.linnakangas@i 221 [ + + ]: 3335 : for (i = 0; i < nInsert; i++)
222 : : {
223 : : char *leafTuple;
224 : : SpGistLeafTupleData leafTupleHdr;
225 : :
226 : : /*
227 : : * the tuples are not aligned, so must copy to access the size
228 : : * field.
229 : : */
230 : 3260 : leafTuple = ptr;
4046 231 : 3260 : memcpy(&leafTupleHdr, leafTuple,
232 : : sizeof(SpGistLeafTupleData));
233 : :
52 peter@eisentraut.org 234 :GNC 3260 : addOrReplaceTuple(page, leafTuple, leafTupleHdr.size, toInsert[i]);
4145 heikki.linnakangas@i 235 :CBC 3260 : ptr += leafTupleHdr.size;
236 : : }
237 : :
238 : 75 : PageSetLSN(page, lsn);
239 : 75 : MarkBufferDirty(buffer);
240 : : }
241 [ + - ]: 76 : if (BufferIsValid(buffer))
242 : 76 : UnlockReleaseBuffer(buffer);
243 : :
244 : : /* Delete tuples from the source page, inserting a redirection pointer */
4046 245 [ + - ]: 76 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
246 : : {
3529 kgrittn@postgresql.o 247 : 76 : page = BufferGetPage(buffer);
248 : :
4145 heikki.linnakangas@i 249 : 76 : spgPageIndexMultiDelete(&state, page, toDelete, xldata->nMoves,
3102 tgl@sss.pgh.pa.us 250 : 76 : state.isBuild ? SPGIST_PLACEHOLDER : SPGIST_REDIRECT,
251 : : SPGIST_PLACEHOLDER,
252 : : blknoDst,
4145 heikki.linnakangas@i 253 [ - + ]: 76 : toInsert[nInsert - 1]);
254 : :
255 : 76 : PageSetLSN(page, lsn);
256 : 76 : MarkBufferDirty(buffer);
257 : : }
258 [ + - ]: 76 : if (BufferIsValid(buffer))
259 : 76 : UnlockReleaseBuffer(buffer);
260 : :
261 : : /* And update the parent downlink */
4046 262 [ + + ]: 76 : if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
263 : : {
264 : : SpGistInnerTuple tuple;
265 : :
3529 kgrittn@postgresql.o 266 : 75 : page = BufferGetPage(buffer);
267 : :
4145 heikki.linnakangas@i 268 : 75 : tuple = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 269 : 75 : PageGetItemId(page, xldata->offnumParent));
270 : :
4145 heikki.linnakangas@i 271 : 75 : spgUpdateNodeLink(tuple, xldata->nodeI,
4046 272 : 75 : blknoDst, toInsert[nInsert - 1]);
273 : :
4145 274 : 75 : PageSetLSN(page, lsn);
275 : 75 : MarkBufferDirty(buffer);
276 : : }
277 [ + - ]: 76 : if (BufferIsValid(buffer))
278 : 76 : UnlockReleaseBuffer(buffer);
5115 tgl@sss.pgh.pa.us 279 : 76 : }
280 : :
281 : : static void
4046 heikki.linnakangas@i 282 : 101 : spgRedoAddNode(XLogReaderState *record)
283 : : {
284 : 101 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 285 : 101 : char *ptr = XLogRecGetData(record);
286 : 101 : spgxlogAddNode *xldata = (spgxlogAddNode *) ptr;
287 : : char *innerTuple;
288 : : SpGistInnerTupleData innerTupleHdr;
289 : : SpGistState state;
290 : : Buffer buffer;
291 : : Page page;
292 : : XLogRedoAction action;
293 : :
294 : 101 : ptr += sizeof(spgxlogAddNode);
4214 heikki.linnakangas@i 295 : 101 : innerTuple = ptr;
296 : : /* the tuple is unaligned, so make a copy to access its header */
297 : 101 : memcpy(&innerTupleHdr, innerTuple, sizeof(SpGistInnerTupleData));
298 : :
5115 tgl@sss.pgh.pa.us 299 : 101 : fillFakeState(&state, xldata->stateSrc);
300 : :
4046 heikki.linnakangas@i 301 [ + + - + ]: 101 : if (!XLogRecHasBlockRef(record, 1))
302 : : {
303 : : /* update in place */
304 [ - + ]: 100 : Assert(xldata->parentBlk == -1);
305 [ + - ]: 100 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
306 : : {
3529 kgrittn@postgresql.o 307 : 100 : page = BufferGetPage(buffer);
308 : :
4145 heikki.linnakangas@i 309 : 100 : PageIndexTupleDelete(page, xldata->offnum);
52 peter@eisentraut.org 310 :GNC 100 : if (PageAddItem(page, innerTuple, innerTupleHdr.size,
311 : : xldata->offnum,
4046 heikki.linnakangas@i 312 [ - + ]:CBC 100 : false, false) != xldata->offnum)
4145 heikki.linnakangas@i 313 [ # # ]:UBC 0 : elog(ERROR, "failed to add item of size %u to SPGiST index page",
314 : : innerTupleHdr.size);
315 : :
4145 heikki.linnakangas@i 316 :CBC 100 : PageSetLSN(page, lsn);
317 : 100 : MarkBufferDirty(buffer);
318 : : }
319 [ + - ]: 200 : if (BufferIsValid(buffer))
320 : 100 : UnlockReleaseBuffer(buffer);
321 : : }
322 : : else
323 : : {
324 : : BlockNumber blkno;
325 : : BlockNumber blknoNew;
326 : :
4046 327 : 1 : XLogRecGetBlockTag(record, 0, NULL, NULL, &blkno);
328 : 1 : XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoNew);
329 : :
330 : : /*
331 : : * In normal operation we would have all three pages (source, dest,
332 : : * and parent) locked simultaneously; but in WAL replay it should be
333 : : * safe to update them one at a time, as long as we do it in the right
334 : : * order. We must insert the new tuple before replacing the old tuple
335 : : * with the redirect tuple.
336 : : */
337 : :
338 : : /* Install new tuple first so redirect is valid */
4145 339 [ + - ]: 1 : if (xldata->newPage)
340 : : {
341 : : /* AddNode is not used for nulls pages */
4046 342 : 1 : buffer = XLogInitBufferForRedo(record, 1);
4145 343 : 1 : SpGistInitBuffer(buffer, 0);
344 : 1 : action = BLK_NEEDS_REDO;
345 : : }
346 : : else
4046 heikki.linnakangas@i 347 :UBC 0 : action = XLogReadBufferForRedo(record, 1, &buffer);
4145 heikki.linnakangas@i 348 [ + - ]:CBC 1 : if (action == BLK_NEEDS_REDO)
349 : : {
3529 kgrittn@postgresql.o 350 : 1 : page = BufferGetPage(buffer);
351 : :
52 peter@eisentraut.org 352 :GNC 1 : addOrReplaceTuple(page, innerTuple, innerTupleHdr.size, xldata->offnumNew);
353 : :
354 : : /*
355 : : * If parent is in this same page, update it now.
356 : : */
4046 heikki.linnakangas@i 357 [ - + ]:CBC 1 : if (xldata->parentBlk == 1)
358 : : {
359 : : SpGistInnerTuple parentTuple;
360 : :
4046 heikki.linnakangas@i 361 :UBC 0 : parentTuple = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 362 : 0 : PageGetItemId(page, xldata->offnumParent));
363 : :
4046 heikki.linnakangas@i 364 : 0 : spgUpdateNodeLink(parentTuple, xldata->nodeI,
365 : 0 : blknoNew, xldata->offnumNew);
366 : : }
4046 heikki.linnakangas@i 367 :CBC 1 : PageSetLSN(page, lsn);
4145 368 : 1 : MarkBufferDirty(buffer);
369 : : }
370 [ + - ]: 1 : if (BufferIsValid(buffer))
371 : 1 : UnlockReleaseBuffer(buffer);
372 : :
373 : : /* Delete old tuple, replacing it with redirect or placeholder tuple */
4046 374 [ + - ]: 1 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
375 : : {
376 : : SpGistDeadTuple dt;
377 : :
3529 kgrittn@postgresql.o 378 : 1 : page = BufferGetPage(buffer);
379 : :
4145 heikki.linnakangas@i 380 [ - + ]: 1 : if (state.isBuild)
4145 heikki.linnakangas@i 381 :UBC 0 : dt = spgFormDeadTuple(&state, SPGIST_PLACEHOLDER,
382 : : InvalidBlockNumber,
383 : : InvalidOffsetNumber);
384 : : else
4145 heikki.linnakangas@i 385 :CBC 1 : dt = spgFormDeadTuple(&state, SPGIST_REDIRECT,
386 : : blknoNew,
387 : 1 : xldata->offnumNew);
388 : :
389 : 1 : PageIndexTupleDelete(page, xldata->offnum);
52 peter@eisentraut.org 390 :GNC 1 : if (PageAddItem(page, dt, dt->size,
391 : : xldata->offnum,
4145 heikki.linnakangas@i 392 [ - + ]:CBC 1 : false, false) != xldata->offnum)
4145 heikki.linnakangas@i 393 [ # # ]:UBC 0 : elog(ERROR, "failed to add item of size %u to SPGiST index page",
394 : : dt->size);
395 : :
4145 heikki.linnakangas@i 396 [ - + ]:CBC 1 : if (state.isBuild)
4145 heikki.linnakangas@i 397 :UBC 0 : SpGistPageGetOpaque(page)->nPlaceholder++;
398 : : else
4145 heikki.linnakangas@i 399 :CBC 1 : SpGistPageGetOpaque(page)->nRedirection++;
400 : :
401 : : /*
402 : : * If parent is in this same page, update it now.
403 : : */
4046 404 [ - + ]: 1 : if (xldata->parentBlk == 0)
405 : : {
406 : : SpGistInnerTuple parentTuple;
407 : :
4046 heikki.linnakangas@i 408 :UBC 0 : parentTuple = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 409 : 0 : PageGetItemId(page, xldata->offnumParent));
410 : :
4046 heikki.linnakangas@i 411 : 0 : spgUpdateNodeLink(parentTuple, xldata->nodeI,
412 : 0 : blknoNew, xldata->offnumNew);
413 : : }
4046 heikki.linnakangas@i 414 :CBC 1 : PageSetLSN(page, lsn);
4145 415 : 1 : MarkBufferDirty(buffer);
416 : : }
417 [ + - ]: 1 : if (BufferIsValid(buffer))
418 : 1 : UnlockReleaseBuffer(buffer);
419 : :
420 : : /*
421 : : * Update parent downlink (if we didn't do it as part of the source or
422 : : * destination page update already).
423 : : */
4046 424 [ + - ]: 1 : if (xldata->parentBlk == 2)
425 : : {
426 [ - + ]: 1 : if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
427 : : {
428 : : SpGistInnerTuple parentTuple;
429 : :
3529 kgrittn@postgresql.o 430 :LBC (1) : page = BufferGetPage(buffer);
431 : :
4046 heikki.linnakangas@i 432 : (1) : parentTuple = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 433 : (1) : PageGetItemId(page, xldata->offnumParent));
434 : :
4046 heikki.linnakangas@i 435 : (1) : spgUpdateNodeLink(parentTuple, xldata->nodeI,
436 : (1) : blknoNew, xldata->offnumNew);
437 : :
438 : (1) : PageSetLSN(page, lsn);
439 : (1) : MarkBufferDirty(buffer);
440 : : }
4046 heikki.linnakangas@i 441 [ + - ]:CBC 1 : if (BufferIsValid(buffer))
442 : 1 : UnlockReleaseBuffer(buffer);
443 : : }
444 : : }
5115 tgl@sss.pgh.pa.us 445 : 101 : }
446 : :
447 : : static void
4046 heikki.linnakangas@i 448 : 101 : spgRedoSplitTuple(XLogReaderState *record)
449 : : {
450 : 101 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 451 : 101 : char *ptr = XLogRecGetData(record);
452 : 101 : spgxlogSplitTuple *xldata = (spgxlogSplitTuple *) ptr;
453 : : char *prefixTuple;
454 : : SpGistInnerTupleData prefixTupleHdr;
455 : : char *postfixTuple;
456 : : SpGistInnerTupleData postfixTupleHdr;
457 : : Buffer buffer;
458 : : Page page;
459 : : XLogRedoAction action;
460 : :
461 : 101 : ptr += sizeof(spgxlogSplitTuple);
4214 heikki.linnakangas@i 462 : 101 : prefixTuple = ptr;
463 : : /* the prefix tuple is unaligned, so make a copy to access its header */
464 : 101 : memcpy(&prefixTupleHdr, prefixTuple, sizeof(SpGistInnerTupleData));
465 : 101 : ptr += prefixTupleHdr.size;
466 : 101 : postfixTuple = ptr;
467 : : /* postfix tuple is also unaligned */
468 : 101 : memcpy(&postfixTupleHdr, postfixTuple, sizeof(SpGistInnerTupleData));
469 : :
470 : : /*
471 : : * In normal operation we would have both pages locked simultaneously; but
472 : : * in WAL replay it should be safe to update them one at a time, as long
473 : : * as we do it in the right order.
474 : : */
475 : :
476 : : /* insert postfix tuple first to avoid dangling link */
4046 477 [ + + ]: 101 : if (!xldata->postfixBlkSame)
478 : : {
4145 479 [ + + ]: 27 : if (xldata->newPage)
480 : : {
4046 481 : 1 : buffer = XLogInitBufferForRedo(record, 1);
482 : : /* SplitTuple is not used for nulls pages */
4145 483 : 1 : SpGistInitBuffer(buffer, 0);
484 : 1 : action = BLK_NEEDS_REDO;
485 : : }
486 : : else
4046 487 : 26 : action = XLogReadBufferForRedo(record, 1, &buffer);
4145 488 [ + - ]: 27 : if (action == BLK_NEEDS_REDO)
489 : : {
3529 kgrittn@postgresql.o 490 : 27 : page = BufferGetPage(buffer);
491 : :
52 peter@eisentraut.org 492 :GNC 27 : addOrReplaceTuple(page, postfixTuple, postfixTupleHdr.size, xldata->offnumPostfix);
493 : :
4145 heikki.linnakangas@i 494 :CBC 27 : PageSetLSN(page, lsn);
495 : 27 : MarkBufferDirty(buffer);
496 : : }
497 [ + - ]: 27 : if (BufferIsValid(buffer))
498 : 27 : UnlockReleaseBuffer(buffer);
499 : : }
500 : :
501 : : /* now handle the original page */
4046 502 [ + - ]: 101 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
503 : : {
3529 kgrittn@postgresql.o 504 : 101 : page = BufferGetPage(buffer);
505 : :
4145 heikki.linnakangas@i 506 : 101 : PageIndexTupleDelete(page, xldata->offnumPrefix);
52 peter@eisentraut.org 507 :GNC 101 : if (PageAddItem(page, prefixTuple, prefixTupleHdr.size,
3102 tgl@sss.pgh.pa.us 508 [ - + ]:CBC 101 : xldata->offnumPrefix, false, false) != xldata->offnumPrefix)
4145 heikki.linnakangas@i 509 [ # # ]:UBC 0 : elog(ERROR, "failed to add item of size %u to SPGiST index page",
510 : : prefixTupleHdr.size);
511 : :
4046 heikki.linnakangas@i 512 [ + + ]:CBC 101 : if (xldata->postfixBlkSame)
52 peter@eisentraut.org 513 :GNC 74 : addOrReplaceTuple(page, postfixTuple, postfixTupleHdr.size, xldata->offnumPostfix);
514 : :
4145 heikki.linnakangas@i 515 :CBC 101 : PageSetLSN(page, lsn);
516 : 101 : MarkBufferDirty(buffer);
517 : : }
518 [ + - ]: 101 : if (BufferIsValid(buffer))
519 : 101 : UnlockReleaseBuffer(buffer);
5115 tgl@sss.pgh.pa.us 520 : 101 : }
521 : :
522 : : static void
4046 heikki.linnakangas@i 523 : 204 : spgRedoPickSplit(XLogReaderState *record)
524 : : {
525 : 204 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 526 : 204 : char *ptr = XLogRecGetData(record);
527 : 204 : spgxlogPickSplit *xldata = (spgxlogPickSplit *) ptr;
528 : : char *innerTuple;
529 : : SpGistInnerTupleData innerTupleHdr;
530 : : SpGistState state;
531 : : OffsetNumber *toDelete;
532 : : OffsetNumber *toInsert;
533 : : uint8 *leafPageSelect;
534 : : Buffer srcBuffer;
535 : : Buffer destBuffer;
536 : : Buffer innerBuffer;
537 : : Page srcPage;
538 : : Page destPage;
539 : : Page page;
540 : : int i;
541 : : BlockNumber blknoInner;
542 : : XLogRedoAction action;
543 : :
4046 heikki.linnakangas@i 544 : 204 : XLogRecGetBlockTag(record, 2, NULL, NULL, &blknoInner);
545 : :
5115 tgl@sss.pgh.pa.us 546 : 204 : fillFakeState(&state, xldata->stateSrc);
547 : :
4214 heikki.linnakangas@i 548 : 204 : ptr += SizeOfSpgxlogPickSplit;
5115 tgl@sss.pgh.pa.us 549 : 204 : toDelete = (OffsetNumber *) ptr;
4214 heikki.linnakangas@i 550 : 204 : ptr += sizeof(OffsetNumber) * xldata->nDelete;
5115 tgl@sss.pgh.pa.us 551 : 204 : toInsert = (OffsetNumber *) ptr;
4214 heikki.linnakangas@i 552 : 204 : ptr += sizeof(OffsetNumber) * xldata->nInsert;
5115 tgl@sss.pgh.pa.us 553 : 204 : leafPageSelect = (uint8 *) ptr;
4214 heikki.linnakangas@i 554 : 204 : ptr += sizeof(uint8) * xldata->nInsert;
555 : :
556 : 204 : innerTuple = ptr;
557 : : /* the inner tuple is unaligned, so make a copy to access its header */
558 : 204 : memcpy(&innerTupleHdr, innerTuple, sizeof(SpGistInnerTupleData));
559 : 204 : ptr += innerTupleHdr.size;
560 : :
561 : : /* now ptr points to the list of leaf tuples */
562 : :
4046 563 [ + + ]: 204 : if (xldata->isRootSplit)
564 : : {
565 : : /* when splitting root, we touch it only in the guise of new inner */
5115 tgl@sss.pgh.pa.us 566 : 3 : srcBuffer = InvalidBuffer;
4784 567 : 3 : srcPage = NULL;
568 : : }
5115 569 [ - + ]: 201 : else if (xldata->initSrc)
570 : : {
571 : : /* just re-init the source page */
4046 heikki.linnakangas@i 572 :UBC 0 : srcBuffer = XLogInitBufferForRedo(record, 0);
111 peter@eisentraut.org 573 :UNC 0 : srcPage = BufferGetPage(srcBuffer);
574 : :
5030 tgl@sss.pgh.pa.us 575 :UBC 0 : SpGistInitBuffer(srcBuffer,
3102 576 [ # # ]: 0 : SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));
577 : : /* don't update LSN etc till we're done with it */
578 : : }
579 : : else
580 : : {
581 : : /*
582 : : * Delete the specified tuples from source page. (In case we're in
583 : : * Hot Standby, we need to hold lock on the page till we're done
584 : : * inserting leaf tuples and the new inner tuple, else the added
585 : : * redirect tuple will be a dangling link.)
586 : : */
4046 heikki.linnakangas@i 587 :CBC 201 : srcPage = NULL;
588 [ + + ]: 201 : if (XLogReadBufferForRedo(record, 0, &srcBuffer) == BLK_NEEDS_REDO)
589 : : {
3529 kgrittn@postgresql.o 590 : 200 : srcPage = BufferGetPage(srcBuffer);
591 : :
592 : : /*
593 : : * We have it a bit easier here than in doPickSplit(), because we
594 : : * know the inner tuple's location already, so we can inject the
595 : : * correct redirection tuple now.
596 : : */
4145 heikki.linnakangas@i 597 [ + - ]: 200 : if (!state.isBuild)
598 : 200 : spgPageIndexMultiDelete(&state, srcPage,
599 : 200 : toDelete, xldata->nDelete,
600 : : SPGIST_REDIRECT,
601 : : SPGIST_PLACEHOLDER,
602 : : blknoInner,
603 : 200 : xldata->offnumInner);
604 : : else
4145 heikki.linnakangas@i 605 :UBC 0 : spgPageIndexMultiDelete(&state, srcPage,
606 : 0 : toDelete, xldata->nDelete,
607 : : SPGIST_PLACEHOLDER,
608 : : SPGIST_PLACEHOLDER,
609 : : InvalidBlockNumber,
610 : : InvalidOffsetNumber);
611 : :
612 : : /* don't update LSN etc till we're done with it */
613 : : }
614 : : }
615 : :
616 : : /* try to access dest page if any */
4046 heikki.linnakangas@i 617 [ + - - + ]:CBC 204 : if (!XLogRecHasBlockRef(record, 1))
618 : : {
5115 tgl@sss.pgh.pa.us 619 :UBC 0 : destBuffer = InvalidBuffer;
4784 620 : 0 : destPage = NULL;
621 : : }
5115 tgl@sss.pgh.pa.us 622 [ + + ]:CBC 204 : else if (xldata->initDest)
623 : : {
624 : : /* just re-init the dest page */
4046 heikki.linnakangas@i 625 : 189 : destBuffer = XLogInitBufferForRedo(record, 1);
111 peter@eisentraut.org 626 :GNC 189 : destPage = BufferGetPage(destBuffer);
627 : :
5030 tgl@sss.pgh.pa.us 628 :CBC 189 : SpGistInitBuffer(destBuffer,
3102 629 [ - + ]: 189 : SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));
630 : : /* don't update LSN etc till we're done with it */
631 : : }
632 : : else
633 : : {
634 : : /*
635 : : * We could probably release the page lock immediately in the
636 : : * full-page-image case, but for safety let's hold it till later.
637 : : */
4046 heikki.linnakangas@i 638 [ + + ]: 15 : if (XLogReadBufferForRedo(record, 1, &destBuffer) == BLK_NEEDS_REDO)
111 peter@eisentraut.org 639 :GNC 14 : destPage = BufferGetPage(destBuffer);
640 : : else
4145 heikki.linnakangas@i 641 :GBC 1 : destPage = NULL; /* don't do any page updates */
642 : : }
643 : :
644 : : /* restore leaf tuples to src and/or dest page */
5115 tgl@sss.pgh.pa.us 645 [ + + ]:CBC 28626 : for (i = 0; i < xldata->nInsert; i++)
646 : : {
647 : : char *leafTuple;
648 : : SpGistLeafTupleData leafTupleHdr;
649 : :
650 : : /* the tuples are not aligned, so must copy to access the size field. */
4214 heikki.linnakangas@i 651 : 28422 : leafTuple = ptr;
652 : 28422 : memcpy(&leafTupleHdr, leafTuple, sizeof(SpGistLeafTupleData));
653 : 28422 : ptr += leafTupleHdr.size;
654 : :
4784 tgl@sss.pgh.pa.us 655 [ + + ]: 28422 : page = leafPageSelect[i] ? destPage : srcPage;
656 [ + + ]: 28422 : if (page == NULL)
5115 tgl@sss.pgh.pa.us 657 :GBC 48 : continue; /* no need to touch this page */
658 : :
52 peter@eisentraut.org 659 :GNC 28374 : addOrReplaceTuple(page, leafTuple, leafTupleHdr.size, toInsert[i]);
660 : : }
661 : :
662 : : /* Now update src and dest page LSNs if needed */
4784 tgl@sss.pgh.pa.us 663 [ + + ]:CBC 204 : if (srcPage != NULL)
664 : : {
665 : 200 : PageSetLSN(srcPage, lsn);
666 : 200 : MarkBufferDirty(srcBuffer);
667 : : }
668 [ + + ]: 204 : if (destPage != NULL)
669 : : {
670 : 203 : PageSetLSN(destPage, lsn);
671 : 203 : MarkBufferDirty(destBuffer);
672 : : }
673 : :
674 : : /* restore new inner tuple */
4145 heikki.linnakangas@i 675 [ + + ]: 204 : if (xldata->initInner)
676 : : {
4046 677 : 6 : innerBuffer = XLogInitBufferForRedo(record, 2);
678 [ - + ]: 6 : SpGistInitBuffer(innerBuffer, (xldata->storesNulls ? SPGIST_NULLS : 0));
4145 679 : 6 : action = BLK_NEEDS_REDO;
680 : : }
681 : : else
4046 682 : 198 : action = XLogReadBufferForRedo(record, 2, &innerBuffer);
683 : :
4145 684 [ + + ]: 204 : if (action == BLK_NEEDS_REDO)
685 : : {
3529 kgrittn@postgresql.o 686 : 189 : page = BufferGetPage(innerBuffer);
687 : :
52 peter@eisentraut.org 688 :GNC 189 : addOrReplaceTuple(page, innerTuple, innerTupleHdr.size, xldata->offnumInner);
689 : :
690 : : /* if inner is also parent, update link while we're here */
4046 heikki.linnakangas@i 691 [ + + ]:CBC 189 : if (xldata->innerIsParent)
692 : : {
693 : : SpGistInnerTuple parent;
694 : :
4145 695 : 171 : parent = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 696 : 171 : PageGetItemId(page, xldata->offnumParent));
4145 heikki.linnakangas@i 697 : 171 : spgUpdateNodeLink(parent, xldata->nodeI,
4046 698 : 171 : blknoInner, xldata->offnumInner);
699 : : }
700 : :
4145 701 : 189 : PageSetLSN(page, lsn);
702 : 189 : MarkBufferDirty(innerBuffer);
703 : : }
704 [ + - ]: 204 : if (BufferIsValid(innerBuffer))
705 : 204 : UnlockReleaseBuffer(innerBuffer);
706 : :
707 : : /*
708 : : * Now we can release the leaf-page locks. It's okay to do this before
709 : : * updating the parent downlink.
710 : : */
4784 tgl@sss.pgh.pa.us 711 [ + + ]: 204 : if (BufferIsValid(srcBuffer))
712 : 201 : UnlockReleaseBuffer(srcBuffer);
713 [ + - ]: 204 : if (BufferIsValid(destBuffer))
714 : 204 : UnlockReleaseBuffer(destBuffer);
715 : :
716 : : /* update parent downlink, unless we did it above */
4046 heikki.linnakangas@i 717 [ + + + - ]: 204 : if (XLogRecHasBlockRef(record, 3))
5115 tgl@sss.pgh.pa.us 718 : 15 : {
719 : : Buffer parentBuffer;
720 : :
4046 heikki.linnakangas@i 721 [ + + ]: 15 : if (XLogReadBufferForRedo(record, 3, &parentBuffer) == BLK_NEEDS_REDO)
722 : : {
723 : : SpGistInnerTuple parent;
724 : :
3529 kgrittn@postgresql.o 725 : 14 : page = BufferGetPage(parentBuffer);
726 : :
4145 heikki.linnakangas@i 727 : 14 : parent = (SpGistInnerTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 728 : 14 : PageGetItemId(page, xldata->offnumParent));
4145 heikki.linnakangas@i 729 : 14 : spgUpdateNodeLink(parent, xldata->nodeI,
4046 730 : 14 : blknoInner, xldata->offnumInner);
731 : :
4145 732 : 14 : PageSetLSN(page, lsn);
733 : 14 : MarkBufferDirty(parentBuffer);
734 : : }
735 [ + - ]: 15 : if (BufferIsValid(parentBuffer))
736 : 15 : UnlockReleaseBuffer(parentBuffer);
737 : : }
738 : : else
4046 739 [ + + - + ]: 189 : Assert(xldata->innerIsParent || xldata->isRootSplit);
5115 tgl@sss.pgh.pa.us 740 : 204 : }
741 : :
742 : : static void
4046 heikki.linnakangas@i 743 : 145 : spgRedoVacuumLeaf(XLogReaderState *record)
744 : : {
745 : 145 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 746 : 145 : char *ptr = XLogRecGetData(record);
747 : 145 : spgxlogVacuumLeaf *xldata = (spgxlogVacuumLeaf *) ptr;
748 : : OffsetNumber *toDead;
749 : : OffsetNumber *toPlaceholder;
750 : : OffsetNumber *moveSrc;
751 : : OffsetNumber *moveDest;
752 : : OffsetNumber *chainSrc;
753 : : OffsetNumber *chainDest;
754 : : SpGistState state;
755 : : Buffer buffer;
756 : : Page page;
757 : : int i;
758 : :
759 : 145 : fillFakeState(&state, xldata->stateSrc);
760 : :
4214 heikki.linnakangas@i 761 : 145 : ptr += SizeOfSpgxlogVacuumLeaf;
5115 tgl@sss.pgh.pa.us 762 : 145 : toDead = (OffsetNumber *) ptr;
763 : 145 : ptr += sizeof(OffsetNumber) * xldata->nDead;
764 : 145 : toPlaceholder = (OffsetNumber *) ptr;
765 : 145 : ptr += sizeof(OffsetNumber) * xldata->nPlaceholder;
766 : 145 : moveSrc = (OffsetNumber *) ptr;
767 : 145 : ptr += sizeof(OffsetNumber) * xldata->nMove;
768 : 145 : moveDest = (OffsetNumber *) ptr;
769 : 145 : ptr += sizeof(OffsetNumber) * xldata->nMove;
770 : 145 : chainSrc = (OffsetNumber *) ptr;
771 : 145 : ptr += sizeof(OffsetNumber) * xldata->nChain;
772 : 145 : chainDest = (OffsetNumber *) ptr;
773 : :
4046 heikki.linnakangas@i 774 [ + + ]: 145 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
775 : : {
3529 kgrittn@postgresql.o 776 : 9 : page = BufferGetPage(buffer);
777 : :
4145 heikki.linnakangas@i 778 : 9 : spgPageIndexMultiDelete(&state, page,
779 : 9 : toDead, xldata->nDead,
780 : : SPGIST_DEAD, SPGIST_DEAD,
781 : : InvalidBlockNumber,
782 : : InvalidOffsetNumber);
783 : :
784 : 9 : spgPageIndexMultiDelete(&state, page,
785 : 9 : toPlaceholder, xldata->nPlaceholder,
786 : : SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
787 : : InvalidBlockNumber,
788 : : InvalidOffsetNumber);
789 : :
790 : : /* see comments in vacuumLeafPage() */
791 [ + + ]: 17 : for (i = 0; i < xldata->nMove; i++)
792 : : {
793 : 8 : ItemId idSrc = PageGetItemId(page, moveSrc[i]);
794 : 8 : ItemId idDest = PageGetItemId(page, moveDest[i]);
795 : : ItemIdData tmp;
796 : :
797 : 8 : tmp = *idSrc;
798 : 8 : *idSrc = *idDest;
799 : 8 : *idDest = tmp;
800 : : }
801 : :
802 : 9 : spgPageIndexMultiDelete(&state, page,
803 : 9 : moveSrc, xldata->nMove,
804 : : SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
805 : : InvalidBlockNumber,
806 : : InvalidOffsetNumber);
807 : :
808 [ + + ]: 21 : for (i = 0; i < xldata->nChain; i++)
809 : : {
810 : : SpGistLeafTuple lt;
811 : :
812 : 12 : lt = (SpGistLeafTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 813 : 12 : PageGetItemId(page, chainSrc[i]));
4145 heikki.linnakangas@i 814 [ - + ]: 12 : Assert(lt->tupstate == SPGIST_LIVE);
1718 tgl@sss.pgh.pa.us 815 : 12 : SGLT_SET_NEXTOFFSET(lt, chainDest[i]);
816 : : }
817 : :
4145 heikki.linnakangas@i 818 : 9 : PageSetLSN(page, lsn);
819 : 9 : MarkBufferDirty(buffer);
820 : : }
821 [ + - ]: 145 : if (BufferIsValid(buffer))
822 : 145 : UnlockReleaseBuffer(buffer);
5115 tgl@sss.pgh.pa.us 823 : 145 : }
824 : :
825 : : static void
4046 heikki.linnakangas@i 826 :UBC 0 : spgRedoVacuumRoot(XLogReaderState *record)
827 : : {
828 : 0 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 829 : 0 : char *ptr = XLogRecGetData(record);
830 : 0 : spgxlogVacuumRoot *xldata = (spgxlogVacuumRoot *) ptr;
831 : : OffsetNumber *toDelete;
832 : : Buffer buffer;
833 : : Page page;
834 : :
4214 heikki.linnakangas@i 835 : 0 : toDelete = xldata->offsets;
836 : :
4046 837 [ # # ]: 0 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
838 : : {
3529 kgrittn@postgresql.o 839 : 0 : page = BufferGetPage(buffer);
840 : :
841 : : /* The tuple numbers are in order */
4145 heikki.linnakangas@i 842 : 0 : PageIndexMultiDelete(page, toDelete, xldata->nDelete);
843 : :
844 : 0 : PageSetLSN(page, lsn);
845 : 0 : MarkBufferDirty(buffer);
846 : : }
847 [ # # ]: 0 : if (BufferIsValid(buffer))
848 : 0 : UnlockReleaseBuffer(buffer);
5115 tgl@sss.pgh.pa.us 849 : 0 : }
850 : :
851 : : static void
4046 heikki.linnakangas@i 852 :CBC 519 : spgRedoVacuumRedirect(XLogReaderState *record)
853 : : {
854 : 519 : XLogRecPtr lsn = record->EndRecPtr;
5115 tgl@sss.pgh.pa.us 855 : 519 : char *ptr = XLogRecGetData(record);
856 : 519 : spgxlogVacuumRedirect *xldata = (spgxlogVacuumRedirect *) ptr;
857 : : OffsetNumber *itemToPlaceholder;
858 : : Buffer buffer;
859 : :
4214 heikki.linnakangas@i 860 : 519 : itemToPlaceholder = xldata->offsets;
861 : :
862 : : /*
863 : : * If any redirection tuples are being removed, make sure there are no
864 : : * live Hot Standby transactions that might need to see them.
865 : : */
4784 tgl@sss.pgh.pa.us 866 [ + - ]: 519 : if (InHotStandby)
867 : : {
868 : : RelFileLocator locator;
869 : :
1127 pg@bowt.ie 870 : 519 : XLogRecGetBlockTag(record, 0, &locator, NULL, NULL);
871 : 519 : ResolveRecoveryConflictWithSnapshot(xldata->snapshotConflictHorizon,
986 andres@anarazel.de 872 : 519 : xldata->isCatalogRel,
873 : : locator);
874 : : }
875 : :
4046 heikki.linnakangas@i 876 [ + + ]: 519 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
877 : : {
3529 kgrittn@postgresql.o 878 : 153 : Page page = BufferGetPage(buffer);
4145 heikki.linnakangas@i 879 : 153 : SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
880 : : int i;
881 : :
882 : : /* Convert redirect pointers to plain placeholders */
883 [ + + ]: 156 : for (i = 0; i < xldata->nToPlaceholder; i++)
884 : : {
885 : : SpGistDeadTuple dt;
886 : :
887 : 3 : dt = (SpGistDeadTuple) PageGetItem(page,
3102 tgl@sss.pgh.pa.us 888 : 3 : PageGetItemId(page, itemToPlaceholder[i]));
4145 heikki.linnakangas@i 889 [ - + ]: 3 : Assert(dt->tupstate == SPGIST_REDIRECT);
890 : 3 : dt->tupstate = SPGIST_PLACEHOLDER;
891 : 3 : ItemPointerSetInvalid(&dt->pointer);
892 : : }
893 : :
894 [ - + ]: 153 : Assert(opaque->nRedirection >= xldata->nToPlaceholder);
895 : 153 : opaque->nRedirection -= xldata->nToPlaceholder;
896 : 153 : opaque->nPlaceholder += xldata->nToPlaceholder;
897 : :
898 : : /* Remove placeholder tuples at end of page */
899 [ + - ]: 153 : if (xldata->firstPlaceholder != InvalidOffsetNumber)
900 : : {
901 : 153 : int max = PageGetMaxOffsetNumber(page);
902 : : OffsetNumber *toDelete;
903 : :
8 michael@paquier.xyz 904 :GNC 153 : toDelete = palloc_array(OffsetNumber, max);
905 : :
4145 heikki.linnakangas@i 906 [ + + ]:CBC 11063 : for (i = xldata->firstPlaceholder; i <= max; i++)
907 : 10910 : toDelete[i - xldata->firstPlaceholder] = i;
908 : :
909 : 153 : i = max - xldata->firstPlaceholder + 1;
910 [ - + ]: 153 : Assert(opaque->nPlaceholder >= i);
911 : 153 : opaque->nPlaceholder -= i;
912 : :
913 : : /* The array is sorted, so can use PageIndexMultiDelete */
914 : 153 : PageIndexMultiDelete(page, toDelete, i);
915 : :
916 : 153 : pfree(toDelete);
917 : : }
918 : :
919 : 153 : PageSetLSN(page, lsn);
920 : 153 : MarkBufferDirty(buffer);
921 : : }
922 [ + - ]: 519 : if (BufferIsValid(buffer))
923 : 519 : UnlockReleaseBuffer(buffer);
5115 tgl@sss.pgh.pa.us 924 : 519 : }
925 : :
926 : : void
4046 heikki.linnakangas@i 927 : 40114 : spg_redo(XLogReaderState *record)
928 : : {
929 : 40114 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
930 : : MemoryContext oldCxt;
931 : :
5115 tgl@sss.pgh.pa.us 932 : 40114 : oldCxt = MemoryContextSwitchTo(opCtx);
933 [ + + + + : 40114 : switch (info)
+ + - +
- ]
934 : : {
935 : 38968 : case XLOG_SPGIST_ADD_LEAF:
4046 heikki.linnakangas@i 936 : 38968 : spgRedoAddLeaf(record);
5115 tgl@sss.pgh.pa.us 937 : 38968 : break;
938 : 76 : case XLOG_SPGIST_MOVE_LEAFS:
4046 heikki.linnakangas@i 939 : 76 : spgRedoMoveLeafs(record);
5115 tgl@sss.pgh.pa.us 940 : 76 : break;
941 : 101 : case XLOG_SPGIST_ADD_NODE:
4046 heikki.linnakangas@i 942 : 101 : spgRedoAddNode(record);
5115 tgl@sss.pgh.pa.us 943 : 101 : break;
944 : 101 : case XLOG_SPGIST_SPLIT_TUPLE:
4046 heikki.linnakangas@i 945 : 101 : spgRedoSplitTuple(record);
5115 tgl@sss.pgh.pa.us 946 : 101 : break;
947 : 204 : case XLOG_SPGIST_PICKSPLIT:
4046 heikki.linnakangas@i 948 : 204 : spgRedoPickSplit(record);
5115 tgl@sss.pgh.pa.us 949 : 204 : break;
950 : 145 : case XLOG_SPGIST_VACUUM_LEAF:
4046 heikki.linnakangas@i 951 : 145 : spgRedoVacuumLeaf(record);
5115 tgl@sss.pgh.pa.us 952 : 145 : break;
5115 tgl@sss.pgh.pa.us 953 :UBC 0 : case XLOG_SPGIST_VACUUM_ROOT:
4046 heikki.linnakangas@i 954 : 0 : spgRedoVacuumRoot(record);
5115 tgl@sss.pgh.pa.us 955 : 0 : break;
5115 tgl@sss.pgh.pa.us 956 :CBC 519 : case XLOG_SPGIST_VACUUM_REDIRECT:
4046 heikki.linnakangas@i 957 : 519 : spgRedoVacuumRedirect(record);
5115 tgl@sss.pgh.pa.us 958 : 519 : break;
5115 tgl@sss.pgh.pa.us 959 :UBC 0 : default:
960 [ # # ]: 0 : elog(PANIC, "spg_redo: unknown op code %u", info);
961 : : }
962 : :
5115 tgl@sss.pgh.pa.us 963 :CBC 40114 : MemoryContextSwitchTo(oldCxt);
964 : 40114 : MemoryContextReset(opCtx);
965 : 40114 : }
966 : :
967 : : void
968 : 198 : spg_xlog_startup(void)
969 : : {
970 : 198 : opCtx = AllocSetContextCreate(CurrentMemoryContext,
971 : : "SP-GiST temporary context",
972 : : ALLOCSET_DEFAULT_SIZES);
973 : 198 : }
974 : :
975 : : void
976 : 144 : spg_xlog_cleanup(void)
977 : : {
978 : 144 : MemoryContextDelete(opCtx);
979 : 144 : opCtx = NULL;
980 : 144 : }
981 : :
982 : : /*
983 : : * Mask a SpGist page before performing consistency checks on it.
984 : : */
985 : : void
3235 rhaas@postgresql.org 986 : 80268 : spg_mask(char *pagedata, BlockNumber blkno)
987 : : {
988 : 80268 : Page page = (Page) pagedata;
2968 tgl@sss.pgh.pa.us 989 : 80268 : PageHeader pagehdr = (PageHeader) page;
990 : :
3009 rhaas@postgresql.org 991 : 80268 : mask_page_lsn_and_checksum(page);
992 : :
3235 993 : 80268 : mask_page_hint_bits(page);
994 : :
995 : : /*
996 : : * Mask the unused space, but only if the page's pd_lower appears to have
997 : : * been set correctly.
998 : : */
2007 akorotkov@postgresql 999 [ + - ]: 80268 : if (pagehdr->pd_lower >= SizeOfPageHeaderData)
3235 rhaas@postgresql.org 1000 : 80268 : mask_unused_space(page);
1001 : 80268 : }
|