27
27
* - length of page region (OffsetNumber)
28
28
* - data - the data to place into the region ('length' number of bytes)
29
29
*
30
- * Unchanged regions of a page are not represented in its delta. As a
31
- *result, a delta can be more compact than the full page image. But having
32
- *an unchanged regionin the middle of two fragments that is smaller than
33
- *the fragment header (offset and length) does not pay off in terms of the
34
- *overall size of the delta. For this reason, webreak fragmentsonly if
35
- *the unchanged region isbigger than MATCH_THRESHOLD.
30
+ * Unchanged regions of a page are not represented in its delta. As a result,
31
+ * a delta can be more compact than the full page image. But having an
32
+ * unchanged regionbetween two fragments that is smaller than the fragment
33
+ * header (offset+ length) does not pay off in terms of the overall size of
34
+ * the delta. For this reason, wemerge adjacent fragmentsif the unchanged
35
+ *region between them is<= MATCH_THRESHOLD bytes .
36
36
*
37
37
* The worst case for delta sizes occurs when we did not find any unchanged
38
38
* region in the page. The size of the delta will be the size of the page plus
41
41
*/
42
42
#define FRAGMENT_HEADER_SIZE (2 * sizeof(OffsetNumber))
43
43
#define MATCH_THRESHOLD FRAGMENT_HEADER_SIZE
44
- #define MAX_DELTA_SIZE BLCKSZ + FRAGMENT_HEADER_SIZE
44
+ #define MAX_DELTA_SIZE ( BLCKSZ + FRAGMENT_HEADER_SIZE)
45
45
46
46
/* Struct of generic xlog data for single page */
47
47
typedef struct
48
48
{
49
49
Buffer buffer ;/* registered buffer */
50
- char image [BLCKSZ ];/* copy of page image for modification */
51
- char data [MAX_DELTA_SIZE ];/* delta between page images */
52
- int dataLen ;/* space consumed in data field */
53
50
bool fullImage ;/* are we taking a full image of this page? */
51
+ int deltaLen ;/* space consumed in delta field */
52
+ char image [BLCKSZ ];/* copy of page image for modification */
53
+ char delta [MAX_DELTA_SIZE ];/* delta between page images */
54
54
}PageData ;
55
55
56
56
/* State of generic xlog record construction */
@@ -61,22 +61,26 @@ struct GenericXLogState
61
61
};
62
62
63
63
static void writeFragment (PageData * pageData ,OffsetNumber offset ,
64
- OffsetNumber len ,Pointer data );
65
- static void writeDelta (PageData * pageData );
66
- static void applyPageRedo (Page page ,Pointer data ,Size dataSize );
64
+ OffsetNumber len ,const char * data );
65
+ static void computeDelta (PageData * pageData );
66
+ static void applyPageRedo (Page page ,const char * delta ,Size deltaSize );
67
+
67
68
68
69
/*
69
- * Write next fragment into delta.
70
+ * Write next fragment into pageData's delta.
71
+ *
72
+ * The fragment has the given offset and length, and data points to the
73
+ * actual data (of length length).
70
74
*/
71
75
static void
72
76
writeFragment (PageData * pageData ,OffsetNumber offset ,OffsetNumber length ,
73
- Pointer data )
77
+ const char * data )
74
78
{
75
- Pointer ptr = pageData -> data + pageData -> dataLen ;
79
+ char * ptr = pageData -> delta + pageData -> deltaLen ;
76
80
77
- /*Check if we have enough space */
78
- Assert (pageData -> dataLen + sizeof (offset )+
79
- sizeof (length )+ length <=sizeof (pageData -> data ));
81
+ /*Verify we have enough space */
82
+ Assert (pageData -> deltaLen + sizeof (offset )+
83
+ sizeof (length )+ length <=sizeof (pageData -> delta ));
80
84
81
85
/* Write fragment data */
82
86
memcpy (ptr ,& offset ,sizeof (offset ));
@@ -86,14 +90,14 @@ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
86
90
memcpy (ptr ,data ,length );
87
91
ptr += length ;
88
92
89
- pageData -> dataLen = ptr - pageData -> data ;
93
+ pageData -> deltaLen = ptr - pageData -> delta ;
90
94
}
91
95
92
96
/*
93
- *Make delta for given page.
97
+ *Compute the delta record for given page.
94
98
*/
95
99
static void
96
- writeDelta (PageData * pageData )
100
+ computeDelta (PageData * pageData )
97
101
{
98
102
Page page = BufferGetPage (pageData -> buffer ,NULL ,NULL ,
99
103
BGP_NO_SNAPSHOT_TEST ),
@@ -106,6 +110,8 @@ writeDelta(PageData *pageData)
106
110
imageLower = ((PageHeader )image )-> pd_lower ,
107
111
imageUpper = ((PageHeader )image )-> pd_upper ;
108
112
113
+ pageData -> deltaLen = 0 ;
114
+
109
115
for (i = 0 ;i < BLCKSZ ;i ++ )
110
116
{
111
117
bool match ;
@@ -181,22 +187,22 @@ writeDelta(PageData *pageData)
181
187
char tmp [BLCKSZ ];
182
188
183
189
memcpy (tmp ,image ,BLCKSZ );
184
- applyPageRedo (tmp ,pageData -> data ,pageData -> dataLen );
185
- if (memcmp (tmp ,page ,pageLower )
186
- || memcmp (tmp + pageUpper ,page + pageUpper ,BLCKSZ - pageUpper ))
190
+ applyPageRedo (tmp ,pageData -> delta ,pageData -> deltaLen );
191
+ if (memcmp (tmp ,page ,pageLower )!= 0 ||
192
+ memcmp (tmp + pageUpper ,page + pageUpper ,BLCKSZ - pageUpper )!= 0 )
187
193
elog (ERROR ,"result of generic xlog apply does not match" );
188
194
}
189
195
#endif
190
196
}
191
197
192
198
/*
193
- * Start new generic xlog record.
199
+ * Start new generic xlog record for modifications to specified relation .
194
200
*/
195
201
GenericXLogState *
196
202
GenericXLogStart (Relation relation )
197
203
{
198
- int i ;
199
204
GenericXLogState * state ;
205
+ int i ;
200
206
201
207
state = (GenericXLogState * )palloc (sizeof (GenericXLogState ));
202
208
@@ -209,24 +215,30 @@ GenericXLogStart(Relation relation)
209
215
210
216
/*
211
217
* Register new buffer for generic xlog record.
218
+ *
219
+ * Returns pointer to the page's image in the GenericXLogState, which
220
+ * is what the caller should modify.
221
+ *
222
+ * If the buffer is already registered, just return its existing entry.
212
223
*/
213
224
Page
214
225
GenericXLogRegister (GenericXLogState * state ,Buffer buffer ,bool isNew )
215
226
{
216
227
int block_id ;
217
228
218
- /*Place new buffer to unused slot in array */
229
+ /*Search array for existing entry or first unused slot */
219
230
for (block_id = 0 ;block_id < MAX_GENERIC_XLOG_PAGES ;block_id ++ )
220
231
{
221
232
PageData * page = & state -> pages [block_id ];
222
233
223
234
if (BufferIsInvalid (page -> buffer ))
224
235
{
236
+ /* Empty slot, so use it (there cannot be a match later) */
225
237
page -> buffer = buffer ;
226
- memcpy (page -> image ,BufferGetPage (buffer ,NULL ,NULL ,
227
- BGP_NO_SNAPSHOT_TEST ),BLCKSZ );
228
- page -> dataLen = 0 ;
229
238
page -> fullImage = isNew ;
239
+ memcpy (page -> image ,
240
+ BufferGetPage (buffer ,NULL ,NULL ,BGP_NO_SNAPSHOT_TEST ),
241
+ BLCKSZ );
230
242
return (Page )page -> image ;
231
243
}
232
244
else if (page -> buffer == buffer )
@@ -239,15 +251,16 @@ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
239
251
}
240
252
}
241
253
242
- elog (ERROR ,"maximum numberof %d generic xlog buffers is exceeded" ,
254
+ elog (ERROR ,"maximum number%d of generic xlog buffers is exceeded" ,
243
255
MAX_GENERIC_XLOG_PAGES );
244
-
245
256
/* keep compiler quiet */
246
257
return NULL ;
247
258
}
248
259
249
260
/*
250
261
* Unregister particular buffer for generic xlog record.
262
+ *
263
+ * XXX this is dangerous and should go away.
251
264
*/
252
265
void
253
266
GenericXLogUnregister (GenericXLogState * state ,Buffer buffer )
@@ -274,7 +287,8 @@ GenericXLogUnregister(GenericXLogState *state, Buffer buffer)
274
287
}
275
288
276
289
/*
277
- * Put all changes in registered buffers to generic xlog record.
290
+ * Apply changes represented by GenericXLogState to the actual buffers,
291
+ * and emit a generic xlog record.
278
292
*/
279
293
XLogRecPtr
280
294
GenericXLogFinish (GenericXLogState * state )
@@ -291,33 +305,35 @@ GenericXLogFinish(GenericXLogState *state)
291
305
292
306
for (i = 0 ;i < MAX_GENERIC_XLOG_PAGES ;i ++ )
293
307
{
308
+ PageData * pageData = & state -> pages [i ];
309
+ Page page ;
294
310
char tmp [BLCKSZ ];
295
- PageData * page = & state -> pages [i ];
296
311
297
- if (BufferIsInvalid (page -> buffer ))
312
+ if (BufferIsInvalid (pageData -> buffer ))
298
313
continue ;
299
314
315
+ page = BufferGetPage (pageData -> buffer ,NULL ,NULL ,
316
+ BGP_NO_SNAPSHOT_TEST );
317
+
300
318
/* Swap current and saved page image. */
301
- memcpy (tmp ,page -> image ,BLCKSZ );
302
- memcpy (page -> image ,BufferGetPage (page -> buffer ,NULL ,NULL ,
303
- BGP_NO_SNAPSHOT_TEST ),BLCKSZ );
304
- memcpy (BufferGetPage (page -> buffer ,NULL ,NULL ,
305
- BGP_NO_SNAPSHOT_TEST ),tmp ,BLCKSZ );
319
+ memcpy (tmp ,pageData -> image ,BLCKSZ );
320
+ memcpy (pageData -> image ,page ,BLCKSZ );
321
+ memcpy (page ,tmp ,BLCKSZ );
306
322
307
- if (page -> fullImage )
323
+ if (pageData -> fullImage )
308
324
{
309
325
/* A full page image does not require anything special */
310
- XLogRegisterBuffer (i ,page -> buffer ,REGBUF_FORCE_IMAGE );
326
+ XLogRegisterBuffer (i ,pageData -> buffer ,REGBUF_FORCE_IMAGE );
311
327
}
312
328
else
313
329
{
314
330
/*
315
- * In normal mode, calculate delta and write it as data
331
+ * In normal mode, calculate delta and write it asxlog data
316
332
* associated with this page.
317
333
*/
318
- XLogRegisterBuffer (i ,page -> buffer ,REGBUF_STANDARD );
319
- writeDelta ( page );
320
- XLogRegisterBufData (i ,page -> data , page -> dataLen );
334
+ XLogRegisterBuffer (i ,pageData -> buffer ,REGBUF_STANDARD );
335
+ computeDelta ( pageData );
336
+ XLogRegisterBufData (i ,pageData -> delta , pageData -> deltaLen );
321
337
}
322
338
}
323
339
@@ -327,13 +343,13 @@ GenericXLogFinish(GenericXLogState *state)
327
343
/* Set LSN and mark buffers dirty */
328
344
for (i = 0 ;i < MAX_GENERIC_XLOG_PAGES ;i ++ )
329
345
{
330
- PageData * page = & state -> pages [i ];
346
+ PageData * pageData = & state -> pages [i ];
331
347
332
- if (BufferIsInvalid (page -> buffer ))
348
+ if (BufferIsInvalid (pageData -> buffer ))
333
349
continue ;
334
- PageSetLSN (BufferGetPage (page -> buffer ,NULL ,NULL ,
350
+ PageSetLSN (BufferGetPage (pageData -> buffer ,NULL ,NULL ,
335
351
BGP_NO_SNAPSHOT_TEST ),lsn );
336
- MarkBufferDirty (page -> buffer );
352
+ MarkBufferDirty (pageData -> buffer );
337
353
}
338
354
END_CRIT_SECTION ();
339
355
}
@@ -343,13 +359,15 @@ GenericXLogFinish(GenericXLogState *state)
343
359
START_CRIT_SECTION ();
344
360
for (i = 0 ;i < MAX_GENERIC_XLOG_PAGES ;i ++ )
345
361
{
346
- PageData * page = & state -> pages [i ];
362
+ PageData * pageData = & state -> pages [i ];
347
363
348
- if (BufferIsInvalid (page -> buffer ))
364
+ if (BufferIsInvalid (pageData -> buffer ))
349
365
continue ;
350
- memcpy (BufferGetPage (page -> buffer ,NULL ,NULL ,
351
- BGP_NO_SNAPSHOT_TEST ),page -> image ,BLCKSZ );
352
- MarkBufferDirty (page -> buffer );
366
+ memcpy (BufferGetPage (pageData -> buffer ,NULL ,NULL ,
367
+ BGP_NO_SNAPSHOT_TEST ),
368
+ pageData -> image ,
369
+ BLCKSZ );
370
+ MarkBufferDirty (pageData -> buffer );
353
371
}
354
372
END_CRIT_SECTION ();
355
373
}
@@ -360,7 +378,9 @@ GenericXLogFinish(GenericXLogState *state)
360
378
}
361
379
362
380
/*
363
- * Abort generic xlog record.
381
+ * Abort generic xlog record construction. No changes are applied to buffers.
382
+ *
383
+ * Note: caller is responsible for releasing locks/pins on buffers, if needed.
364
384
*/
365
385
void
366
386
GenericXLogAbort (GenericXLogState * state )
@@ -372,10 +392,10 @@ GenericXLogAbort(GenericXLogState *state)
372
392
* Apply delta to given page image.
373
393
*/
374
394
static void
375
- applyPageRedo (Page page ,Pointer data ,Size dataSize )
395
+ applyPageRedo (Page page ,const char * delta ,Size deltaSize )
376
396
{
377
- Pointer ptr = data ,
378
- end = data + dataSize ;
397
+ const char * ptr = delta ;
398
+ const char * end = delta + deltaSize ;
379
399
380
400
while (ptr < end )
381
401
{
@@ -399,10 +419,11 @@ applyPageRedo(Page page, Pointer data, Size dataSize)
399
419
void
400
420
generic_redo (XLogReaderState * record )
401
421
{
402
- uint8 block_id ;
403
- Buffer buffers [MAX_GENERIC_XLOG_PAGES ]= {InvalidBuffer };
404
422
XLogRecPtr lsn = record -> EndRecPtr ;
423
+ Buffer buffers [MAX_GENERIC_XLOG_PAGES ];
424
+ uint8 block_id ;
405
425
426
+ /* Protect limited size of buffers[] array */
406
427
Assert (record -> max_block_id < MAX_GENERIC_XLOG_PAGES );
407
428
408
429
/* Iterate over blocks */
@@ -411,20 +432,24 @@ generic_redo(XLogReaderState *record)
411
432
XLogRedoAction action ;
412
433
413
434
if (!XLogRecHasBlockRef (record ,block_id ))
435
+ {
436
+ buffers [block_id ]= InvalidBuffer ;
414
437
continue ;
438
+ }
415
439
416
440
action = XLogReadBufferForRedo (record ,block_id ,& buffers [block_id ]);
417
441
418
442
/* Apply redo to given block if needed */
419
443
if (action == BLK_NEEDS_REDO )
420
444
{
421
- Pointer blockData ;
422
- Size blockDataSize ;
423
445
Page page ;
446
+ char * blockDelta ;
447
+ Size blockDeltaSize ;
424
448
425
- page = BufferGetPage (buffers [block_id ],NULL ,NULL ,BGP_NO_SNAPSHOT_TEST );
426
- blockData = XLogRecGetBlockData (record ,block_id ,& blockDataSize );
427
- applyPageRedo (page ,blockData ,blockDataSize );
449
+ page = BufferGetPage (buffers [block_id ],NULL ,NULL ,
450
+ BGP_NO_SNAPSHOT_TEST );
451
+ blockDelta = XLogRecGetBlockData (record ,block_id ,& blockDeltaSize );
452
+ applyPageRedo (page ,blockDelta ,blockDeltaSize );
428
453
429
454
PageSetLSN (page ,lsn );
430
455
MarkBufferDirty (buffers [block_id ]);