1/*
2 * brin_xlog.c
3 * XLog replay routines for BRIN indexes
4 *
5 * Portions Copyright (c) 1996-2019, 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 */
23static void
24brin_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 */
45static void
46brin_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 */
123static void
124brin_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 */
134static void
135brin_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 */
169static void
170brin_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 */
207static void
208brin_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
238 /*
239 * Set pd_lower just past the end of the metadata. This is essential,
240 * because without doing so, metadata will be lost if xlog.c
241 * compresses the page. (We must do this here because pre-v11
242 * versions of PG did not set the metapage's pd_lower correctly, so a
243 * pg_upgraded index might contain the wrong value.)
244 */
245 ((PageHeader) metapg)->pd_lower =
246 ((char *) metadata + sizeof(BrinMetaPageData)) - (char *) metapg;
247
248 MarkBufferDirty(metabuf);
249 }
250
251 /*
252 * Re-init the target block as a revmap page. There's never a full- page
253 * image here.
254 */
255
256 buf = XLogInitBufferForRedo(record, 1);
257 page = (Page) BufferGetPage(buf);
258 brin_page_init(page, BRIN_PAGETYPE_REVMAP);
259
260 PageSetLSN(page, lsn);
261 MarkBufferDirty(buf);
262
263 UnlockReleaseBuffer(buf);
264 if (BufferIsValid(metabuf))
265 UnlockReleaseBuffer(metabuf);
266}
267
268static void
269brin_xlog_desummarize_page(XLogReaderState *record)
270{
271 XLogRecPtr lsn = record->EndRecPtr;
272 xl_brin_desummarize *xlrec;
273 Buffer buffer;
274 XLogRedoAction action;
275
276 xlrec = (xl_brin_desummarize *) XLogRecGetData(record);
277
278 /* Update the revmap */
279 action = XLogReadBufferForRedo(record, 0, &buffer);
280 if (action == BLK_NEEDS_REDO)
281 {
282 ItemPointerData iptr;
283
284 ItemPointerSetInvalid(&iptr);
285 brinSetHeapBlockItemptr(buffer, xlrec->pagesPerRange, xlrec->heapBlk, iptr);
286
287 PageSetLSN(BufferGetPage(buffer), lsn);
288 MarkBufferDirty(buffer);
289 }
290 if (BufferIsValid(buffer))
291 UnlockReleaseBuffer(buffer);
292
293 /* remove the leftover entry from the regular page */
294 action = XLogReadBufferForRedo(record, 1, &buffer);
295 if (action == BLK_NEEDS_REDO)
296 {
297 Page regPg = BufferGetPage(buffer);
298
299 PageIndexTupleDeleteNoCompact(regPg, xlrec->regOffset);
300
301 PageSetLSN(regPg, lsn);
302 MarkBufferDirty(buffer);
303 }
304 if (BufferIsValid(buffer))
305 UnlockReleaseBuffer(buffer);
306}
307
308void
309brin_redo(XLogReaderState *record)
310{
311 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
312
313 switch (info & XLOG_BRIN_OPMASK)
314 {
315 case XLOG_BRIN_CREATE_INDEX:
316 brin_xlog_createidx(record);
317 break;
318 case XLOG_BRIN_INSERT:
319 brin_xlog_insert(record);
320 break;
321 case XLOG_BRIN_UPDATE:
322 brin_xlog_update(record);
323 break;
324 case XLOG_BRIN_SAMEPAGE_UPDATE:
325 brin_xlog_samepage_update(record);
326 break;
327 case XLOG_BRIN_REVMAP_EXTEND:
328 brin_xlog_revmap_extend(record);
329 break;
330 case XLOG_BRIN_DESUMMARIZE:
331 brin_xlog_desummarize_page(record);
332 break;
333 default:
334 elog(PANIC, "brin_redo: unknown op code %u", info);
335 }
336}
337
338/*
339 * Mask a BRIN page before doing consistency checks.
340 */
341void
342brin_mask(char *pagedata, BlockNumber blkno)
343{
344 Page page = (Page) pagedata;
345 PageHeader pagehdr = (PageHeader) page;
346
347 mask_page_lsn_and_checksum(page);
348
349 mask_page_hint_bits(page);
350
351 /*
352 * Regular brin pages contain unused space which needs to be masked.
353 * Similarly for meta pages, but mask it only if pd_lower appears to have
354 * been set correctly.
355 */
356 if (BRIN_IS_REGULAR_PAGE(page) ||
357 (BRIN_IS_META_PAGE(page) && pagehdr->pd_lower > SizeOfPageHeaderData))
358 {
359 mask_unused_space(page);
360 }
361}
362