@@ -2121,6 +2121,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
2121
2121
int ndone ;
2122
2122
PGAlignedBlock scratch ;
2123
2123
Page page ;
2124
+ Buffer vmbuffer = InvalidBuffer ;
2124
2125
bool needwal ;
2125
2126
Size saveFreeSpace ;
2126
2127
bool need_tuple_data = RelationIsLogicallyLogged (relation );
@@ -2175,21 +2176,30 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
2175
2176
while (ndone < ntuples )
2176
2177
{
2177
2178
Buffer buffer ;
2178
- Buffer vmbuffer = InvalidBuffer ;
2179
+ bool starting_with_empty_page ;
2179
2180
bool all_visible_cleared = false;
2181
+ bool all_frozen_set = false;
2180
2182
int nthispage ;
2181
2183
2182
2184
CHECK_FOR_INTERRUPTS ();
2183
2185
2184
2186
/*
2185
2187
* Find buffer where at least the next tuple will fit. If the page is
2186
2188
* all-visible, this will also pin the requisite visibility map page.
2189
+ *
2190
+ * Also pin visibility map page if COPY FREEZE inserts tuples into an
2191
+ * empty page. See all_frozen_set below.
2187
2192
*/
2188
2193
buffer = RelationGetBufferForTuple (relation , heaptuples [ndone ]-> t_len ,
2189
2194
InvalidBuffer , options , bistate ,
2190
2195
& vmbuffer , NULL );
2191
2196
page = BufferGetPage (buffer );
2192
2197
2198
+ starting_with_empty_page = PageGetMaxOffsetNumber (page ) == 0 ;
2199
+
2200
+ if (starting_with_empty_page && (options & HEAP_INSERT_FROZEN ))
2201
+ all_frozen_set = true;
2202
+
2193
2203
/* NO EREPORT(ERROR) from here till changes are logged */
2194
2204
START_CRIT_SECTION ();
2195
2205
@@ -2223,14 +2233,23 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
2223
2233
log_heap_new_cid (relation , heaptup );
2224
2234
}
2225
2235
2226
- if (PageIsAllVisible (page ))
2236
+ /*
2237
+ * If the page is all visible, need to clear that, unless we're only
2238
+ * going to add further frozen rows to it.
2239
+ *
2240
+ * If we're only adding already frozen rows to a previously empty
2241
+ * page, mark it as all-visible.
2242
+ */
2243
+ if (PageIsAllVisible (page ) && !(options & HEAP_INSERT_FROZEN ))
2227
2244
{
2228
2245
all_visible_cleared = true;
2229
2246
PageClearAllVisible (page );
2230
2247
visibilitymap_clear (relation ,
2231
2248
BufferGetBlockNumber (buffer ),
2232
2249
vmbuffer , VISIBILITYMAP_VALID_BITS );
2233
2250
}
2251
+ else if (all_frozen_set )
2252
+ PageSetAllVisible (page );
2234
2253
2235
2254
/*
2236
2255
* XXX Should we set PageSetPrunable on this page ? See heap_insert()
@@ -2254,8 +2273,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
2254
2273
* If the page was previously empty, we can reinit the page
2255
2274
* instead of restoring the whole thing.
2256
2275
*/
2257
- init = (ItemPointerGetOffsetNumber (& (heaptuples [ndone ]-> t_self )) == FirstOffsetNumber &&
2258
- PageGetMaxOffsetNumber (page ) == FirstOffsetNumber + nthispage - 1 );
2276
+ init = starting_with_empty_page ;
2259
2277
2260
2278
/* allocate xl_heap_multi_insert struct from the scratch area */
2261
2279
xlrec = (xl_heap_multi_insert * ) scratchptr ;
@@ -2273,7 +2291,15 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
2273
2291
/* the rest of the scratch space is used for tuple data */
2274
2292
tupledata = scratchptr ;
2275
2293
2276
- xlrec -> flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0 ;
2294
+ /* check that the mutually exclusive flags are not both set */
2295
+ Assert (!(all_visible_cleared && all_frozen_set ));
2296
+
2297
+ xlrec -> flags = 0 ;
2298
+ if (all_visible_cleared )
2299
+ xlrec -> flags = XLH_INSERT_ALL_VISIBLE_CLEARED ;
2300
+ if (all_frozen_set )
2301
+ xlrec -> flags = XLH_INSERT_ALL_FROZEN_SET ;
2302
+
2277
2303
xlrec -> ntuples = nthispage ;
2278
2304
2279
2305
/*
@@ -2347,13 +2373,40 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
2347
2373
2348
2374
END_CRIT_SECTION ();
2349
2375
2350
- UnlockReleaseBuffer (buffer );
2351
- if (vmbuffer != InvalidBuffer )
2352
- ReleaseBuffer (vmbuffer );
2376
+ /*
2377
+ * If we've frozen everything on the page, update the visibilitymap.
2378
+ * We're already holding pin on the vmbuffer.
2379
+ */
2380
+ if (all_frozen_set )
2381
+ {
2382
+ Assert (PageIsAllVisible (page ));
2383
+ Assert (visibilitymap_pin_ok (BufferGetBlockNumber (buffer ), vmbuffer ));
2353
2384
2385
+ /*
2386
+ * It's fine to use InvalidTransactionId here - this is only used
2387
+ * when HEAP_INSERT_FROZEN is specified, which intentionally
2388
+ * violates visibility rules.
2389
+ */
2390
+ visibilitymap_set (relation , BufferGetBlockNumber (buffer ), buffer ,
2391
+ InvalidXLogRecPtr , vmbuffer ,
2392
+ InvalidTransactionId ,
2393
+ VISIBILITYMAP_ALL_VISIBLE | VISIBILITYMAP_ALL_FROZEN );
2394
+ }
2395
+
2396
+ UnlockReleaseBuffer (buffer );
2354
2397
ndone += nthispage ;
2398
+
2399
+ /*
2400
+ * NB: Only release vmbuffer after inserting all tuples - it's fairly
2401
+ * likely that we'll insert into subsequent heap pages that are likely
2402
+ * to use the same vm page.
2403
+ */
2355
2404
}
2356
2405
2406
+ /* We're done with inserting all tuples, so release the last vmbuffer. */
2407
+ if (vmbuffer != InvalidBuffer )
2408
+ ReleaseBuffer (vmbuffer );
2409
+
2357
2410
/*
2358
2411
* We're done with the actual inserts. Check for conflicts again, to
2359
2412
* ensure that all rw-conflicts in to these inserts are detected. Without
@@ -8725,6 +8778,10 @@ heap_xlog_insert(XLogReaderState *record)
8725
8778
if (xlrec -> flags & XLH_INSERT_ALL_VISIBLE_CLEARED )
8726
8779
PageClearAllVisible (page );
8727
8780
8781
+ /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
8782
+ if (xlrec -> flags & XLH_INSERT_ALL_FROZEN_SET )
8783
+ PageSetAllVisible (page );
8784
+
8728
8785
MarkBufferDirty (buffer );
8729
8786
}
8730
8787
if (BufferIsValid (buffer ))
@@ -8775,6 +8832,10 @@ heap_xlog_multi_insert(XLogReaderState *record)
8775
8832
8776
8833
XLogRecGetBlockTag (record , 0 , & rnode , NULL , & blkno );
8777
8834
8835
+ /* check that the mutually exclusive flags are not both set */
8836
+ Assert (!((xlrec -> flags & XLH_INSERT_ALL_VISIBLE_CLEARED ) &&
8837
+ (xlrec -> flags & XLH_INSERT_ALL_FROZEN_SET )));
8838
+
8778
8839
/*
8779
8840
* The visibility map may need to be fixed even if the heap page is
8780
8841
* already up-to-date.
0 commit comments