Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * gistxlog.c
4 : * WAL replay logic for GiST.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/gist/gistxlog.c
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/bufmask.h"
17 : #include "access/gist_private.h"
18 : #include "access/gistxlog.h"
19 : #include "access/heapam_xlog.h"
20 : #include "access/transam.h"
21 : #include "access/xloginsert.h"
22 : #include "access/xlogutils.h"
23 : #include "miscadmin.h"
24 : #include "storage/procarray.h"
25 : #include "utils/memutils.h"
26 : #include "utils/rel.h"
27 :
28 : static MemoryContext opCtx; /* working memory for operations */
29 :
30 : /*
31 : * Replay the clearing of F_FOLLOW_RIGHT flag on a child page.
32 : *
33 : * Even if the WAL record includes a full-page image, we have to update the
34 : * follow-right flag, because that change is not included in the full-page
35 : * image. To be sure that the intermediate state with the wrong flag value is
36 : * not visible to concurrent Hot Standby queries, this function handles
37 : * restoring the full-page image as well as updating the flag. (Note that
38 : * we never need to do anything else to the child page in the current WAL
39 : * action.)
40 : */
41 : static void
3062 heikki.linnakangas 42 CBC 443 : gistRedoClearFollowRight(XLogReaderState *record, uint8 block_id)
43 : {
44 443 : XLogRecPtr lsn = record->EndRecPtr;
45 : Buffer buffer;
46 : Page page;
47 : XLogRedoAction action;
48 :
49 : /*
50 : * Note that we still update the page even if it was restored from a full
51 : * page image, because the updated NSN is not included in the image.
52 : */
53 443 : action = XLogReadBufferForRedo(record, block_id, &buffer);
3161 54 443 : if (action == BLK_NEEDS_REDO || action == BLK_RESTORED)
55 : {
2545 kgrittn 56 443 : page = BufferGetPage(buffer);
57 :
3734 heikki.linnakangas 58 443 : GistPageSetNSN(page, lsn);
3800 tgl 59 443 : GistClearFollowRight(page);
60 :
61 443 : PageSetLSN(page, lsn);
62 443 : MarkBufferDirty(buffer);
63 : }
3161 heikki.linnakangas 64 443 : if (BufferIsValid(buffer))
65 443 : UnlockReleaseBuffer(buffer);
6508 teodor 66 443 : }
67 :
68 : /*
69 : * redo any page update (except page split)
70 : */
71 : static void
3062 heikki.linnakangas 72 57037 : gistRedoPageUpdateRecord(XLogReaderState *record)
73 : {
74 57037 : XLogRecPtr lsn = record->EndRecPtr;
75 57037 : gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) XLogRecGetData(record);
76 : Buffer buffer;
77 : Page page;
78 :
79 57037 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
80 : {
81 : char *begin;
82 : char *data;
83 : Size datalen;
201 tgl 84 57008 : int ninserted PG_USED_FOR_ASSERTS_ONLY = 0;
85 :
3062 heikki.linnakangas 86 57008 : data = begin = XLogRecGetBlockData(record, 0, &datalen);
87 :
2545 kgrittn 88 57008 : page = (Page) BufferGetPage(buffer);
89 :
2403 tgl 90 57008 : if (xldata->ntodelete == 1 && xldata->ntoinsert == 1)
3161 heikki.linnakangas 91 20844 : {
92 : /*
93 : * When replacing one tuple with one other tuple, we must use
94 : * PageIndexTupleOverwrite for consistency with gistplacetopage.
95 : */
2403 tgl 96 20844 : OffsetNumber offnum = *((OffsetNumber *) data);
97 : IndexTuple itup;
98 : Size itupsize;
99 :
100 20844 : data += sizeof(OffsetNumber);
101 20844 : itup = (IndexTuple) data;
102 20844 : itupsize = IndexTupleSize(itup);
103 20844 : if (!PageIndexTupleOverwrite(page, offnum, (Item) itup, itupsize))
2403 tgl 104 UBC 0 : elog(ERROR, "failed to add item to GiST index page, size %d bytes",
105 : (int) itupsize);
2403 tgl 106 CBC 20844 : data += itupsize;
107 : /* should be nothing left after consuming 1 tuple */
108 20844 : Assert(data - begin == datalen);
109 : /* update insertion count for assert check below */
110 20844 : ninserted++;
111 : }
112 36164 : else if (xldata->ntodelete > 0)
113 : {
114 : /* Otherwise, delete old tuples if any */
3161 heikki.linnakangas 115 472 : OffsetNumber *todelete = (OffsetNumber *) data;
116 :
117 472 : data += sizeof(OffsetNumber) * xldata->ntodelete;
118 :
2761 teodor 119 472 : PageIndexMultiDelete(page, todelete, xldata->ntodelete);
3161 heikki.linnakangas 120 472 : if (GistPageIsLeaf(page))
121 32 : GistMarkTuplesDeleted(page);
122 : }
123 :
124 : /* Add new tuples if any */
3062 125 57008 : if (data - begin < datalen)
126 : {
3161 127 36132 : OffsetNumber off = (PageIsEmpty(page)) ? FirstOffsetNumber :
128 36124 : OffsetNumberNext(PageGetMaxOffsetNumber(page));
129 :
3062 130 72704 : while (data - begin < datalen)
131 : {
3161 132 36572 : IndexTuple itup = (IndexTuple) data;
133 36572 : Size sz = IndexTupleSize(itup);
134 : OffsetNumber l;
135 :
136 36572 : data += sz;
137 :
138 36572 : l = PageAddItem(page, (Item) itup, sz, off, false, false);
139 36572 : if (l == InvalidOffsetNumber)
3161 heikki.linnakangas 140 UBC 0 : elog(ERROR, "failed to add item to GiST index page, size %d bytes",
141 : (int) sz);
3161 heikki.linnakangas 142 CBC 36572 : off++;
3062 143 36572 : ninserted++;
144 : }
145 : }
146 :
147 : /* Check that XLOG record contained expected number of tuples */
148 57008 : Assert(ninserted == xldata->ntoinsert);
149 :
3161 150 57008 : PageSetLSN(page, lsn);
151 57008 : MarkBufferDirty(buffer);
152 : }
153 :
154 : /*
155 : * Fix follow-right data on left child page
156 : *
157 : * This must be done while still holding the lock on the target page. Note
158 : * that even if the target page no longer exists, we still attempt to
159 : * replay the change on the child page.
160 : */
3062 161 57037 : if (XLogRecHasBlockRef(record, 1))
162 441 : gistRedoClearFollowRight(record, 1);
163 :
3161 164 57037 : if (BufferIsValid(buffer))
165 57037 : UnlockReleaseBuffer(buffer);
6508 teodor 166 57037 : }
167 :
168 :
169 : /*
170 : * redo delete on gist index page to remove tuples marked as DEAD during index
171 : * tuple insertion
172 : */
173 : static void
1570 akorotkov 174 UBC 0 : gistRedoDeleteRecord(XLogReaderState *record)
175 : {
176 0 : XLogRecPtr lsn = record->EndRecPtr;
177 0 : gistxlogDelete *xldata = (gistxlogDelete *) XLogRecGetData(record);
178 : Buffer buffer;
179 : Page page;
7 andres 180 UNC 0 : OffsetNumber *toDelete = xldata->offsets;
1570 akorotkov 181 EUB :
182 : /*
183 : * If we have any conflict processing to do, it must happen before we
184 : * update the page.
185 : *
186 : * GiST delete records can conflict with standby queries. You might think
187 : * that vacuum records would conflict as well, but we've handled that
188 : * already. XLOG_HEAP2_PRUNE records provide the highest xid cleaned by
189 : * the vacuum of the heap and so we can resolve any conflicts just once
190 : * when that arrives. After that we know that no conflicts exist from
191 : * individual gist vacuum records on that index.
192 : */
1570 akorotkov 193 UIC 0 : if (InHotStandby)
1570 akorotkov 194 EUB : {
195 : RelFileLocator rlocator;
196 :
277 rhaas 197 UNC 0 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, NULL);
1570 akorotkov 198 EUB :
143 pg 199 UNC 0 : ResolveRecoveryConflictWithSnapshot(xldata->snapshotConflictHorizon,
2 andres 200 0 : xldata->isCatalogRel,
201 : rlocator);
1570 akorotkov 202 EUB : }
203 :
1570 akorotkov 204 UIC 0 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
205 : {
206 0 : page = (Page) BufferGetPage(buffer);
1570 akorotkov 207 EUB :
7 andres 208 UNC 0 : PageIndexMultiDelete(page, toDelete, xldata->ntodelete);
1570 akorotkov 209 EUB :
1570 akorotkov 210 UBC 0 : GistClearPageHasGarbage(page);
1570 akorotkov 211 UIC 0 : GistMarkTuplesDeleted(page);
212 :
1570 akorotkov 213 UBC 0 : PageSetLSN(page, lsn);
214 0 : MarkBufferDirty(buffer);
1570 akorotkov 215 EUB : }
216 :
1570 akorotkov 217 UIC 0 : if (BufferIsValid(buffer))
218 0 : UnlockReleaseBuffer(buffer);
219 0 : }
220 :
3062 heikki.linnakangas 221 ECB : /*
222 : * Returns an array of index pointers.
223 : */
224 : static IndexTuple *
3062 heikki.linnakangas 225 GIC 901 : decodePageSplitRecord(char *begin, int len, int *n)
226 : {
227 : char *ptr;
3062 heikki.linnakangas 228 CBC 901 : int i = 0;
3062 heikki.linnakangas 229 ECB : IndexTuple *tuples;
230 :
231 : /* extract the number of tuples */
3062 heikki.linnakangas 232 GIC 901 : memcpy(n, begin, sizeof(int));
3062 heikki.linnakangas 233 CBC 901 : ptr = begin + sizeof(int);
234 :
235 901 : tuples = palloc(*n * sizeof(IndexTuple));
6408 bruce 236 ECB :
3062 heikki.linnakangas 237 CBC 78225 : for (i = 0; i < *n; i++)
238 : {
239 77324 : Assert(ptr - begin < len);
3062 heikki.linnakangas 240 GIC 77324 : tuples[i] = (IndexTuple) ptr;
3062 heikki.linnakangas 241 CBC 77324 : ptr += IndexTupleSize((IndexTuple) ptr);
242 : }
3062 heikki.linnakangas 243 GIC 901 : Assert(ptr - begin == len);
244 :
3062 heikki.linnakangas 245 CBC 901 : return tuples;
246 : }
6408 bruce 247 ECB :
6508 teodor 248 : static void
3062 heikki.linnakangas 249 CBC 448 : gistRedoPageSplitRecord(XLogReaderState *record)
250 : {
3062 heikki.linnakangas 251 GIC 448 : XLogRecPtr lsn = record->EndRecPtr;
4490 252 448 : gistxlogPageSplit *xldata = (gistxlogPageSplit *) XLogRecGetData(record);
3800 tgl 253 CBC 448 : Buffer firstbuffer = InvalidBuffer;
254 : Buffer buffer;
255 : Page page;
256 : int i;
4490 heikki.linnakangas 257 GIC 448 : bool isrootsplit = false;
258 :
259 : /*
260 : * We must hold lock on the first-listed page throughout the action,
261 : * including while updating the left child page (if any). We can unlock
262 : * remaining pages in the list as soon as they've been written, because
263 : * there is no path for concurrent queries to reach those pages without
3800 tgl 264 ECB : * first visiting the first-listed page.
265 : */
266 :
267 : /* loop around all pages */
3062 heikki.linnakangas 268 GIC 1349 : for (i = 0; i < xldata->npage; i++)
269 : {
270 : int flags;
271 : char *data;
272 : Size datalen;
3062 heikki.linnakangas 273 ECB : int num;
274 : BlockNumber blkno;
275 : IndexTuple *tuples;
276 :
3062 heikki.linnakangas 277 CBC 901 : XLogRecGetBlockTag(record, i + 1, NULL, NULL, &blkno);
3062 heikki.linnakangas 278 GIC 901 : if (blkno == GIST_ROOT_BLKNO)
279 : {
4490 heikki.linnakangas 280 CBC 5 : Assert(i == 0);
281 5 : isrootsplit = true;
4490 heikki.linnakangas 282 ECB : }
283 :
3062 heikki.linnakangas 284 CBC 901 : buffer = XLogInitBufferForRedo(record, i + 1);
2545 kgrittn 285 GIC 901 : page = (Page) BufferGetPage(buffer);
3062 heikki.linnakangas 286 901 : data = XLogRecGetBlockData(record, i + 1, &datalen);
3062 heikki.linnakangas 287 ECB :
3062 heikki.linnakangas 288 CBC 901 : tuples = decodePageSplitRecord(data, datalen, &num);
289 :
6502 teodor 290 ECB : /* ok, clear buffer */
3062 heikki.linnakangas 291 CBC 901 : if (xldata->origleaf && blkno != GIST_ROOT_BLKNO)
4490 heikki.linnakangas 292 GIC 892 : flags = F_LEAF;
293 : else
4490 heikki.linnakangas 294 CBC 9 : flags = 0;
6408 bruce 295 GIC 901 : GISTInitBuffer(buffer, flags);
6408 bruce 296 ECB :
297 : /* and fill it */
3062 heikki.linnakangas 298 CBC 901 : gistfillbuffer(page, tuples, num, FirstOffsetNumber);
6408 bruce 299 ECB :
3062 heikki.linnakangas 300 CBC 901 : if (blkno == GIST_ROOT_BLKNO)
301 : {
4490 heikki.linnakangas 302 GIC 5 : GistPageGetOpaque(page)->rightlink = InvalidBlockNumber;
3734 303 5 : GistPageSetNSN(page, xldata->orignsn);
4490 heikki.linnakangas 304 CBC 5 : GistClearFollowRight(page);
305 : }
306 : else
307 : {
3062 308 896 : if (i < xldata->npage - 1)
3062 heikki.linnakangas 309 ECB : {
310 : BlockNumber nextblkno;
311 :
3062 heikki.linnakangas 312 CBC 448 : XLogRecGetBlockTag(record, i + 2, NULL, NULL, &nextblkno);
313 448 : GistPageGetOpaque(page)->rightlink = nextblkno;
3062 heikki.linnakangas 314 ECB : }
4490 315 : else
4490 heikki.linnakangas 316 CBC 448 : GistPageGetOpaque(page)->rightlink = xldata->origrlink;
3734 heikki.linnakangas 317 GIC 896 : GistPageSetNSN(page, xldata->orignsn);
3062 heikki.linnakangas 318 CBC 896 : if (i < xldata->npage - 1 && !isrootsplit &&
4231 heikki.linnakangas 319 GIC 443 : xldata->markfollowright)
4490 320 443 : GistMarkFollowRight(page);
4490 heikki.linnakangas 321 ECB : else
4490 heikki.linnakangas 322 CBC 453 : GistClearFollowRight(page);
323 : }
4490 heikki.linnakangas 324 ECB :
6502 teodor 325 CBC 901 : PageSetLSN(page, lsn);
6218 tgl 326 GIC 901 : MarkBufferDirty(buffer);
3800 tgl 327 ECB :
3800 tgl 328 GIC 901 : if (i == 0)
329 448 : firstbuffer = buffer;
330 : else
3800 tgl 331 CBC 453 : UnlockReleaseBuffer(buffer);
6508 teodor 332 ECB : }
333 :
334 : /* Fix follow-right data on left child page, if any */
3062 heikki.linnakangas 335 CBC 448 : if (XLogRecHasBlockRef(record, 0))
336 2 : gistRedoClearFollowRight(record, 0);
337 :
338 : /* Finally, release lock on the first page */
3800 tgl 339 GIC 448 : UnlockReleaseBuffer(firstbuffer);
6508 teodor 340 GBC 448 : }
341 :
1479 heikki.linnakangas 342 EUB : /* redo page deletion */
343 : static void
1479 heikki.linnakangas 344 UIC 0 : gistRedoPageDelete(XLogReaderState *record)
345 : {
346 0 : XLogRecPtr lsn = record->EndRecPtr;
1479 heikki.linnakangas 347 UBC 0 : gistxlogPageDelete *xldata = (gistxlogPageDelete *) XLogRecGetData(record);
348 : Buffer parentBuffer;
1479 heikki.linnakangas 349 EUB : Buffer leafBuffer;
350 :
1479 heikki.linnakangas 351 UBC 0 : if (XLogReadBufferForRedo(record, 0, &leafBuffer) == BLK_NEEDS_REDO)
352 : {
353 0 : Page page = (Page) BufferGetPage(leafBuffer);
1479 heikki.linnakangas 354 EUB :
1355 heikki.linnakangas 355 UIC 0 : GistPageSetDeleted(page, xldata->deleteXid);
356 :
1479 heikki.linnakangas 357 UBC 0 : PageSetLSN(page, lsn);
1479 heikki.linnakangas 358 UIC 0 : MarkBufferDirty(leafBuffer);
1479 heikki.linnakangas 359 EUB : }
360 :
1479 heikki.linnakangas 361 UBC 0 : if (XLogReadBufferForRedo(record, 1, &parentBuffer) == BLK_NEEDS_REDO)
362 : {
363 0 : Page page = (Page) BufferGetPage(parentBuffer);
1479 heikki.linnakangas 364 EUB :
1479 heikki.linnakangas 365 UIC 0 : PageIndexTupleDelete(page, xldata->downlinkOffset);
366 :
1479 heikki.linnakangas 367 UBC 0 : PageSetLSN(page, lsn);
368 0 : MarkBufferDirty(parentBuffer);
1479 heikki.linnakangas 369 EUB : }
370 :
1479 heikki.linnakangas 371 UBC 0 : if (BufferIsValid(parentBuffer))
1479 heikki.linnakangas 372 UIC 0 : UnlockReleaseBuffer(parentBuffer);
373 0 : if (BufferIsValid(leafBuffer))
1479 heikki.linnakangas 374 UBC 0 : UnlockReleaseBuffer(leafBuffer);
1479 heikki.linnakangas 375 UIC 0 : }
1479 heikki.linnakangas 376 EUB :
377 : static void
1479 heikki.linnakangas 378 UIC 0 : gistRedoPageReuse(XLogReaderState *record)
379 : {
380 0 : gistxlogPageReuse *xlrec = (gistxlogPageReuse *) XLogRecGetData(record);
381 :
382 : /*
383 : * PAGE_REUSE records exist to provide a conflict point when we reuse
384 : * pages in the index via the FSM. That's all they do though.
385 : *
386 : * snapshotConflictHorizon was the page's deleteXid. The
387 : * GlobalVisCheckRemovableFullXid(deleteXid) test in gistPageRecyclable()
969 andres 388 EUB : * conceptually mirrors the PGPROC->xmin > limitXmin test in
970 389 : * GetConflictingVirtualXIDs(). Consequently, one XID value achieves the
390 : * same exclusion effect on primary and standby.
391 : */
1479 heikki.linnakangas 392 UBC 0 : if (InHotStandby)
143 pg 393 UNC 0 : ResolveRecoveryConflictWithSnapshotFullXid(xlrec->snapshotConflictHorizon,
2 andres 394 0 : xlrec->isCatalogRel,
395 : xlrec->locator);
1479 heikki.linnakangas 396 LBC 0 : }
397 :
6508 teodor 398 ECB : void
3062 heikki.linnakangas 399 GIC 57485 : gist_redo(XLogReaderState *record)
400 : {
401 57485 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
402 : MemoryContext oldCxt;
403 :
404 : /*
405 : * GiST indexes do not require any conflict processing. NB: If we ever
406 : * implement a similar optimization we have in b-tree, and remove killed
4859 simon 407 ECB : * tuples outside VACUUM, we'll need to handle that here.
408 : */
409 :
6508 teodor 410 CBC 57485 : oldCxt = MemoryContextSwitchTo(opCtx);
6408 bruce 411 57485 : switch (info)
6408 bruce 412 ECB : {
6219 tgl 413 GBC 57037 : case XLOG_GIST_PAGE_UPDATE:
3062 heikki.linnakangas 414 57037 : gistRedoPageUpdateRecord(record);
6508 teodor 415 57037 : break;
1570 akorotkov 416 UBC 0 : case XLOG_GIST_DELETE:
417 0 : gistRedoDeleteRecord(record);
418 0 : break;
1479 heikki.linnakangas 419 LBC 0 : case XLOG_GIST_PAGE_REUSE:
420 0 : gistRedoPageReuse(record);
421 0 : break;
6408 bruce 422 GBC 448 : case XLOG_GIST_PAGE_SPLIT:
3062 heikki.linnakangas 423 448 : gistRedoPageSplitRecord(record);
6508 teodor 424 448 : break;
1479 heikki.linnakangas 425 UBC 0 : case XLOG_GIST_PAGE_DELETE:
1479 heikki.linnakangas 426 UIC 0 : gistRedoPageDelete(record);
1479 heikki.linnakangas 427 UBC 0 : break;
1100 noah 428 0 : case XLOG_GIST_ASSIGN_LSN:
1100 noah 429 EUB : /* nop. See gistGetFakeLSN(). */
1100 noah 430 UIC 0 : break;
6508 teodor 431 0 : default:
6508 teodor 432 LBC 0 : elog(PANIC, "gist_redo: unknown op code %u", info);
6508 teodor 433 ECB : }
434 :
6508 teodor 435 GIC 57485 : MemoryContextSwitchTo(oldCxt);
436 57485 : MemoryContextReset(opCtx);
6508 teodor 437 CBC 57485 : }
438 :
6508 teodor 439 ECB : void
6408 bruce 440 CBC 141 : gist_xlog_startup(void)
441 : {
6508 teodor 442 GIC 141 : opCtx = createTempGistContext();
6508 teodor 443 CBC 141 : }
444 :
6508 teodor 445 ECB : void
6408 bruce 446 CBC 108 : gist_xlog_cleanup(void)
447 : {
6508 teodor 448 GIC 108 : MemoryContextDelete(opCtx);
6089 tgl 449 108 : }
450 :
451 : /*
2251 rhaas 452 EUB : * Mask a Gist page before running consistency checks on it.
453 : */
454 : void
2251 rhaas 455 UIC 0 : gist_mask(char *pagedata, BlockNumber blkno)
2251 rhaas 456 EUB : {
2251 rhaas 457 UIC 0 : Page page = (Page) pagedata;
2251 rhaas 458 EUB :
2025 rhaas 459 UBC 0 : mask_page_lsn_and_checksum(page);
460 :
2251 rhaas 461 UIC 0 : mask_page_hint_bits(page);
462 0 : mask_unused_space(page);
463 :
464 : /*
2251 rhaas 465 EUB : * NSN is nothing but a special purpose LSN. Hence, mask it for the same
466 : * reason as mask_page_lsn_and_checksum.
467 : */
2251 rhaas 468 UIC 0 : GistPageSetNSN(page, (uint64) MASK_MARKER);
469 :
470 : /*
2251 rhaas 471 EUB : * We update F_FOLLOW_RIGHT flag on the left child after writing WAL
472 : * record. Hence, mask this flag. See gistplacetopage() for details.
473 : */
2251 rhaas 474 UIC 0 : GistMarkFollowRight(page);
475 :
476 0 : if (GistPageIsLeaf(page))
477 : {
478 : /*
479 : * In gist leaf pages, it is possible to modify the LP_FLAGS without
2251 rhaas 480 EUB : * emitting any WAL record. Hence, mask the line pointer flags. See
481 : * gistkillitems() for details.
482 : */
2251 rhaas 483 UIC 0 : mask_lp_flags(page);
484 : }
485 :
486 : /*
2251 rhaas 487 EUB : * During gist redo, we never mark a page as garbage. Hence, mask it to
488 : * ignore any differences.
489 : */
2251 rhaas 490 UIC 0 : GistClearPageHasGarbage(page);
491 0 : }
492 :
493 : /*
4490 heikki.linnakangas 494 ECB : * Write WAL record of a page split.
495 : */
496 : XLogRecPtr
2476 alvherre 497 GIC 1730 : gistXLogSplit(bool page_is_leaf,
498 : SplitedPageLayout *dist,
499 : BlockNumber origrlink, GistNSN orignsn,
500 : Buffer leftchildbuf, bool markfollowright)
6408 bruce 501 ECB : {
502 : gistxlogPageSplit xlrec;
503 : SplitedPageLayout *ptr;
3062 heikki.linnakangas 504 GIC 1730 : int npage = 0;
4382 bruce 505 ECB : XLogRecPtr recptr;
3062 heikki.linnakangas 506 : int i;
507 :
4490 heikki.linnakangas 508 CBC 5246 : for (ptr = dist; ptr; ptr = ptr->next)
6502 teodor 509 3516 : npage++;
3260 bruce 510 ECB :
4490 heikki.linnakangas 511 CBC 1730 : xlrec.origrlink = origrlink;
512 1730 : xlrec.orignsn = orignsn;
4490 heikki.linnakangas 513 GIC 1730 : xlrec.origleaf = page_is_leaf;
4490 heikki.linnakangas 514 CBC 1730 : xlrec.npage = (uint16) npage;
4231 heikki.linnakangas 515 GIC 1730 : xlrec.markfollowright = markfollowright;
516 :
3062 517 1730 : XLogBeginInsert();
518 :
519 : /*
4490 heikki.linnakangas 520 ECB : * Include a full page image of the child buf. (only necessary if a
521 : * checkpoint happened since the child page was split)
522 : */
4490 heikki.linnakangas 523 GIC 1730 : if (BufferIsValid(leftchildbuf))
3062 524 6 : XLogRegisterBuffer(0, leftchildbuf, REGBUF_STANDARD);
525 :
526 : /*
527 : * NOTE: We register a lot of data. The caller must've called
528 : * XLogEnsureRecordSpace() to prepare for that. We cannot do it here,
529 : * because we're already in a critical section. If you change the number
3062 heikki.linnakangas 530 ECB : * of buffer or data registrations here, make sure you modify the
531 : * XLogEnsureRecordSpace() calls accordingly!
532 : */
3062 heikki.linnakangas 533 CBC 1730 : XLogRegisterData((char *) &xlrec, sizeof(gistxlogPageSplit));
534 :
535 1730 : i = 1;
4490 536 5246 : for (ptr = dist; ptr; ptr = ptr->next)
4490 heikki.linnakangas 537 ECB : {
3062 heikki.linnakangas 538 CBC 3516 : XLogRegisterBuffer(i, ptr->buffer, REGBUF_WILL_INIT);
3062 heikki.linnakangas 539 GIC 3516 : XLogRegisterBufData(i, (char *) &(ptr->block.num), sizeof(int));
540 3516 : XLogRegisterBufData(i, (char *) ptr->list, ptr->lenlist);
3062 heikki.linnakangas 541 CBC 3516 : i++;
542 : }
4490 heikki.linnakangas 543 ECB :
3062 heikki.linnakangas 544 GIC 1730 : recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT);
545 :
4490 546 1730 : return recptr;
547 : }
548 :
549 : /*
550 : * Write XLOG record describing a page deletion. This also includes removal of
1479 heikki.linnakangas 551 EUB : * downlink from the parent page.
552 : */
553 : XLogRecPtr
1355 heikki.linnakangas 554 UIC 0 : gistXLogPageDelete(Buffer buffer, FullTransactionId xid,
555 : Buffer parentBuffer, OffsetNumber downlinkOffset)
556 : {
1479 heikki.linnakangas 557 EUB : gistxlogPageDelete xlrec;
558 : XLogRecPtr recptr;
559 :
1479 heikki.linnakangas 560 UBC 0 : xlrec.deleteXid = xid;
561 0 : xlrec.downlinkOffset = downlinkOffset;
562 :
563 0 : XLogBeginInsert();
564 0 : XLogRegisterData((char *) &xlrec, SizeOfGistxlogPageDelete);
565 :
566 0 : XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
1479 heikki.linnakangas 567 UIC 0 : XLogRegisterBuffer(1, parentBuffer, REGBUF_STANDARD);
1479 heikki.linnakangas 568 EUB :
1479 heikki.linnakangas 569 UIC 0 : recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE);
570 :
571 0 : return recptr;
572 : }
573 :
574 : /*
1100 noah 575 EUB : * Write an empty XLOG record to assign a distinct LSN.
576 : */
577 : XLogRecPtr
1100 noah 578 UIC 0 : gistXLogAssignLSN(void)
579 : {
580 0 : int dummy = 0;
581 :
582 : /*
1100 noah 583 EUB : * Records other than SWITCH_WAL must have content. We use an integer 0 to
584 : * follow the restriction.
585 : */
1100 noah 586 UBC 0 : XLogBeginInsert();
1100 noah 587 UIC 0 : XLogSetRecordFlags(XLOG_MARK_UNIMPORTANT);
588 0 : XLogRegisterData((char *) &dummy, sizeof(dummy));
589 0 : return XLogInsert(RM_GIST_ID, XLOG_GIST_ASSIGN_LSN);
590 : }
591 :
592 : /*
1479 heikki.linnakangas 593 EUB : * Write XLOG record about reuse of a deleted page.
594 : */
595 : void
8 andres 596 UNC 0 : gistXLogPageReuse(Relation rel, Relation heaprel,
597 : BlockNumber blkno, FullTransactionId deleteXid)
598 : {
599 : gistxlogPageReuse xlrec_reuse;
600 :
601 : /*
602 : * Note that we don't register the buffer with the record, because this
603 : * operation doesn't modify the page. This record only exists to provide a
604 : * conflict point for Hot Standby.
605 : */
1479 heikki.linnakangas 606 EUB :
607 : /* XLOG stuff */
7 andres 608 UNC 0 : xlrec_reuse.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel);
277 rhaas 609 0 : xlrec_reuse.locator = rel->rd_locator;
1479 heikki.linnakangas 610 UBC 0 : xlrec_reuse.block = blkno;
143 pg 611 UNC 0 : xlrec_reuse.snapshotConflictHorizon = deleteXid;
1479 heikki.linnakangas 612 EUB :
1479 heikki.linnakangas 613 UBC 0 : XLogBeginInsert();
1479 heikki.linnakangas 614 UIC 0 : XLogRegisterData((char *) &xlrec_reuse, SizeOfGistxlogPageReuse);
1479 heikki.linnakangas 615 EUB :
1479 heikki.linnakangas 616 UBC 0 : XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_REUSE);
1479 heikki.linnakangas 617 UIC 0 : }
618 :
619 : /*
620 : * Write XLOG record describing a page update. The update can include any
621 : * number of deletions and/or insertions of tuples on a single index page.
622 : *
623 : * If this update inserts a downlink for a split page, also record that
624 : * the F_FOLLOW_RIGHT flag on the child page is cleared and NSN set.
625 : *
626 : * Note that both the todelete array and the tuples are marked as belonging
627 : * to the target buffer; they need not be stored in XLOG if XLogInsert decides
628 : * to log the whole buffer contents instead.
629 : */
4490 heikki.linnakangas 630 ECB : XLogRecPtr
2476 alvherre 631 GIC 246502 : gistXLogUpdate(Buffer buffer,
632 : OffsetNumber *todelete, int ntodelete,
633 : IndexTuple *itup, int ituplen,
634 : Buffer leftchildbuf)
635 : {
636 : gistxlogPageUpdate xlrec;
637 : int i;
638 : XLogRecPtr recptr;
6219 tgl 639 ECB :
3800 tgl 640 CBC 246502 : xlrec.ntodelete = ntodelete;
3062 heikki.linnakangas 641 GIC 246502 : xlrec.ntoinsert = ituplen;
6502 teodor 642 ECB :
3062 heikki.linnakangas 643 CBC 246502 : XLogBeginInsert();
3062 heikki.linnakangas 644 GIC 246502 : XLogRegisterData((char *) &xlrec, sizeof(gistxlogPageUpdate));
6502 teodor 645 ECB :
3062 heikki.linnakangas 646 CBC 246502 : XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
3062 heikki.linnakangas 647 GIC 246502 : XLogRegisterBufData(0, (char *) todelete, sizeof(OffsetNumber) * ntodelete);
648 :
4490 heikki.linnakangas 649 ECB : /* new tuples */
6219 tgl 650 CBC 494580 : for (i = 0; i < ituplen; i++)
3062 heikki.linnakangas 651 GIC 248078 : XLogRegisterBufData(0, (char *) (itup[i]), IndexTupleSize(itup[i]));
652 :
653 : /*
654 : * Include a full page image of the child buf. (only necessary if a
655 : * checkpoint happened since the child page was split)
4490 heikki.linnakangas 656 ECB : */
4490 heikki.linnakangas 657 CBC 246502 : if (BufferIsValid(leftchildbuf))
3062 heikki.linnakangas 658 GIC 1668 : XLogRegisterBuffer(1, leftchildbuf, REGBUF_STANDARD);
6408 bruce 659 ECB :
3062 heikki.linnakangas 660 GIC 246502 : recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE);
6502 teodor 661 ECB :
6502 teodor 662 GIC 246502 : return recptr;
663 : }
664 :
665 : /*
666 : * Write XLOG record describing a delete of leaf index tuples marked as DEAD
667 : * during new tuple insertion. One may think that this case is already covered
668 : * by gistXLogUpdate(). But deletion of index tuples might conflict with
669 : * standby queries and needs special handling.
670 : */
1570 akorotkov 671 EUB : XLogRecPtr
1570 akorotkov 672 UIC 0 : gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete,
673 : TransactionId snapshotConflictHorizon, Relation heaprel)
674 : {
675 : gistxlogDelete xlrec;
676 : XLogRecPtr recptr;
1570 akorotkov 677 EUB :
7 andres 678 UNC 0 : xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel);
143 pg 679 0 : xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
1570 akorotkov 680 UBC 0 : xlrec.ntodelete = ntodelete;
681 :
682 0 : XLogBeginInsert();
683 0 : XLogRegisterData((char *) &xlrec, SizeOfGistxlogDelete);
684 :
685 : /*
686 : * We need the target-offsets array whether or not we store the whole
687 : * buffer, to allow us to find the snapshotConflictHorizon on a standby
688 : * server.
689 : */
1570 akorotkov 690 UIC 0 : XLogRegisterData((char *) todelete, ntodelete * sizeof(OffsetNumber));
1570 akorotkov 691 EUB :
1570 akorotkov 692 UIC 0 : XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
1570 akorotkov 693 EUB :
1570 akorotkov 694 UIC 0 : recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_DELETE);
1570 akorotkov 695 EUB :
1570 akorotkov 696 UIC 0 : return recptr;
1570 akorotkov 697 EUB : }
|