1 /*
2  * brin_xlog.c
3  *		XLog replay routines for BRIN indexes
4  *
5  * Portions Copyright (c) 1996-2016, 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/xlogutils.h"
17 
18 
19 /*
20  * xlog replay routines
21  */
22 static void
brin_xlog_createidx(XLogReaderState * record)23 brin_xlog_createidx(XLogReaderState *record)
24 {
25 	XLogRecPtr	lsn = record->EndRecPtr;
26 	xl_brin_createidx *xlrec = (xl_brin_createidx *) XLogRecGetData(record);
27 	Buffer		buf;
28 	Page		page;
29 
30 	/* create the index' metapage */
31 	buf = XLogInitBufferForRedo(record, 0);
32 	Assert(BufferIsValid(buf));
33 	page = (Page) BufferGetPage(buf);
34 	brin_metapage_init(page, xlrec->pagesPerRange, xlrec->version);
35 	PageSetLSN(page, lsn);
36 	MarkBufferDirty(buf);
37 	UnlockReleaseBuffer(buf);
38 }
39 
40 /*
41  * Common part of an insert or update. Inserts the new tuple and updates the
42  * revmap.
43  */
44 static void
brin_xlog_insert_update(XLogReaderState * record,xl_brin_insert * xlrec)45 brin_xlog_insert_update(XLogReaderState *record,
46 						xl_brin_insert *xlrec)
47 {
48 	XLogRecPtr	lsn = record->EndRecPtr;
49 	Buffer		buffer;
50 	BlockNumber regpgno;
51 	Page		page;
52 	XLogRedoAction action;
53 
54 	/*
55 	 * If we inserted the first and only tuple on the page, re-initialize the
56 	 * page from scratch.
57 	 */
58 	if (XLogRecGetInfo(record) & XLOG_BRIN_INIT_PAGE)
59 	{
60 		buffer = XLogInitBufferForRedo(record, 0);
61 		page = BufferGetPage(buffer);
62 		brin_page_init(page, BRIN_PAGETYPE_REGULAR);
63 		action = BLK_NEEDS_REDO;
64 	}
65 	else
66 	{
67 		action = XLogReadBufferForRedo(record, 0, &buffer);
68 	}
69 
70 	/* need this page's blkno to store in revmap */
71 	regpgno = BufferGetBlockNumber(buffer);
72 
73 	/* insert the index item into the page */
74 	if (action == BLK_NEEDS_REDO)
75 	{
76 		OffsetNumber offnum;
77 		BrinTuple  *tuple;
78 		Size		tuplen;
79 
80 		tuple = (BrinTuple *) XLogRecGetBlockData(record, 0, &tuplen);
81 
82 		Assert(tuple->bt_blkno == xlrec->heapBlk);
83 
84 		page = (Page) BufferGetPage(buffer);
85 		offnum = xlrec->offnum;
86 		if (PageGetMaxOffsetNumber(page) + 1 < offnum)
87 			elog(PANIC, "brin_xlog_insert_update: invalid max offset number");
88 
89 		offnum = PageAddItem(page, (Item) tuple, tuplen, offnum, true, false);
90 		if (offnum == InvalidOffsetNumber)
91 			elog(PANIC, "brin_xlog_insert_update: failed to add tuple");
92 
93 		PageSetLSN(page, lsn);
94 		MarkBufferDirty(buffer);
95 	}
96 	if (BufferIsValid(buffer))
97 		UnlockReleaseBuffer(buffer);
98 
99 	/* update the revmap */
100 	action = XLogReadBufferForRedo(record, 1, &buffer);
101 	if (action == BLK_NEEDS_REDO)
102 	{
103 		ItemPointerData tid;
104 
105 		ItemPointerSet(&tid, regpgno, xlrec->offnum);
106 		page = (Page) BufferGetPage(buffer);
107 
108 		brinSetHeapBlockItemptr(buffer, xlrec->pagesPerRange, xlrec->heapBlk,
109 								tid);
110 		PageSetLSN(page, lsn);
111 		MarkBufferDirty(buffer);
112 	}
113 	if (BufferIsValid(buffer))
114 		UnlockReleaseBuffer(buffer);
115 
116 	/* XXX no FSM updates here ... */
117 }
118 
119 /*
120  * replay a BRIN index insertion
121  */
122 static void
brin_xlog_insert(XLogReaderState * record)123 brin_xlog_insert(XLogReaderState *record)
124 {
125 	xl_brin_insert *xlrec = (xl_brin_insert *) XLogRecGetData(record);
126 
127 	brin_xlog_insert_update(record, xlrec);
128 }
129 
130 /*
131  * replay a BRIN index update
132  */
133 static void
brin_xlog_update(XLogReaderState * record)134 brin_xlog_update(XLogReaderState *record)
135 {
136 	XLogRecPtr	lsn = record->EndRecPtr;
137 	xl_brin_update *xlrec = (xl_brin_update *) XLogRecGetData(record);
138 	Buffer		buffer;
139 	XLogRedoAction action;
140 
141 	/* First remove the old tuple */
142 	action = XLogReadBufferForRedo(record, 2, &buffer);
143 	if (action == BLK_NEEDS_REDO)
144 	{
145 		Page		page;
146 		OffsetNumber offnum;
147 
148 		page = (Page) BufferGetPage(buffer);
149 
150 		offnum = xlrec->oldOffnum;
151 		if (PageGetMaxOffsetNumber(page) + 1 < offnum)
152 			elog(PANIC, "brin_xlog_update: invalid max offset number");
153 
154 		PageIndexDeleteNoCompact(page, &offnum, 1);
155 
156 		PageSetLSN(page, lsn);
157 		MarkBufferDirty(buffer);
158 	}
159 
160 	/* Then insert the new tuple and update revmap, like in an insertion. */
161 	brin_xlog_insert_update(record, &xlrec->insert);
162 
163 	if (BufferIsValid(buffer))
164 		UnlockReleaseBuffer(buffer);
165 }
166 
167 /*
168  * Update a tuple on a single page.
169  */
170 static void
brin_xlog_samepage_update(XLogReaderState * record)171 brin_xlog_samepage_update(XLogReaderState *record)
172 {
173 	XLogRecPtr	lsn = record->EndRecPtr;
174 	xl_brin_samepage_update *xlrec;
175 	Buffer		buffer;
176 	XLogRedoAction action;
177 
178 	xlrec = (xl_brin_samepage_update *) XLogRecGetData(record);
179 	action = XLogReadBufferForRedo(record, 0, &buffer);
180 	if (action == BLK_NEEDS_REDO)
181 	{
182 		Size		tuplen;
183 		BrinTuple  *brintuple;
184 		Page		page;
185 		OffsetNumber offnum;
186 
187 		brintuple = (BrinTuple *) XLogRecGetBlockData(record, 0, &tuplen);
188 
189 		page = (Page) BufferGetPage(buffer);
190 
191 		offnum = xlrec->offnum;
192 		if (PageGetMaxOffsetNumber(page) + 1 < offnum)
193 			elog(PANIC, "brin_xlog_samepage_update: invalid max offset number");
194 
195 		PageIndexDeleteNoCompact(page, &offnum, 1);
196 		offnum = PageAddItemExtended(page, (Item) brintuple, tuplen, offnum,
197 									 PAI_OVERWRITE | PAI_ALLOW_FAR_OFFSET);
198 		if (offnum == InvalidOffsetNumber)
199 			elog(PANIC, "brin_xlog_samepage_update: failed to add tuple");
200 
201 		PageSetLSN(page, lsn);
202 		MarkBufferDirty(buffer);
203 	}
204 	if (BufferIsValid(buffer))
205 		UnlockReleaseBuffer(buffer);
206 
207 	/* XXX no FSM updates here ... */
208 }
209 
210 /*
211  * Replay a revmap page extension
212  */
213 static void
brin_xlog_revmap_extend(XLogReaderState * record)214 brin_xlog_revmap_extend(XLogReaderState *record)
215 {
216 	XLogRecPtr	lsn = record->EndRecPtr;
217 	xl_brin_revmap_extend *xlrec;
218 	Buffer		metabuf;
219 	Buffer		buf;
220 	Page		page;
221 	BlockNumber targetBlk;
222 	XLogRedoAction action;
223 
224 	xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record);
225 
226 	XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk);
227 	Assert(xlrec->targetBlk == targetBlk);
228 
229 	/* Update the metapage */
230 	action = XLogReadBufferForRedo(record, 0, &metabuf);
231 	if (action == BLK_NEEDS_REDO)
232 	{
233 		Page		metapg;
234 		BrinMetaPageData *metadata;
235 
236 		metapg = BufferGetPage(metabuf);
237 		metadata = (BrinMetaPageData *) PageGetContents(metapg);
238 
239 		Assert(metadata->lastRevmapPage == xlrec->targetBlk - 1);
240 		metadata->lastRevmapPage = xlrec->targetBlk;
241 
242 		PageSetLSN(metapg, lsn);
243 		MarkBufferDirty(metabuf);
244 	}
245 
246 	/*
247 	 * Re-init the target block as a revmap page.  There's never a full- page
248 	 * image here.
249 	 */
250 
251 	buf = XLogInitBufferForRedo(record, 1);
252 	page = (Page) BufferGetPage(buf);
253 	brin_page_init(page, BRIN_PAGETYPE_REVMAP);
254 
255 	PageSetLSN(page, lsn);
256 	MarkBufferDirty(buf);
257 
258 	UnlockReleaseBuffer(buf);
259 	if (BufferIsValid(metabuf))
260 		UnlockReleaseBuffer(metabuf);
261 }
262 
263 void
brin_redo(XLogReaderState * record)264 brin_redo(XLogReaderState *record)
265 {
266 	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
267 
268 	switch (info & XLOG_BRIN_OPMASK)
269 	{
270 		case XLOG_BRIN_CREATE_INDEX:
271 			brin_xlog_createidx(record);
272 			break;
273 		case XLOG_BRIN_INSERT:
274 			brin_xlog_insert(record);
275 			break;
276 		case XLOG_BRIN_UPDATE:
277 			brin_xlog_update(record);
278 			break;
279 		case XLOG_BRIN_SAMEPAGE_UPDATE:
280 			brin_xlog_samepage_update(record);
281 			break;
282 		case XLOG_BRIN_REVMAP_EXTEND:
283 			brin_xlog_revmap_extend(record);
284 			break;
285 		default:
286 			elog(PANIC, "brin_redo: unknown op code %u", info);
287 	}
288 }
289