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