1 /*
2 * brin_xlog.c
3 * XLog replay routines for BRIN indexes
4 *
5 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
7 *
8 * IDENTIFICATION
9 * src/backend/access/brin/brin_xlog.c
10 */
11 #include "postgres.h"
12
13 #include "access/brin_page.h"
14 #include "access/brin_pageops.h"
15 #include "access/brin_xlog.h"
16 #include "access/bufmask.h"
17 #include "access/xlogutils.h"
18
19
20 /*
21 * xlog replay routines
22 */
23 static void
brin_xlog_createidx(XLogReaderState * record)24 brin_xlog_createidx(XLogReaderState *record)
25 {
26 XLogRecPtr lsn = record->EndRecPtr;
27 xl_brin_createidx *xlrec = (xl_brin_createidx *) XLogRecGetData(record);
28 Buffer buf;
29 Page page;
30
31 /* create the index' metapage */
32 buf = XLogInitBufferForRedo(record, 0);
33 Assert(BufferIsValid(buf));
34 page = (Page) BufferGetPage(buf);
35 brin_metapage_init(page, xlrec->pagesPerRange, xlrec->version);
36 PageSetLSN(page, lsn);
37 MarkBufferDirty(buf);
38 UnlockReleaseBuffer(buf);
39 }
40
41 /*
42 * Common part of an insert or update. Inserts the new tuple and updates the
43 * revmap.
44 */
45 static void
brin_xlog_insert_update(XLogReaderState * record,xl_brin_insert * xlrec)46 brin_xlog_insert_update(XLogReaderState *record,
47 xl_brin_insert *xlrec)
48 {
49 XLogRecPtr lsn = record->EndRecPtr;
50 Buffer buffer;
51 BlockNumber regpgno;
52 Page page;
53 XLogRedoAction action;
54
55 /*
56 * If we inserted the first and only tuple on the page, re-initialize the
57 * page from scratch.
58 */
59 if (XLogRecGetInfo(record) & XLOG_BRIN_INIT_PAGE)
60 {
61 buffer = XLogInitBufferForRedo(record, 0);
62 page = BufferGetPage(buffer);
63 brin_page_init(page, BRIN_PAGETYPE_REGULAR);
64 action = BLK_NEEDS_REDO;
65 }
66 else
67 {
68 action = XLogReadBufferForRedo(record, 0, &buffer);
69 }
70
71 /* need this page's blkno to store in revmap */
72 regpgno = BufferGetBlockNumber(buffer);
73
74 /* insert the index item into the page */
75 if (action == BLK_NEEDS_REDO)
76 {
77 OffsetNumber offnum;
78 BrinTuple *tuple;
79 Size tuplen;
80
81 tuple = (BrinTuple *) XLogRecGetBlockData(record, 0, &tuplen);
82
83 Assert(tuple->bt_blkno == xlrec->heapBlk);
84
85 page = (Page) BufferGetPage(buffer);
86 offnum = xlrec->offnum;
87 if (PageGetMaxOffsetNumber(page) + 1 < offnum)
88 elog(PANIC, "brin_xlog_insert_update: invalid max offset number");
89
90 offnum = PageAddItem(page, (Item) tuple, tuplen, offnum, true, false);
91 if (offnum == InvalidOffsetNumber)
92 elog(PANIC, "brin_xlog_insert_update: failed to add tuple");
93
94 PageSetLSN(page, lsn);
95 MarkBufferDirty(buffer);
96 }
97 if (BufferIsValid(buffer))
98 UnlockReleaseBuffer(buffer);
99
100 /* update the revmap */
101 action = XLogReadBufferForRedo(record, 1, &buffer);
102 if (action == BLK_NEEDS_REDO)
103 {
104 ItemPointerData tid;
105
106 ItemPointerSet(&tid, regpgno, xlrec->offnum);
107 page = (Page) BufferGetPage(buffer);
108
109 brinSetHeapBlockItemptr(buffer, xlrec->pagesPerRange, xlrec->heapBlk,
110 tid);
111 PageSetLSN(page, lsn);
112 MarkBufferDirty(buffer);
113 }
114 if (BufferIsValid(buffer))
115 UnlockReleaseBuffer(buffer);
116
117 /* XXX no FSM updates here ... */
118 }
119
120 /*
121 * replay a BRIN index insertion
122 */
123 static void
brin_xlog_insert(XLogReaderState * record)124 brin_xlog_insert(XLogReaderState *record)
125 {
126 xl_brin_insert *xlrec = (xl_brin_insert *) XLogRecGetData(record);
127
128 brin_xlog_insert_update(record, xlrec);
129 }
130
131 /*
132 * replay a BRIN index update
133 */
134 static void
brin_xlog_update(XLogReaderState * record)135 brin_xlog_update(XLogReaderState *record)
136 {
137 XLogRecPtr lsn = record->EndRecPtr;
138 xl_brin_update *xlrec = (xl_brin_update *) XLogRecGetData(record);
139 Buffer buffer;
140 XLogRedoAction action;
141
142 /* First remove the old tuple */
143 action = XLogReadBufferForRedo(record, 2, &buffer);
144 if (action == BLK_NEEDS_REDO)
145 {
146 Page page;
147 OffsetNumber offnum;
148
149 page = (Page) BufferGetPage(buffer);
150
151 offnum = xlrec->oldOffnum;
152
153 PageIndexTupleDeleteNoCompact(page, offnum);
154
155 PageSetLSN(page, lsn);
156 MarkBufferDirty(buffer);
157 }
158
159 /* Then insert the new tuple and update revmap, like in an insertion. */
160 brin_xlog_insert_update(record, &xlrec->insert);
161
162 if (BufferIsValid(buffer))
163 UnlockReleaseBuffer(buffer);
164 }
165
166 /*
167 * Update a tuple on a single page.
168 */
169 static void
brin_xlog_samepage_update(XLogReaderState * record)170 brin_xlog_samepage_update(XLogReaderState *record)
171 {
172 XLogRecPtr lsn = record->EndRecPtr;
173 xl_brin_samepage_update *xlrec;
174 Buffer buffer;
175 XLogRedoAction action;
176
177 xlrec = (xl_brin_samepage_update *) XLogRecGetData(record);
178 action = XLogReadBufferForRedo(record, 0, &buffer);
179 if (action == BLK_NEEDS_REDO)
180 {
181 Size tuplen;
182 BrinTuple *brintuple;
183 Page page;
184 OffsetNumber offnum;
185
186 brintuple = (BrinTuple *) XLogRecGetBlockData(record, 0, &tuplen);
187
188 page = (Page) BufferGetPage(buffer);
189
190 offnum = xlrec->offnum;
191
192 if (!PageIndexTupleOverwrite(page, offnum, (Item) brintuple, tuplen))
193 elog(PANIC, "brin_xlog_samepage_update: failed to replace tuple");
194
195 PageSetLSN(page, lsn);
196 MarkBufferDirty(buffer);
197 }
198 if (BufferIsValid(buffer))
199 UnlockReleaseBuffer(buffer);
200
201 /* XXX no FSM updates here ... */
202 }
203
204 /*
205 * Replay a revmap page extension
206 */
207 static void
brin_xlog_revmap_extend(XLogReaderState * record)208 brin_xlog_revmap_extend(XLogReaderState *record)
209 {
210 XLogRecPtr lsn = record->EndRecPtr;
211 xl_brin_revmap_extend *xlrec;
212 Buffer metabuf;
213 Buffer buf;
214 Page page;
215 BlockNumber targetBlk;
216 XLogRedoAction action;
217
218 xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record);
219
220 XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk);
221 Assert(xlrec->targetBlk == targetBlk);
222
223 /* Update the metapage */
224 action = XLogReadBufferForRedo(record, 0, &metabuf);
225 if (action == BLK_NEEDS_REDO)
226 {
227 Page metapg;
228 BrinMetaPageData *metadata;
229
230 metapg = BufferGetPage(metabuf);
231 metadata = (BrinMetaPageData *) PageGetContents(metapg);
232
233 Assert(metadata->lastRevmapPage == xlrec->targetBlk - 1);
234 metadata->lastRevmapPage = xlrec->targetBlk;
235
236 PageSetLSN(metapg, lsn);
237 MarkBufferDirty(metabuf);
238 }
239
240 /*
241 * Re-init the target block as a revmap page. There's never a full- page
242 * image here.
243 */
244
245 buf = XLogInitBufferForRedo(record, 1);
246 page = (Page) BufferGetPage(buf);
247 brin_page_init(page, BRIN_PAGETYPE_REVMAP);
248
249 PageSetLSN(page, lsn);
250 MarkBufferDirty(buf);
251
252 UnlockReleaseBuffer(buf);
253 if (BufferIsValid(metabuf))
254 UnlockReleaseBuffer(metabuf);
255 }
256
257 static void
brin_xlog_desummarize_page(XLogReaderState * record)258 brin_xlog_desummarize_page(XLogReaderState *record)
259 {
260 XLogRecPtr lsn = record->EndRecPtr;
261 xl_brin_desummarize *xlrec;
262 Buffer buffer;
263 XLogRedoAction action;
264
265 xlrec = (xl_brin_desummarize *) XLogRecGetData(record);
266
267 /* Update the revmap */
268 action = XLogReadBufferForRedo(record, 0, &buffer);
269 if (action == BLK_NEEDS_REDO)
270 {
271 ItemPointerData iptr;
272
273 ItemPointerSetInvalid(&iptr);
274 brinSetHeapBlockItemptr(buffer, xlrec->pagesPerRange, xlrec->heapBlk, iptr);
275
276 PageSetLSN(BufferGetPage(buffer), lsn);
277 MarkBufferDirty(buffer);
278 }
279 if (BufferIsValid(buffer))
280 UnlockReleaseBuffer(buffer);
281
282 /* remove the leftover entry from the regular page */
283 action = XLogReadBufferForRedo(record, 1, &buffer);
284 if (action == BLK_NEEDS_REDO)
285 {
286 Page regPg = BufferGetPage(buffer);
287
288 PageIndexTupleDeleteNoCompact(regPg, xlrec->regOffset);
289
290 PageSetLSN(regPg, lsn);
291 MarkBufferDirty(buffer);
292 }
293 if (BufferIsValid(buffer))
294 UnlockReleaseBuffer(buffer);
295 }
296
297 void
brin_redo(XLogReaderState * record)298 brin_redo(XLogReaderState *record)
299 {
300 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
301
302 switch (info & XLOG_BRIN_OPMASK)
303 {
304 case XLOG_BRIN_CREATE_INDEX:
305 brin_xlog_createidx(record);
306 break;
307 case XLOG_BRIN_INSERT:
308 brin_xlog_insert(record);
309 break;
310 case XLOG_BRIN_UPDATE:
311 brin_xlog_update(record);
312 break;
313 case XLOG_BRIN_SAMEPAGE_UPDATE:
314 brin_xlog_samepage_update(record);
315 break;
316 case XLOG_BRIN_REVMAP_EXTEND:
317 brin_xlog_revmap_extend(record);
318 break;
319 case XLOG_BRIN_DESUMMARIZE:
320 brin_xlog_desummarize_page(record);
321 break;
322 default:
323 elog(PANIC, "brin_redo: unknown op code %u", info);
324 }
325 }
326
327 /*
328 * Mask a BRIN page before doing consistency checks.
329 */
330 void
brin_mask(char * pagedata,BlockNumber blkno)331 brin_mask(char *pagedata, BlockNumber blkno)
332 {
333 Page page = (Page) pagedata;
334
335 mask_page_lsn_and_checksum(page);
336
337 mask_page_hint_bits(page);
338
339 if (BRIN_IS_REGULAR_PAGE(page))
340 {
341 /* Regular brin pages contain unused space which needs to be masked. */
342 mask_unused_space(page);
343 }
344 }
345