1 : /*-------------------------------------------------------------------------
2 : *
3 : * gistxlog.c
4 : * WAL replay logic for GiST.
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/gist/gistxlog.c,v 1.26 2007/02/01 19:10:25 momjian Exp $
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/gist_private.h"
17 : #include "access/heapam.h"
18 : #include "miscadmin.h"
19 : #include "utils/memutils.h"
20 :
21 :
22 : typedef struct
23 : {
24 : gistxlogPageUpdate *data;
25 : int len;
26 : IndexTuple *itup;
27 : OffsetNumber *todelete;
28 : } PageUpdateRecord;
29 :
30 : typedef struct
31 : {
32 : gistxlogPage *header;
33 : IndexTuple *itup;
34 : } NewPage;
35 :
36 : typedef struct
37 : {
38 : gistxlogPageSplit *data;
39 : NewPage *page;
40 : } PageSplitRecord;
41 :
42 : /* track for incomplete inserts, idea was taken from nbtxlog.c */
43 :
44 : typedef struct gistIncompleteInsert
45 : {
46 : RelFileNode node;
47 : BlockNumber origblkno; /* for splits */
48 : ItemPointerData key;
49 : int lenblk;
50 : BlockNumber *blkno;
51 : XLogRecPtr lsn;
52 : BlockNumber *path;
53 : int pathlen;
54 : } gistIncompleteInsert;
55 :
56 :
57 : static MemoryContext opCtx; /* working memory for operations */
58 : static MemoryContext insertCtx; /* holds incomplete_inserts list */
59 : static List *incomplete_inserts;
60 :
61 :
62 : #define ItemPointerEQ(a, b) \
63 : ( ItemPointerGetOffsetNumber(a) == ItemPointerGetOffsetNumber(b) && \
64 : ItemPointerGetBlockNumber (a) == ItemPointerGetBlockNumber(b) )
65 :
66 :
67 : static void
68 : pushIncompleteInsert(RelFileNode node, XLogRecPtr lsn, ItemPointerData key,
69 : BlockNumber *blkno, int lenblk,
70 : PageSplitRecord *xlinfo /* to extract blkno info */ )
71 0 : {
72 : MemoryContext oldCxt;
73 : gistIncompleteInsert *ninsert;
74 :
75 0 : if (!ItemPointerIsValid(&key))
76 :
77 : /*
78 : * if key is null then we should not store insertion as incomplete,
79 : * because it's a vacuum operation..
80 : */
81 0 : return;
82 :
83 0 : oldCxt = MemoryContextSwitchTo(insertCtx);
84 0 : ninsert = (gistIncompleteInsert *) palloc(sizeof(gistIncompleteInsert));
85 :
86 0 : ninsert->node = node;
87 0 : ninsert->key = key;
88 0 : ninsert->lsn = lsn;
89 :
90 0 : if (lenblk && blkno)
91 : {
92 0 : ninsert->lenblk = lenblk;
93 0 : ninsert->blkno = (BlockNumber *) palloc(sizeof(BlockNumber) * ninsert->lenblk);
94 0 : memcpy(ninsert->blkno, blkno, sizeof(BlockNumber) * ninsert->lenblk);
95 0 : ninsert->origblkno = *blkno;
96 : }
97 : else
98 : {
99 : int i;
100 :
101 : Assert(xlinfo);
102 0 : ninsert->lenblk = xlinfo->data->npage;
103 0 : ninsert->blkno = (BlockNumber *) palloc(sizeof(BlockNumber) * ninsert->lenblk);
104 0 : for (i = 0; i < ninsert->lenblk; i++)
105 0 : ninsert->blkno[i] = xlinfo->page[i].header->blkno;
106 0 : ninsert->origblkno = xlinfo->data->origblkno;
107 : }
108 : Assert(ninsert->lenblk > 0);
109 :
110 : /*
111 : * Stick the new incomplete insert onto the front of the list, not the
112 : * back. This is so that gist_xlog_cleanup will process incompletions in
113 : * last-in-first-out order.
114 : */
115 0 : incomplete_inserts = lcons(ninsert, incomplete_inserts);
116 :
117 : MemoryContextSwitchTo(oldCxt);
118 : }
119 :
120 : static void
121 : forgetIncompleteInsert(RelFileNode node, ItemPointerData key)
122 0 : {
123 : ListCell *l;
124 :
125 0 : if (!ItemPointerIsValid(&key))
126 0 : return;
127 :
128 0 : if (incomplete_inserts == NIL)
129 0 : return;
130 :
131 0 : foreach(l, incomplete_inserts)
132 : {
133 0 : gistIncompleteInsert *insert = (gistIncompleteInsert *) lfirst(l);
134 :
135 0 : if (RelFileNodeEquals(node, insert->node) && ItemPointerEQ(&(insert->key), &(key)))
136 : {
137 : /* found */
138 0 : incomplete_inserts = list_delete_ptr(incomplete_inserts, insert);
139 0 : pfree(insert->blkno);
140 0 : pfree(insert);
141 0 : break;
142 : }
143 : }
144 : }
145 :
146 : static void
147 : decodePageUpdateRecord(PageUpdateRecord *decoded, XLogRecord *record)
148 0 : {
149 0 : char *begin = XLogRecGetData(record),
150 : *ptr;
151 0 : int i = 0,
152 0 : addpath = 0;
153 :
154 0 : decoded->data = (gistxlogPageUpdate *) begin;
155 :
156 0 : if (decoded->data->ntodelete)
157 : {
158 0 : decoded->todelete = (OffsetNumber *) (begin + sizeof(gistxlogPageUpdate) + addpath);
159 0 : addpath = MAXALIGN(sizeof(OffsetNumber) * decoded->data->ntodelete);
160 : }
161 : else
162 0 : decoded->todelete = NULL;
163 :
164 0 : decoded->len = 0;
165 0 : ptr = begin + sizeof(gistxlogPageUpdate) + addpath;
166 0 : while (ptr - begin < record->xl_len)
167 : {
168 0 : decoded->len++;
169 0 : ptr += IndexTupleSize((IndexTuple) ptr);
170 : }
171 :
172 0 : decoded->itup = (IndexTuple *) palloc(sizeof(IndexTuple) * decoded->len);
173 :
174 0 : ptr = begin + sizeof(gistxlogPageUpdate) + addpath;
175 0 : while (ptr - begin < record->xl_len)
176 : {
177 0 : decoded->itup[i] = (IndexTuple) ptr;
178 0 : ptr += IndexTupleSize(decoded->itup[i]);
179 0 : i++;
180 : }
181 0 : }
182 :
183 : /*
184 : * redo any page update (except page split)
185 : */
186 : static void
187 : gistRedoPageUpdateRecord(XLogRecPtr lsn, XLogRecord *record, bool isnewroot)
188 0 : {
189 0 : gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) XLogRecGetData(record);
190 : PageUpdateRecord xlrec;
191 : Relation reln;
192 : Buffer buffer;
193 : Page page;
194 :
195 : /* we must fix incomplete_inserts list even if XLR_BKP_BLOCK_1 is set */
196 0 : forgetIncompleteInsert(xldata->node, xldata->key);
197 :
198 0 : if (!isnewroot && xldata->blkno != GIST_ROOT_BLKNO)
199 : /* operation with root always finalizes insertion */
200 0 : pushIncompleteInsert(xldata->node, lsn, xldata->key,
201 : &(xldata->blkno), 1,
202 : NULL);
203 :
204 : /* nothing else to do if page was backed up (and no info to do it with) */
205 0 : if (record->xl_info & XLR_BKP_BLOCK_1)
206 0 : return;
207 :
208 0 : decodePageUpdateRecord(&xlrec, record);
209 :
210 0 : reln = XLogOpenRelation(xlrec.data->node);
211 0 : buffer = XLogReadBuffer(reln, xlrec.data->blkno, false);
212 0 : if (!BufferIsValid(buffer))
213 : return;
214 0 : page = (Page) BufferGetPage(buffer);
215 :
216 0 : if (XLByteLE(lsn, PageGetLSN(page)))
217 : {
218 0 : UnlockReleaseBuffer(buffer);
219 0 : return;
220 : }
221 :
222 0 : if (isnewroot)
223 0 : GISTInitBuffer(buffer, 0);
224 0 : else if (xlrec.data->ntodelete)
225 : {
226 : int i;
227 :
228 0 : for (i = 0; i < xlrec.data->ntodelete; i++)
229 0 : PageIndexTupleDelete(page, xlrec.todelete[i]);
230 0 : if (GistPageIsLeaf(page))
231 0 : GistMarkTuplesDeleted(page);
232 : }
233 :
234 : /* add tuples */
235 0 : if (xlrec.len > 0)
236 0 : gistfillbuffer(reln, page, xlrec.itup, xlrec.len, InvalidOffsetNumber);
237 :
238 : /*
239 : * special case: leafpage, nothing to insert, nothing to delete, then
240 : * vacuum marks page
241 : */
242 0 : if (GistPageIsLeaf(page) && xlrec.len == 0 && xlrec.data->ntodelete == 0)
243 0 : GistClearTuplesDeleted(page);
244 :
245 0 : if (!GistPageIsLeaf(page) && PageGetMaxOffsetNumber(page) == InvalidOffsetNumber && xldata->blkno == GIST_ROOT_BLKNO)
246 :
247 : /*
248 : * all links on non-leaf root page was deleted by vacuum full, so root
249 : * page becomes a leaf
250 : */
251 0 : GistPageSetLeaf(page);
252 :
253 0 : GistPageGetOpaque(page)->rightlink = InvalidBlockNumber;
254 0 : PageSetLSN(page, lsn);
255 0 : PageSetTLI(page, ThisTimeLineID);
256 0 : MarkBufferDirty(buffer);
257 0 : UnlockReleaseBuffer(buffer);
258 : }
259 :
260 : static void
261 : gistRedoPageDeleteRecord(XLogRecPtr lsn, XLogRecord *record)
262 0 : {
263 0 : gistxlogPageDelete *xldata = (gistxlogPageDelete *) XLogRecGetData(record);
264 : Relation reln;
265 : Buffer buffer;
266 : Page page;
267 :
268 : /* nothing else to do if page was backed up (and no info to do it with) */
269 0 : if (record->xl_info & XLR_BKP_BLOCK_1)
270 0 : return;
271 :
272 0 : reln = XLogOpenRelation(xldata->node);
273 0 : buffer = XLogReadBuffer(reln, xldata->blkno, false);
274 0 : if (!BufferIsValid(buffer))
275 : return;
276 :
277 0 : page = (Page) BufferGetPage(buffer);
278 0 : GistPageSetDeleted(page);
279 :
280 0 : PageSetLSN(page, lsn);
281 0 : PageSetTLI(page, ThisTimeLineID);
282 0 : MarkBufferDirty(buffer);
283 0 : UnlockReleaseBuffer(buffer);
284 : }
285 :
286 : static void
287 : decodePageSplitRecord(PageSplitRecord *decoded, XLogRecord *record)
288 0 : {
289 0 : char *begin = XLogRecGetData(record),
290 : *ptr;
291 : int j,
292 0 : i = 0;
293 :
294 0 : decoded->data = (gistxlogPageSplit *) begin;
295 0 : decoded->page = (NewPage *) palloc(sizeof(NewPage) * decoded->data->npage);
296 :
297 0 : ptr = begin + sizeof(gistxlogPageSplit);
298 0 : for (i = 0; i < decoded->data->npage; i++)
299 : {
300 : Assert(ptr - begin < record->xl_len);
301 0 : decoded->page[i].header = (gistxlogPage *) ptr;
302 0 : ptr += sizeof(gistxlogPage);
303 :
304 0 : decoded->page[i].itup = (IndexTuple *)
305 : palloc(sizeof(IndexTuple) * decoded->page[i].header->num);
306 0 : j = 0;
307 0 : while (j < decoded->page[i].header->num)
308 : {
309 : Assert(ptr - begin < record->xl_len);
310 0 : decoded->page[i].itup[j] = (IndexTuple) ptr;
311 0 : ptr += IndexTupleSize((IndexTuple) ptr);
312 0 : j++;
313 : }
314 : }
315 0 : }
316 :
317 : static void
318 : gistRedoPageSplitRecord(XLogRecPtr lsn, XLogRecord *record)
319 0 : {
320 : PageSplitRecord xlrec;
321 : Relation reln;
322 : Buffer buffer;
323 : Page page;
324 : int i;
325 : int flags;
326 :
327 0 : decodePageSplitRecord(&xlrec, record);
328 0 : reln = XLogOpenRelation(xlrec.data->node);
329 0 : flags = xlrec.data->origleaf ? F_LEAF : 0;
330 :
331 : /* loop around all pages */
332 0 : for (i = 0; i < xlrec.data->npage; i++)
333 : {
334 0 : NewPage *newpage = xlrec.page + i;
335 :
336 0 : buffer = XLogReadBuffer(reln, newpage->header->blkno, true);
337 : Assert(BufferIsValid(buffer));
338 0 : page = (Page) BufferGetPage(buffer);
339 :
340 : /* ok, clear buffer */
341 0 : GISTInitBuffer(buffer, flags);
342 :
343 : /* and fill it */
344 0 : gistfillbuffer(reln, page, newpage->itup, newpage->header->num, FirstOffsetNumber);
345 :
346 0 : PageSetLSN(page, lsn);
347 0 : PageSetTLI(page, ThisTimeLineID);
348 0 : MarkBufferDirty(buffer);
349 0 : UnlockReleaseBuffer(buffer);
350 : }
351 :
352 0 : forgetIncompleteInsert(xlrec.data->node, xlrec.data->key);
353 :
354 0 : pushIncompleteInsert(xlrec.data->node, lsn, xlrec.data->key,
355 : NULL, 0,
356 : &xlrec);
357 0 : }
358 :
359 : static void
360 : gistRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
361 0 : {
362 0 : RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
363 : Relation reln;
364 : Buffer buffer;
365 : Page page;
366 :
367 0 : reln = XLogOpenRelation(*node);
368 0 : buffer = XLogReadBuffer(reln, GIST_ROOT_BLKNO, true);
369 : Assert(BufferIsValid(buffer));
370 0 : page = (Page) BufferGetPage(buffer);
371 :
372 0 : GISTInitBuffer(buffer, F_LEAF);
373 :
374 0 : PageSetLSN(page, lsn);
375 0 : PageSetTLI(page, ThisTimeLineID);
376 :
377 0 : MarkBufferDirty(buffer);
378 0 : UnlockReleaseBuffer(buffer);
379 0 : }
380 :
381 : static void
382 : gistRedoCompleteInsert(XLogRecPtr lsn, XLogRecord *record)
383 0 : {
384 0 : char *begin = XLogRecGetData(record),
385 : *ptr;
386 : gistxlogInsertComplete *xlrec;
387 :
388 0 : xlrec = (gistxlogInsertComplete *) begin;
389 :
390 0 : ptr = begin + sizeof(gistxlogInsertComplete);
391 0 : while (ptr - begin < record->xl_len)
392 : {
393 : Assert(record->xl_len - (ptr - begin) >= sizeof(ItemPointerData));
394 0 : forgetIncompleteInsert(xlrec->node, *((ItemPointerData *) ptr));
395 0 : ptr += sizeof(ItemPointerData);
396 : }
397 0 : }
398 :
399 : void
400 : gist_redo(XLogRecPtr lsn, XLogRecord *record)
401 0 : {
402 0 : uint8 info = record->xl_info & ~XLR_INFO_MASK;
403 :
404 : MemoryContext oldCxt;
405 :
406 0 : oldCxt = MemoryContextSwitchTo(opCtx);
407 0 : switch (info)
408 : {
409 : case XLOG_GIST_PAGE_UPDATE:
410 0 : gistRedoPageUpdateRecord(lsn, record, false);
411 0 : break;
412 : case XLOG_GIST_PAGE_DELETE:
413 0 : gistRedoPageDeleteRecord(lsn, record);
414 0 : break;
415 : case XLOG_GIST_NEW_ROOT:
416 0 : gistRedoPageUpdateRecord(lsn, record, true);
417 0 : break;
418 : case XLOG_GIST_PAGE_SPLIT:
419 0 : gistRedoPageSplitRecord(lsn, record);
420 0 : break;
421 : case XLOG_GIST_CREATE_INDEX:
422 0 : gistRedoCreateIndex(lsn, record);
423 0 : break;
424 : case XLOG_GIST_INSERT_COMPLETE:
425 0 : gistRedoCompleteInsert(lsn, record);
426 0 : break;
427 : default:
428 0 : elog(PANIC, "gist_redo: unknown op code %u", info);
429 : }
430 :
431 0 : MemoryContextSwitchTo(oldCxt);
432 0 : MemoryContextReset(opCtx);
433 0 : }
434 :
435 : static void
436 : out_target(StringInfo buf, RelFileNode node, ItemPointerData key)
437 0 : {
438 0 : appendStringInfo(buf, "rel %u/%u/%u",
439 : node.spcNode, node.dbNode, node.relNode);
440 0 : if (ItemPointerIsValid(&key))
441 0 : appendStringInfo(buf, "; tid %u/%u",
442 : ItemPointerGetBlockNumber(&key),
443 : ItemPointerGetOffsetNumber(&key));
444 0 : }
445 :
446 : static void
447 : out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
448 0 : {
449 0 : out_target(buf, xlrec->node, xlrec->key);
450 0 : appendStringInfo(buf, "; block number %u", xlrec->blkno);
451 0 : }
452 :
453 : static void
454 : out_gistxlogPageDelete(StringInfo buf, gistxlogPageDelete *xlrec)
455 0 : {
456 0 : appendStringInfo(buf, "page_delete: rel %u/%u/%u; blkno %u",
457 : xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode,
458 : xlrec->blkno);
459 0 : }
460 :
461 : static void
462 : out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
463 0 : {
464 0 : appendStringInfo(buf, "page_split: ");
465 0 : out_target(buf, xlrec->node, xlrec->key);
466 0 : appendStringInfo(buf, "; block number %u splits to %d pages",
467 : xlrec->origblkno, xlrec->npage);
468 0 : }
469 :
470 : void
471 : gist_desc(StringInfo buf, uint8 xl_info, char *rec)
472 0 : {
473 0 : uint8 info = xl_info & ~XLR_INFO_MASK;
474 :
475 0 : switch (info)
476 : {
477 : case XLOG_GIST_PAGE_UPDATE:
478 0 : appendStringInfo(buf, "page_update: ");
479 0 : out_gistxlogPageUpdate(buf, (gistxlogPageUpdate *) rec);
480 0 : break;
481 : case XLOG_GIST_PAGE_DELETE:
482 0 : out_gistxlogPageDelete(buf, (gistxlogPageDelete *) rec);
483 0 : break;
484 : case XLOG_GIST_NEW_ROOT:
485 0 : appendStringInfo(buf, "new_root: ");
486 0 : out_target(buf, ((gistxlogPageUpdate *) rec)->node, ((gistxlogPageUpdate *) rec)->key);
487 0 : break;
488 : case XLOG_GIST_PAGE_SPLIT:
489 0 : out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec);
490 0 : break;
491 : case XLOG_GIST_CREATE_INDEX:
492 0 : appendStringInfo(buf, "create_index: rel %u/%u/%u",
493 : ((RelFileNode *) rec)->spcNode,
494 : ((RelFileNode *) rec)->dbNode,
495 : ((RelFileNode *) rec)->relNode);
496 0 : break;
497 : case XLOG_GIST_INSERT_COMPLETE:
498 0 : appendStringInfo(buf, "complete_insert: rel %u/%u/%u",
499 : ((gistxlogInsertComplete *) rec)->node.spcNode,
500 : ((gistxlogInsertComplete *) rec)->node.dbNode,
501 : ((gistxlogInsertComplete *) rec)->node.relNode);
502 0 : break;
503 : default:
504 0 : appendStringInfo(buf, "unknown gist op code %u", info);
505 : break;
506 : }
507 0 : }
508 :
509 : IndexTuple
510 : gist_form_invalid_tuple(BlockNumber blkno)
511 0 : {
512 : /*
513 : * we don't alloc space for null's bitmap, this is invalid tuple, be
514 : * carefull in read and write code
515 : */
516 0 : Size size = IndexInfoFindDataOffset(0);
517 0 : IndexTuple tuple = (IndexTuple) palloc0(size);
518 :
519 0 : tuple->t_info |= size;
520 :
521 0 : ItemPointerSetBlockNumber(&(tuple->t_tid), blkno);
522 0 : GistTupleSetInvalid(tuple);
523 :
524 0 : return tuple;
525 : }
526 :
527 :
528 : static void
529 : gistxlogFindPath(Relation index, gistIncompleteInsert *insert)
530 0 : {
531 : GISTInsertStack *top;
532 :
533 0 : insert->pathlen = 0;
534 0 : insert->path = NULL;
535 :
536 0 : if ((top = gistFindPath(index, insert->origblkno)) != NULL)
537 : {
538 : int i;
539 : GISTInsertStack *ptr;
540 :
541 0 : for (ptr = top; ptr; ptr = ptr->parent)
542 0 : insert->pathlen++;
543 :
544 0 : insert->path = (BlockNumber *) palloc(sizeof(BlockNumber) * insert->pathlen);
545 :
546 0 : i = 0;
547 0 : for (ptr = top; ptr; ptr = ptr->parent)
548 0 : insert->path[i++] = ptr->blkno;
549 : }
550 : else
551 0 : elog(ERROR, "lost parent for block %u", insert->origblkno);
552 0 : }
553 :
554 : static SplitedPageLayout *
555 : gistMakePageLayout(Buffer *buffers, int nbuffers)
556 0 : {
557 0 : SplitedPageLayout *res = NULL,
558 : *resptr;
559 :
560 0 : while (nbuffers-- > 0)
561 : {
562 0 : Page page = BufferGetPage(buffers[nbuffers]);
563 : IndexTuple *vec;
564 : int veclen;
565 :
566 0 : resptr = (SplitedPageLayout *) palloc0(sizeof(SplitedPageLayout));
567 :
568 0 : resptr->block.blkno = BufferGetBlockNumber(buffers[nbuffers]);
569 0 : resptr->block.num = PageGetMaxOffsetNumber(page);
570 :
571 0 : vec = gistextractpage(page, &veclen);
572 0 : resptr->list = gistfillitupvec(vec, veclen, &(resptr->lenlist));
573 :
574 0 : resptr->next = res;
575 0 : res = resptr;
576 : }
577 :
578 0 : return res;
579 : }
580 :
581 : /*
582 : * Continue insert after crash. In normal situations, there aren't any
583 : * incomplete inserts, but if a crash occurs partway through an insertion
584 : * sequence, we'll need to finish making the index valid at the end of WAL
585 : * replay.
586 : *
587 : * Note that we assume the index is now in a valid state, except for the
588 : * unfinished insertion. In particular it's safe to invoke gistFindPath();
589 : * there shouldn't be any garbage pages for it to run into.
590 : *
591 : * To complete insert we can't use basic insertion algorithm because
592 : * during insertion we can't call user-defined support functions of opclass.
593 : * So, we insert 'invalid' tuples without real key and do it by separate algorithm.
594 : * 'invalid' tuple should be updated by vacuum full.
595 : */
596 : static void
597 : gistContinueInsert(gistIncompleteInsert *insert)
598 0 : {
599 : IndexTuple *itup;
600 : int i,
601 : lenitup;
602 : Relation index;
603 :
604 0 : index = XLogOpenRelation(insert->node);
605 :
606 : /*
607 : * needed vector itup never will be more than initial lenblkno+2, because
608 : * during this processing Indextuple can be only smaller
609 : */
610 0 : lenitup = insert->lenblk;
611 0 : itup = (IndexTuple *) palloc(sizeof(IndexTuple) * (lenitup + 2 /* guarantee root split */ ));
612 :
613 0 : for (i = 0; i < insert->lenblk; i++)
614 0 : itup[i] = gist_form_invalid_tuple(insert->blkno[i]);
615 :
616 : /*
617 : * any insertion of itup[] should make LOG message about
618 : */
619 :
620 0 : if (insert->origblkno == GIST_ROOT_BLKNO)
621 : {
622 : /*
623 : * it was split root, so we should only make new root. it can't be
624 : * simple insert into root, we should replace all content of root.
625 : */
626 0 : Buffer buffer = XLogReadBuffer(index, GIST_ROOT_BLKNO, true);
627 :
628 0 : gistnewroot(index, buffer, itup, lenitup, NULL);
629 0 : UnlockReleaseBuffer(buffer);
630 : }
631 : else
632 : {
633 : Buffer *buffers;
634 : Page *pages;
635 : int numbuffer;
636 : OffsetNumber *todelete;
637 :
638 : /* construct path */
639 0 : gistxlogFindPath(index, insert);
640 :
641 : Assert(insert->pathlen > 0);
642 :
643 0 : buffers = (Buffer *) palloc(sizeof(Buffer) * (insert->lenblk + 2 /* guarantee root split */ ));
644 0 : pages = (Page *) palloc(sizeof(Page) * (insert->lenblk + 2 /* guarantee root split */ ));
645 0 : todelete = (OffsetNumber *) palloc(sizeof(OffsetNumber) * (insert->lenblk + 2 /* guarantee root split */ ));
646 :
647 0 : for (i = 0; i < insert->pathlen; i++)
648 : {
649 : int j,
650 : k,
651 0 : pituplen = 0;
652 : XLogRecData *rdata;
653 : XLogRecPtr recptr;
654 0 : Buffer tempbuffer = InvalidBuffer;
655 0 : int ntodelete = 0;
656 :
657 0 : numbuffer = 1;
658 0 : buffers[0] = ReadBuffer(index, insert->path[i]);
659 0 : LockBuffer(buffers[0], GIST_EXCLUSIVE);
660 :
661 : /*
662 : * we check buffer, because we restored page earlier
663 : */
664 0 : gistcheckpage(index, buffers[0]);
665 :
666 0 : pages[0] = BufferGetPage(buffers[0]);
667 : Assert(!GistPageIsLeaf(pages[0]));
668 :
669 0 : pituplen = PageGetMaxOffsetNumber(pages[0]);
670 :
671 : /* find remove old IndexTuples to remove */
672 0 : for (j = 0; j < pituplen && ntodelete < lenitup; j++)
673 : {
674 : BlockNumber blkno;
675 0 : ItemId iid = PageGetItemId(pages[0], j + FirstOffsetNumber);
676 0 : IndexTuple idxtup = (IndexTuple) PageGetItem(pages[0], iid);
677 :
678 0 : blkno = ItemPointerGetBlockNumber(&(idxtup->t_tid));
679 :
680 0 : for (k = 0; k < lenitup; k++)
681 0 : if (ItemPointerGetBlockNumber(&(itup[k]->t_tid)) == blkno)
682 : {
683 0 : todelete[ntodelete] = j + FirstOffsetNumber - ntodelete;
684 0 : ntodelete++;
685 0 : break;
686 : }
687 : }
688 :
689 0 : if (ntodelete == 0)
690 0 : elog(PANIC, "gistContinueInsert: cannot find pointer to page(s)");
691 :
692 : /*
693 : * we check space with subtraction only first tuple to delete,
694 : * hope, that wiil be enough space....
695 : */
696 :
697 0 : if (gistnospace(pages[0], itup, lenitup, *todelete, 0))
698 : {
699 :
700 : /* no space left on page, so we must split */
701 0 : buffers[numbuffer] = ReadBuffer(index, P_NEW);
702 0 : LockBuffer(buffers[numbuffer], GIST_EXCLUSIVE);
703 0 : GISTInitBuffer(buffers[numbuffer], 0);
704 0 : pages[numbuffer] = BufferGetPage(buffers[numbuffer]);
705 0 : gistfillbuffer(index, pages[numbuffer], itup, lenitup, FirstOffsetNumber);
706 0 : numbuffer++;
707 :
708 0 : if (BufferGetBlockNumber(buffers[0]) == GIST_ROOT_BLKNO)
709 : {
710 : Buffer tmp;
711 :
712 : /*
713 : * we split root, just copy content from root to new page
714 : */
715 :
716 : /* sanity check */
717 0 : if (i + 1 != insert->pathlen)
718 0 : elog(PANIC, "unexpected pathlen in index \"%s\"",
719 : RelationGetRelationName(index));
720 :
721 : /* fill new page, root will be changed later */
722 0 : tempbuffer = ReadBuffer(index, P_NEW);
723 0 : LockBuffer(tempbuffer, GIST_EXCLUSIVE);
724 0 : memcpy(BufferGetPage(tempbuffer), pages[0], BufferGetPageSize(tempbuffer));
725 :
726 : /* swap buffers[0] (was root) and temp buffer */
727 0 : tmp = buffers[0];
728 0 : buffers[0] = tempbuffer;
729 0 : tempbuffer = tmp; /* now in tempbuffer GIST_ROOT_BLKNO,
730 : * it is still unchanged */
731 :
732 0 : pages[0] = BufferGetPage(buffers[0]);
733 : }
734 :
735 0 : START_CRIT_SECTION();
736 :
737 0 : for (j = 0; j < ntodelete; j++)
738 0 : PageIndexTupleDelete(pages[0], todelete[j]);
739 :
740 0 : rdata = formSplitRdata(index->rd_node, insert->path[i],
741 : false, &(insert->key),
742 : gistMakePageLayout(buffers, numbuffer));
743 :
744 : }
745 : else
746 : {
747 0 : START_CRIT_SECTION();
748 :
749 0 : for (j = 0; j < ntodelete; j++)
750 0 : PageIndexTupleDelete(pages[0], todelete[j]);
751 0 : gistfillbuffer(index, pages[0], itup, lenitup, InvalidOffsetNumber);
752 :
753 0 : rdata = formUpdateRdata(index->rd_node, buffers[0],
754 : todelete, ntodelete,
755 : itup, lenitup, &(insert->key));
756 : }
757 :
758 : /*
759 : * use insert->key as mark for completion of insert (form*Rdata()
760 : * above) for following possible replays
761 : */
762 :
763 : /* write pages, we should mark it dirty befor XLogInsert() */
764 0 : for (j = 0; j < numbuffer; j++)
765 : {
766 0 : GistPageGetOpaque(pages[j])->rightlink = InvalidBlockNumber;
767 0 : MarkBufferDirty(buffers[j]);
768 : }
769 0 : recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
770 0 : for (j = 0; j < numbuffer; j++)
771 : {
772 0 : PageSetLSN(pages[j], recptr);
773 0 : PageSetTLI(pages[j], ThisTimeLineID);
774 : }
775 :
776 0 : END_CRIT_SECTION();
777 :
778 0 : lenitup = numbuffer;
779 0 : for (j = 0; j < numbuffer; j++)
780 : {
781 0 : itup[j] = gist_form_invalid_tuple(BufferGetBlockNumber(buffers[j]));
782 0 : UnlockReleaseBuffer(buffers[j]);
783 : }
784 :
785 0 : if (tempbuffer != InvalidBuffer)
786 : {
787 : /*
788 : * it was a root split, so fill it by new values
789 : */
790 0 : gistnewroot(index, tempbuffer, itup, lenitup, &(insert->key));
791 0 : UnlockReleaseBuffer(tempbuffer);
792 : }
793 : }
794 : }
795 :
796 0 : ereport(LOG,
797 : (errmsg("index %u/%u/%u needs VACUUM FULL or REINDEX to finish crash recovery",
798 : insert->node.spcNode, insert->node.dbNode, insert->node.relNode),
799 : errdetail("Incomplete insertion detected during crash replay.")));
800 0 : }
801 :
802 : void
803 : gist_xlog_startup(void)
804 0 : {
805 0 : incomplete_inserts = NIL;
806 0 : insertCtx = AllocSetContextCreate(CurrentMemoryContext,
807 : "GiST recovery temporary context",
808 : ALLOCSET_DEFAULT_MINSIZE,
809 : ALLOCSET_DEFAULT_INITSIZE,
810 : ALLOCSET_DEFAULT_MAXSIZE);
811 0 : opCtx = createTempGistContext();
812 0 : }
813 :
814 : void
815 : gist_xlog_cleanup(void)
816 0 : {
817 : ListCell *l;
818 : MemoryContext oldCxt;
819 :
820 0 : oldCxt = MemoryContextSwitchTo(opCtx);
821 :
822 0 : foreach(l, incomplete_inserts)
823 : {
824 0 : gistIncompleteInsert *insert = (gistIncompleteInsert *) lfirst(l);
825 :
826 0 : gistContinueInsert(insert);
827 0 : MemoryContextReset(opCtx);
828 : }
829 0 : MemoryContextSwitchTo(oldCxt);
830 :
831 0 : MemoryContextDelete(opCtx);
832 0 : MemoryContextDelete(insertCtx);
833 0 : }
834 :
835 : bool
836 : gist_safe_restartpoint(void)
837 0 : {
838 0 : if (incomplete_inserts)
839 0 : return false;
840 0 : return true;
841 : }
842 :
843 :
844 : XLogRecData *
845 : formSplitRdata(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
846 : ItemPointer key, SplitedPageLayout *dist)
847 49 : {
848 : XLogRecData *rdata;
849 49 : gistxlogPageSplit *xlrec = (gistxlogPageSplit *) palloc(sizeof(gistxlogPageSplit));
850 : SplitedPageLayout *ptr;
851 49 : int npage = 0,
852 49 : cur = 1;
853 :
854 49 : ptr = dist;
855 196 : while (ptr)
856 : {
857 98 : npage++;
858 98 : ptr = ptr->next;
859 : }
860 :
861 49 : rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (npage * 2 + 2));
862 :
863 49 : xlrec->node = node;
864 49 : xlrec->origblkno = blkno;
865 49 : xlrec->origleaf = page_is_leaf;
866 49 : xlrec->npage = (uint16) npage;
867 49 : if (key)
868 49 : xlrec->key = *key;
869 : else
870 0 : ItemPointerSetInvalid(&(xlrec->key));
871 :
872 49 : rdata[0].buffer = InvalidBuffer;
873 49 : rdata[0].data = (char *) xlrec;
874 49 : rdata[0].len = sizeof(gistxlogPageSplit);
875 49 : rdata[0].next = NULL;
876 :
877 49 : ptr = dist;
878 196 : while (ptr)
879 : {
880 98 : rdata[cur].buffer = InvalidBuffer;
881 98 : rdata[cur].data = (char *) &(ptr->block);
882 98 : rdata[cur].len = sizeof(gistxlogPage);
883 98 : rdata[cur - 1].next = &(rdata[cur]);
884 98 : cur++;
885 :
886 98 : rdata[cur].buffer = InvalidBuffer;
887 98 : rdata[cur].data = (char *) (ptr->list);
888 98 : rdata[cur].len = ptr->lenlist;
889 98 : rdata[cur - 1].next = &(rdata[cur]);
890 98 : rdata[cur].next = NULL;
891 98 : cur++;
892 98 : ptr = ptr->next;
893 : }
894 :
895 49 : return rdata;
896 : }
897 :
898 : /*
899 : * Construct the rdata array for an XLOG record describing a page update
900 : * (deletion and/or insertion of tuples on a single index page).
901 : *
902 : * Note that both the todelete array and the tuples are marked as belonging
903 : * to the target buffer; they need not be stored in XLOG if XLogInsert decides
904 : * to log the whole buffer contents instead. Also, we take care that there's
905 : * at least one rdata item referencing the buffer, even when ntodelete and
906 : * ituplen are both zero; this ensures that XLogInsert knows about the buffer.
907 : */
908 : XLogRecData *
909 : formUpdateRdata(RelFileNode node, Buffer buffer,
910 : OffsetNumber *todelete, int ntodelete,
911 : IndexTuple *itup, int ituplen, ItemPointer key)
912 4445 : {
913 : XLogRecData *rdata;
914 : gistxlogPageUpdate *xlrec;
915 : int cur,
916 : i;
917 :
918 4445 : rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (3 + ituplen));
919 4445 : xlrec = (gistxlogPageUpdate *) palloc(sizeof(gistxlogPageUpdate));
920 :
921 4445 : xlrec->node = node;
922 4445 : xlrec->blkno = BufferGetBlockNumber(buffer);
923 4445 : xlrec->ntodelete = ntodelete;
924 :
925 4445 : if (key)
926 4445 : xlrec->key = *key;
927 : else
928 0 : ItemPointerSetInvalid(&(xlrec->key));
929 :
930 4445 : rdata[0].buffer = buffer;
931 4445 : rdata[0].buffer_std = true;
932 4445 : rdata[0].data = NULL;
933 4445 : rdata[0].len = 0;
934 4445 : rdata[0].next = &(rdata[1]);
935 :
936 4445 : rdata[1].data = (char *) xlrec;
937 4445 : rdata[1].len = sizeof(gistxlogPageUpdate);
938 4445 : rdata[1].buffer = InvalidBuffer;
939 4445 : rdata[1].next = &(rdata[2]);
940 :
941 4445 : rdata[2].data = (char *) todelete;
942 4445 : rdata[2].len = MAXALIGN(sizeof(OffsetNumber) * ntodelete);
943 4445 : rdata[2].buffer = buffer;
944 4445 : rdata[2].buffer_std = true;
945 4445 : rdata[2].next = NULL;
946 :
947 : /* new tuples */
948 4445 : cur = 3;
949 8939 : for (i = 0; i < ituplen; i++)
950 : {
951 4494 : rdata[cur - 1].next = &(rdata[cur]);
952 4494 : rdata[cur].data = (char *) (itup[i]);
953 4494 : rdata[cur].len = IndexTupleSize(itup[i]);
954 4494 : rdata[cur].buffer = buffer;
955 4494 : rdata[cur].buffer_std = true;
956 4494 : rdata[cur].next = NULL;
957 4494 : cur++;
958 : }
959 :
960 4445 : return rdata;
961 : }
962 :
963 : XLogRecPtr
964 : gistxlogInsertCompletion(RelFileNode node, ItemPointerData *keys, int len)
965 3080 : {
966 : gistxlogInsertComplete xlrec;
967 : XLogRecData rdata[2];
968 : XLogRecPtr recptr;
969 :
970 : Assert(len > 0);
971 3080 : xlrec.node = node;
972 :
973 3080 : rdata[0].buffer = InvalidBuffer;
974 3080 : rdata[0].data = (char *) &xlrec;
975 3080 : rdata[0].len = sizeof(gistxlogInsertComplete);
976 3080 : rdata[0].next = &(rdata[1]);
977 :
978 3080 : rdata[1].buffer = InvalidBuffer;
979 3080 : rdata[1].data = (char *) keys;
980 3080 : rdata[1].len = sizeof(ItemPointerData) * len;
981 3080 : rdata[1].next = NULL;
982 :
983 3080 : START_CRIT_SECTION();
984 :
985 3080 : recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_INSERT_COMPLETE, rdata);
986 :
987 3080 : END_CRIT_SECTION();
988 :
989 3080 : return recptr;
990 : }
|