1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginxlog.c
4 : * WAL replay logic for inverted index.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.11 2007/11/15 21:14:31 momjian Exp $
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/gin.h"
17 : #include "access/heapam.h"
18 : #include "utils/memutils.h"
19 :
20 : static MemoryContext opCtx; /* working memory for operations */
21 : static MemoryContext topCtx;
22 :
23 : typedef struct ginIncompleteSplit
24 : {
25 : RelFileNode node;
26 : BlockNumber leftBlkno;
27 : BlockNumber rightBlkno;
28 : BlockNumber rootBlkno;
29 : } ginIncompleteSplit;
30 :
31 : static List *incomplete_splits;
32 :
33 : static void
34 : pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBlkno, BlockNumber rootBlkno)
35 0 : {
36 : ginIncompleteSplit *split;
37 :
38 0 : MemoryContextSwitchTo(topCtx);
39 :
40 0 : split = palloc(sizeof(ginIncompleteSplit));
41 :
42 0 : split->node = node;
43 0 : split->leftBlkno = leftBlkno;
44 0 : split->rightBlkno = rightBlkno;
45 0 : split->rootBlkno = rootBlkno;
46 :
47 0 : incomplete_splits = lappend(incomplete_splits, split);
48 :
49 0 : MemoryContextSwitchTo(opCtx);
50 0 : }
51 :
52 : static void
53 : forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updateBlkno)
54 0 : {
55 : ListCell *l;
56 :
57 0 : foreach(l, incomplete_splits)
58 : {
59 0 : ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
60 :
61 0 : if (RelFileNodeEquals(node, split->node) && leftBlkno == split->leftBlkno && updateBlkno == split->rightBlkno)
62 : {
63 0 : incomplete_splits = list_delete_ptr(incomplete_splits, split);
64 0 : break;
65 : }
66 : }
67 0 : }
68 :
69 : static void
70 : ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
71 0 : {
72 0 : RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
73 : Relation reln;
74 : Buffer buffer;
75 : Page page;
76 :
77 0 : reln = XLogOpenRelation(*node);
78 0 : buffer = XLogReadBuffer(reln, GIN_ROOT_BLKNO, true);
79 : Assert(BufferIsValid(buffer));
80 0 : page = (Page) BufferGetPage(buffer);
81 :
82 0 : GinInitBuffer(buffer, GIN_LEAF);
83 :
84 0 : PageSetLSN(page, lsn);
85 0 : PageSetTLI(page, ThisTimeLineID);
86 :
87 0 : MarkBufferDirty(buffer);
88 0 : UnlockReleaseBuffer(buffer);
89 0 : }
90 :
91 : static void
92 : ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
93 0 : {
94 0 : ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
95 0 : ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
96 : Relation reln;
97 : Buffer buffer;
98 : Page page;
99 :
100 0 : reln = XLogOpenRelation(data->node);
101 0 : buffer = XLogReadBuffer(reln, data->blkno, true);
102 : Assert(BufferIsValid(buffer));
103 0 : page = (Page) BufferGetPage(buffer);
104 :
105 0 : GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
106 0 : memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem);
107 0 : GinPageGetOpaque(page)->maxoff = data->nitem;
108 :
109 0 : PageSetLSN(page, lsn);
110 0 : PageSetTLI(page, ThisTimeLineID);
111 :
112 0 : MarkBufferDirty(buffer);
113 0 : UnlockReleaseBuffer(buffer);
114 0 : }
115 :
116 : static void
117 : ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
118 0 : {
119 0 : ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
120 : Relation reln;
121 : Buffer buffer;
122 : Page page;
123 :
124 : /* nothing else to do if page was backed up */
125 0 : if (record->xl_info & XLR_BKP_BLOCK_1)
126 0 : return;
127 :
128 0 : reln = XLogOpenRelation(data->node);
129 0 : buffer = XLogReadBuffer(reln, data->blkno, false);
130 : Assert(BufferIsValid(buffer));
131 0 : page = (Page) BufferGetPage(buffer);
132 :
133 0 : if (data->isData)
134 : {
135 : Assert(data->isDelete == FALSE);
136 : Assert(GinPageIsData(page));
137 :
138 0 : if (!XLByteLE(lsn, PageGetLSN(page)))
139 : {
140 0 : if (data->isLeaf)
141 : {
142 : OffsetNumber i;
143 0 : ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
144 :
145 : Assert(GinPageIsLeaf(page));
146 : Assert(data->updateBlkno == InvalidBlockNumber);
147 :
148 0 : for (i = 0; i < data->nitem; i++)
149 0 : GinDataPageAddItem(page, items + i, data->offset + i);
150 : }
151 : else
152 : {
153 : PostingItem *pitem;
154 :
155 : Assert(!GinPageIsLeaf(page));
156 :
157 0 : if (data->updateBlkno != InvalidBlockNumber)
158 : {
159 : /* update link to right page after split */
160 0 : pitem = (PostingItem *) GinDataPageGetItem(page, data->offset);
161 0 : PostingItemSetBlockNumber(pitem, data->updateBlkno);
162 : }
163 :
164 0 : pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
165 :
166 0 : GinDataPageAddItem(page, pitem, data->offset);
167 : }
168 : }
169 :
170 0 : if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
171 : {
172 0 : PostingItem *pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
173 :
174 0 : forgetIncompleteSplit(data->node, PostingItemGetBlockNumber(pitem), data->updateBlkno);
175 : }
176 :
177 : }
178 : else
179 : {
180 : IndexTuple itup;
181 :
182 : Assert(!GinPageIsData(page));
183 :
184 0 : if (!XLByteLE(lsn, PageGetLSN(page)))
185 : {
186 0 : if (data->updateBlkno != InvalidBlockNumber)
187 : {
188 : /* update link to right page after split */
189 : Assert(!GinPageIsLeaf(page));
190 : Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
191 0 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, data->offset));
192 0 : ItemPointerSet(&itup->t_tid, data->updateBlkno, InvalidOffsetNumber);
193 : }
194 :
195 0 : if (data->isDelete)
196 : {
197 : Assert(GinPageIsLeaf(page));
198 : Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
199 0 : PageIndexTupleDelete(page, data->offset);
200 : }
201 :
202 0 : itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
203 :
204 0 : if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, false, false) == InvalidOffsetNumber)
205 0 : elog(ERROR, "failed to add item to index page in %u/%u/%u",
206 : data->node.spcNode, data->node.dbNode, data->node.relNode);
207 : }
208 :
209 0 : if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
210 : {
211 0 : itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
212 0 : forgetIncompleteSplit(data->node, GinItemPointerGetBlockNumber(&itup->t_tid), data->updateBlkno);
213 : }
214 : }
215 :
216 0 : if (!XLByteLE(lsn, PageGetLSN(page)))
217 : {
218 0 : PageSetLSN(page, lsn);
219 0 : PageSetTLI(page, ThisTimeLineID);
220 :
221 0 : MarkBufferDirty(buffer);
222 : }
223 0 : UnlockReleaseBuffer(buffer);
224 : }
225 :
226 : static void
227 : ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
228 0 : {
229 0 : ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
230 : Relation reln;
231 : Buffer lbuffer,
232 : rbuffer;
233 : Page lpage,
234 : rpage;
235 0 : uint32 flags = 0;
236 :
237 0 : reln = XLogOpenRelation(data->node);
238 :
239 0 : if (data->isLeaf)
240 0 : flags |= GIN_LEAF;
241 0 : if (data->isData)
242 0 : flags |= GIN_DATA;
243 :
244 0 : lbuffer = XLogReadBuffer(reln, data->lblkno, data->isRootSplit);
245 : Assert(BufferIsValid(lbuffer));
246 0 : lpage = (Page) BufferGetPage(lbuffer);
247 0 : GinInitBuffer(lbuffer, flags);
248 :
249 0 : rbuffer = XLogReadBuffer(reln, data->rblkno, true);
250 : Assert(BufferIsValid(rbuffer));
251 0 : rpage = (Page) BufferGetPage(rbuffer);
252 0 : GinInitBuffer(rbuffer, flags);
253 :
254 0 : GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber(rbuffer);
255 0 : GinPageGetOpaque(rpage)->rightlink = data->rrlink;
256 :
257 0 : if (data->isData)
258 : {
259 0 : char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
260 0 : Size sizeofitem = GinSizeOfItem(lpage);
261 : OffsetNumber i;
262 : ItemPointer bound;
263 :
264 0 : for (i = 0; i < data->separator; i++)
265 : {
266 0 : GinDataPageAddItem(lpage, ptr, InvalidOffsetNumber);
267 0 : ptr += sizeofitem;
268 : }
269 :
270 0 : for (i = data->separator; i < data->nitem; i++)
271 : {
272 0 : GinDataPageAddItem(rpage, ptr, InvalidOffsetNumber);
273 0 : ptr += sizeofitem;
274 : }
275 :
276 : /* set up right key */
277 0 : bound = GinDataPageGetRightBound(lpage);
278 0 : if (data->isLeaf)
279 0 : *bound = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
280 : else
281 0 : *bound = ((PostingItem *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
282 :
283 0 : bound = GinDataPageGetRightBound(rpage);
284 0 : *bound = data->rightbound;
285 : }
286 : else
287 : {
288 0 : IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogSplit));
289 : OffsetNumber i;
290 :
291 0 : for (i = 0; i < data->separator; i++)
292 : {
293 0 : if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
294 0 : elog(ERROR, "failed to add item to index page in %u/%u/%u",
295 : data->node.spcNode, data->node.dbNode, data->node.relNode);
296 0 : itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
297 : }
298 :
299 0 : for (i = data->separator; i < data->nitem; i++)
300 : {
301 0 : if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
302 0 : elog(ERROR, "failed to add item to index page in %u/%u/%u",
303 : data->node.spcNode, data->node.dbNode, data->node.relNode);
304 0 : itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
305 : }
306 : }
307 :
308 0 : PageSetLSN(rpage, lsn);
309 0 : PageSetTLI(rpage, ThisTimeLineID);
310 0 : MarkBufferDirty(rbuffer);
311 :
312 0 : PageSetLSN(lpage, lsn);
313 0 : PageSetTLI(lpage, ThisTimeLineID);
314 0 : MarkBufferDirty(lbuffer);
315 :
316 0 : if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
317 0 : forgetIncompleteSplit(data->node, data->leftChildBlkno, data->updateBlkno);
318 :
319 0 : if (data->isRootSplit)
320 : {
321 0 : Buffer rootBuf = XLogReadBuffer(reln, data->rootBlkno, false);
322 0 : Page rootPage = BufferGetPage(rootBuf);
323 :
324 0 : GinInitBuffer(rootBuf, flags & ~GIN_LEAF);
325 :
326 0 : if (data->isData)
327 : {
328 : Assert(data->rootBlkno != GIN_ROOT_BLKNO);
329 0 : dataFillRoot(NULL, rootBuf, lbuffer, rbuffer);
330 : }
331 : else
332 : {
333 : Assert(data->rootBlkno == GIN_ROOT_BLKNO);
334 0 : entryFillRoot(NULL, rootBuf, lbuffer, rbuffer);
335 : }
336 :
337 0 : PageSetLSN(rootPage, lsn);
338 0 : PageSetTLI(rootPage, ThisTimeLineID);
339 :
340 0 : MarkBufferDirty(rootBuf);
341 0 : UnlockReleaseBuffer(rootBuf);
342 : }
343 : else
344 0 : pushIncompleteSplit(data->node, data->lblkno, data->rblkno, data->rootBlkno);
345 :
346 0 : UnlockReleaseBuffer(rbuffer);
347 0 : UnlockReleaseBuffer(lbuffer);
348 0 : }
349 :
350 : static void
351 : ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
352 0 : {
353 0 : ginxlogVacuumPage *data = (ginxlogVacuumPage *) XLogRecGetData(record);
354 : Relation reln;
355 : Buffer buffer;
356 : Page page;
357 :
358 : /* nothing else to do if page was backed up (and no info to do it with) */
359 0 : if (record->xl_info & XLR_BKP_BLOCK_1)
360 0 : return;
361 :
362 0 : reln = XLogOpenRelation(data->node);
363 0 : buffer = XLogReadBuffer(reln, data->blkno, false);
364 : Assert(BufferIsValid(buffer));
365 0 : page = (Page) BufferGetPage(buffer);
366 :
367 0 : if (GinPageIsData(page))
368 : {
369 0 : memcpy(GinDataPageGetData(page), XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
370 : GinSizeOfItem(page) *data->nitem);
371 0 : GinPageGetOpaque(page)->maxoff = data->nitem;
372 : }
373 : else
374 : {
375 : OffsetNumber i,
376 : *tod;
377 0 : IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogVacuumPage));
378 :
379 0 : tod = (OffsetNumber *) palloc(sizeof(OffsetNumber) * PageGetMaxOffsetNumber(page));
380 0 : for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
381 0 : tod[i - 1] = i;
382 :
383 0 : PageIndexMultiDelete(page, tod, PageGetMaxOffsetNumber(page));
384 :
385 0 : for (i = 0; i < data->nitem; i++)
386 : {
387 0 : if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
388 0 : elog(ERROR, "failed to add item to index page in %u/%u/%u",
389 : data->node.spcNode, data->node.dbNode, data->node.relNode);
390 0 : itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
391 : }
392 : }
393 :
394 0 : PageSetLSN(page, lsn);
395 0 : PageSetTLI(page, ThisTimeLineID);
396 :
397 0 : MarkBufferDirty(buffer);
398 0 : UnlockReleaseBuffer(buffer);
399 : }
400 :
401 : static void
402 : ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
403 0 : {
404 0 : ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
405 : Relation reln;
406 : Buffer buffer;
407 : Page page;
408 :
409 0 : reln = XLogOpenRelation(data->node);
410 :
411 0 : if (!(record->xl_info & XLR_BKP_BLOCK_1))
412 : {
413 0 : buffer = XLogReadBuffer(reln, data->blkno, false);
414 0 : page = BufferGetPage(buffer);
415 : Assert(GinPageIsData(page));
416 0 : GinPageGetOpaque(page)->flags = GIN_DELETED;
417 0 : PageSetLSN(page, lsn);
418 0 : PageSetTLI(page, ThisTimeLineID);
419 0 : MarkBufferDirty(buffer);
420 0 : UnlockReleaseBuffer(buffer);
421 : }
422 :
423 0 : if (!(record->xl_info & XLR_BKP_BLOCK_2))
424 : {
425 0 : buffer = XLogReadBuffer(reln, data->parentBlkno, false);
426 0 : page = BufferGetPage(buffer);
427 : Assert(GinPageIsData(page));
428 : Assert(!GinPageIsLeaf(page));
429 0 : PageDeletePostingItem(page, data->parentOffset);
430 0 : PageSetLSN(page, lsn);
431 0 : PageSetTLI(page, ThisTimeLineID);
432 0 : MarkBufferDirty(buffer);
433 0 : UnlockReleaseBuffer(buffer);
434 : }
435 :
436 0 : if (!(record->xl_info & XLR_BKP_BLOCK_3) && data->leftBlkno != InvalidBlockNumber)
437 : {
438 0 : buffer = XLogReadBuffer(reln, data->leftBlkno, false);
439 0 : page = BufferGetPage(buffer);
440 : Assert(GinPageIsData(page));
441 0 : GinPageGetOpaque(page)->rightlink = data->rightLink;
442 0 : PageSetLSN(page, lsn);
443 0 : PageSetTLI(page, ThisTimeLineID);
444 0 : MarkBufferDirty(buffer);
445 0 : UnlockReleaseBuffer(buffer);
446 : }
447 0 : }
448 :
449 : void
450 : gin_redo(XLogRecPtr lsn, XLogRecord *record)
451 0 : {
452 0 : uint8 info = record->xl_info & ~XLR_INFO_MASK;
453 :
454 0 : topCtx = MemoryContextSwitchTo(opCtx);
455 0 : switch (info)
456 : {
457 : case XLOG_GIN_CREATE_INDEX:
458 0 : ginRedoCreateIndex(lsn, record);
459 0 : break;
460 : case XLOG_GIN_CREATE_PTREE:
461 0 : ginRedoCreatePTree(lsn, record);
462 0 : break;
463 : case XLOG_GIN_INSERT:
464 0 : ginRedoInsert(lsn, record);
465 0 : break;
466 : case XLOG_GIN_SPLIT:
467 0 : ginRedoSplit(lsn, record);
468 0 : break;
469 : case XLOG_GIN_VACUUM_PAGE:
470 0 : ginRedoVacuumPage(lsn, record);
471 0 : break;
472 : case XLOG_GIN_DELETE_PAGE:
473 0 : ginRedoDeletePage(lsn, record);
474 0 : break;
475 : default:
476 0 : elog(PANIC, "gin_redo: unknown op code %u", info);
477 : }
478 0 : MemoryContextSwitchTo(topCtx);
479 0 : MemoryContextReset(opCtx);
480 0 : }
481 :
482 : static void
483 : desc_node(StringInfo buf, RelFileNode node, BlockNumber blkno)
484 0 : {
485 0 : appendStringInfo(buf, "node: %u/%u/%u blkno: %u",
486 : node.spcNode, node.dbNode, node.relNode, blkno);
487 0 : }
488 :
489 : void
490 : gin_desc(StringInfo buf, uint8 xl_info, char *rec)
491 0 : {
492 0 : uint8 info = xl_info & ~XLR_INFO_MASK;
493 :
494 0 : switch (info)
495 : {
496 : case XLOG_GIN_CREATE_INDEX:
497 0 : appendStringInfo(buf, "Create index, ");
498 0 : desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO);
499 0 : break;
500 : case XLOG_GIN_CREATE_PTREE:
501 0 : appendStringInfo(buf, "Create posting tree, ");
502 0 : desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno);
503 0 : break;
504 : case XLOG_GIN_INSERT:
505 0 : appendStringInfo(buf, "Insert item, ");
506 0 : desc_node(buf, ((ginxlogInsert *) rec)->node, ((ginxlogInsert *) rec)->blkno);
507 0 : appendStringInfo(buf, " offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
508 : ((ginxlogInsert *) rec)->offset,
509 : ((ginxlogInsert *) rec)->nitem,
510 : (((ginxlogInsert *) rec)->isData) ? 'T' : 'F',
511 : (((ginxlogInsert *) rec)->isLeaf) ? 'T' : 'F',
512 : (((ginxlogInsert *) rec)->isDelete) ? 'T' : 'F',
513 : ((ginxlogInsert *) rec)->updateBlkno
514 : );
515 :
516 0 : break;
517 : case XLOG_GIN_SPLIT:
518 0 : appendStringInfo(buf, "Page split, ");
519 0 : desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno);
520 0 : appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->isRootSplit) ? 'T' : 'F');
521 0 : break;
522 : case XLOG_GIN_VACUUM_PAGE:
523 0 : appendStringInfo(buf, "Vacuum page, ");
524 0 : desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno);
525 0 : break;
526 : case XLOG_GIN_DELETE_PAGE:
527 0 : appendStringInfo(buf, "Delete page, ");
528 0 : desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
529 0 : break;
530 : default:
531 0 : elog(PANIC, "gin_desc: unknown op code %u", info);
532 : }
533 0 : }
534 :
535 : void
536 : gin_xlog_startup(void)
537 0 : {
538 0 : incomplete_splits = NIL;
539 :
540 0 : opCtx = AllocSetContextCreate(CurrentMemoryContext,
541 : "GIN recovery temporary context",
542 : ALLOCSET_DEFAULT_MINSIZE,
543 : ALLOCSET_DEFAULT_INITSIZE,
544 : ALLOCSET_DEFAULT_MAXSIZE);
545 0 : }
546 :
547 : static void
548 : ginContinueSplit(ginIncompleteSplit *split)
549 0 : {
550 : GinBtreeData btree;
551 : Relation reln;
552 : Buffer buffer;
553 : GinBtreeStack stack;
554 :
555 : /*
556 : * elog(NOTICE,"ginContinueSplit root:%u l:%u r:%u", split->rootBlkno,
557 : * split->leftBlkno, split->rightBlkno);
558 : */
559 0 : reln = XLogOpenRelation(split->node);
560 :
561 0 : buffer = XLogReadBuffer(reln, split->leftBlkno, false);
562 :
563 0 : if (split->rootBlkno == GIN_ROOT_BLKNO)
564 : {
565 0 : prepareEntryScan(&btree, reln, (Datum) 0, NULL);
566 0 : btree.entry = ginPageGetLinkItup(buffer);
567 : }
568 : else
569 : {
570 0 : Page page = BufferGetPage(buffer);
571 :
572 0 : prepareDataScan(&btree, reln);
573 :
574 0 : PostingItemSetBlockNumber(&(btree.pitem), split->leftBlkno);
575 0 : if (GinPageIsLeaf(page))
576 0 : btree.pitem.key = *(ItemPointerData *) GinDataPageGetItem(page,
577 : GinPageGetOpaque(page)->maxoff);
578 : else
579 0 : btree.pitem.key = ((PostingItem *) GinDataPageGetItem(page,
580 : GinPageGetOpaque(page)->maxoff))->key;
581 : }
582 :
583 0 : btree.rightblkno = split->rightBlkno;
584 :
585 0 : stack.blkno = split->leftBlkno;
586 0 : stack.buffer = buffer;
587 0 : stack.off = InvalidOffsetNumber;
588 0 : stack.parent = NULL;
589 :
590 0 : findParents(&btree, &stack, split->rootBlkno);
591 0 : ginInsertValue(&btree, stack.parent);
592 :
593 0 : UnlockReleaseBuffer(buffer);
594 0 : }
595 :
596 : void
597 : gin_xlog_cleanup(void)
598 0 : {
599 : ListCell *l;
600 : MemoryContext topCtx;
601 :
602 0 : topCtx = MemoryContextSwitchTo(opCtx);
603 :
604 0 : foreach(l, incomplete_splits)
605 : {
606 0 : ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
607 :
608 0 : ginContinueSplit(split);
609 0 : MemoryContextReset(opCtx);
610 : }
611 :
612 0 : MemoryContextSwitchTo(topCtx);
613 0 : MemoryContextDelete(opCtx);
614 0 : incomplete_splits = NIL;
615 0 : }
616 :
617 : bool
618 : gin_safe_restartpoint(void)
619 0 : {
620 0 : if (incomplete_splits)
621 0 : return false;
622 0 : return true;
623 : }
|