@@ -2121,6 +2121,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
21212121 int ndone ;
21222122 PGAlignedBlock scratch ;
21232123 Page page ;
2124+ Buffer vmbuffer = InvalidBuffer ;
21242125 bool needwal ;
21252126 Size saveFreeSpace ;
21262127 bool need_tuple_data = RelationIsLogicallyLogged (relation );
@@ -2175,21 +2176,30 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
21752176 while (ndone < ntuples )
21762177 {
21772178 Buffer buffer ;
2178- Buffer vmbuffer = InvalidBuffer ;
2179+ bool starting_with_empty_page ;
21792180 bool all_visible_cleared = false;
2181+ bool all_frozen_set = false;
21802182 int nthispage ;
21812183
21822184 CHECK_FOR_INTERRUPTS ();
21832185
21842186 /*
21852187 * Find buffer where at least the next tuple will fit. If the page is
21862188 * 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.
21872192 */
21882193 buffer = RelationGetBufferForTuple (relation , heaptuples [ndone ]-> t_len ,
21892194 InvalidBuffer , options , bistate ,
21902195 & vmbuffer , NULL );
21912196 page = BufferGetPage (buffer );
21922197
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+
21932203 /* NO EREPORT(ERROR) from here till changes are logged */
21942204 START_CRIT_SECTION ();
21952205
@@ -2223,14 +2233,23 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
22232233 log_heap_new_cid (relation , heaptup );
22242234 }
22252235
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 ))
22272244 {
22282245 all_visible_cleared = true;
22292246 PageClearAllVisible (page );
22302247 visibilitymap_clear (relation ,
22312248 BufferGetBlockNumber (buffer ),
22322249 vmbuffer , VISIBILITYMAP_VALID_BITS );
22332250 }
2251+ else if (all_frozen_set )
2252+ PageSetAllVisible (page );
22342253
22352254 /*
22362255 * XXX Should we set PageSetPrunable on this page ? See heap_insert()
@@ -2254,8 +2273,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
22542273 * If the page was previously empty, we can reinit the page
22552274 * instead of restoring the whole thing.
22562275 */
2257- init = (ItemPointerGetOffsetNumber (& (heaptuples [ndone ]-> t_self )) == FirstOffsetNumber &&
2258- PageGetMaxOffsetNumber (page ) == FirstOffsetNumber + nthispage - 1 );
2276+ init = starting_with_empty_page ;
22592277
22602278 /* allocate xl_heap_multi_insert struct from the scratch area */
22612279 xlrec = (xl_heap_multi_insert * ) scratchptr ;
@@ -2273,7 +2291,15 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
22732291 /* the rest of the scratch space is used for tuple data */
22742292 tupledata = scratchptr ;
22752293
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+
22772303 xlrec -> ntuples = nthispage ;
22782304
22792305 /*
@@ -2347,13 +2373,40 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
23472373
23482374 END_CRIT_SECTION ();
23492375
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 ));
23532384
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 );
23542397 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+ */
23552404 }
23562405
2406+ /* We're done with inserting all tuples, so release the last vmbuffer. */
2407+ if (vmbuffer != InvalidBuffer )
2408+ ReleaseBuffer (vmbuffer );
2409+
23572410 /*
23582411 * We're done with the actual inserts. Check for conflicts again, to
23592412 * ensure that all rw-conflicts in to these inserts are detected. Without
@@ -8725,6 +8778,10 @@ heap_xlog_insert(XLogReaderState *record)
87258778 if (xlrec -> flags & XLH_INSERT_ALL_VISIBLE_CLEARED )
87268779 PageClearAllVisible (page );
87278780
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+
87288785 MarkBufferDirty (buffer );
87298786 }
87308787 if (BufferIsValid (buffer ))
@@ -8775,6 +8832,10 @@ heap_xlog_multi_insert(XLogReaderState *record)
87758832
87768833 XLogRecGetBlockTag (record , 0 , & rnode , NULL , & blkno );
87778834
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+
87788839 /*
87798840 * The visibility map may need to be fixed even if the heap page is
87808841 * already up-to-date.
0 commit comments