@@ -1880,8 +1880,12 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
18801880TransactionId xid = GetCurrentTransactionId ();
18811881HeapTuple heaptup ;
18821882Buffer buffer ;
1883+ Page page = NULL ;
18831884Buffer vmbuffer = InvalidBuffer ;
1885+ bool starting_with_empty_page ;
18841886bool all_visible_cleared = false;
1887+ bool all_frozen_set = false;
1888+ uint8 vmstatus = 0 ;
18851889
18861890/*
18871891 * Fill in tuple header fields and toast the tuple if necessary.
@@ -1894,11 +1898,36 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
18941898/*
18951899 * Find buffer to insert this tuple into. If the page is all visible,
18961900 * this will also pin the requisite visibility map page.
1901+ *
1902+ * Also pin visibility map page if COPY FREEZE inserts tuples into an
1903+ * empty page. See all_frozen_set below.
18971904 */
18981905buffer = RelationGetBufferForTuple (relation ,heaptup -> t_len ,
18991906InvalidBuffer ,options ,bistate ,
19001907& vmbuffer ,NULL );
19011908
1909+
1910+ /*
1911+ * If we're inserting frozen entry into an empty page,
1912+ * set visibility map bits and PageAllVisible() hint.
1913+ *
1914+ * If we're inserting frozen entry into already all_frozen page,
1915+ * preserve this state.
1916+ */
1917+ if (options & HEAP_INSERT_FROZEN )
1918+ {
1919+ page = BufferGetPage (buffer );
1920+
1921+ starting_with_empty_page = PageGetMaxOffsetNumber (page )== 0 ;
1922+
1923+ if (visibilitymap_pin_ok (BufferGetBlockNumber (buffer ),vmbuffer ))
1924+ vmstatus = visibilitymap_get_status (relation ,
1925+ BufferGetBlockNumber (buffer ),& vmbuffer );
1926+
1927+ if ((starting_with_empty_page || vmstatus & VISIBILITYMAP_ALL_FROZEN ))
1928+ all_frozen_set = true;
1929+ }
1930+
19021931/*
19031932 * We're about to do the actual insert -- but check for conflict first, to
19041933 * avoid possibly having to roll back work we've just done.
@@ -1922,14 +1951,28 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
19221951RelationPutHeapTuple (relation ,buffer ,heaptup ,
19231952 (options & HEAP_INSERT_SPECULATIVE )!= 0 );
19241953
1925- if (PageIsAllVisible (BufferGetPage (buffer )))
1954+ /*
1955+ * If the page is all visible, need to clear that, unless we're only
1956+ * going to add further frozen rows to it.
1957+ *
1958+ * If we're only adding already frozen rows to a page that was empty or
1959+ * marked as all visible, mark it as all-visible.
1960+ */
1961+ if (PageIsAllVisible (BufferGetPage (buffer ))&& !(options & HEAP_INSERT_FROZEN ))
19261962{
19271963all_visible_cleared = true;
19281964PageClearAllVisible (BufferGetPage (buffer ));
19291965visibilitymap_clear (relation ,
19301966ItemPointerGetBlockNumber (& (heaptup -> t_self )),
19311967vmbuffer ,VISIBILITYMAP_VALID_BITS );
19321968}
1969+ else if (all_frozen_set )
1970+ {
1971+ /* We only ever set all_frozen_set after reading the page. */
1972+ Assert (page );
1973+
1974+ PageSetAllVisible (page );
1975+ }
19331976
19341977/*
19351978 * XXX Should we set PageSetPrunable on this page ?
@@ -1977,6 +2020,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
19772020xlrec .flags = 0 ;
19782021if (all_visible_cleared )
19792022xlrec .flags |=XLH_INSERT_ALL_VISIBLE_CLEARED ;
2023+ if (all_frozen_set )
2024+ xlrec .flags = XLH_INSERT_ALL_FROZEN_SET ;
19802025if (options & HEAP_INSERT_SPECULATIVE )
19812026xlrec .flags |=XLH_INSERT_IS_SPECULATIVE ;
19822027Assert (ItemPointerGetBlockNumber (& heaptup -> t_self )== BufferGetBlockNumber (buffer ));
@@ -2025,6 +2070,29 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
20252070
20262071END_CRIT_SECTION ();
20272072
2073+ /*
2074+ * If we've frozen everything on the page, update the visibilitymap.
2075+ * We're already holding pin on the vmbuffer.
2076+ *
2077+ * No need to update the visibilitymap if it had all_frozen bit set
2078+ * before this insertion.
2079+ */
2080+ if (all_frozen_set && ((vmstatus & VISIBILITYMAP_ALL_FROZEN )== 0 ))
2081+ {
2082+ Assert (PageIsAllVisible (page ));
2083+ Assert (visibilitymap_pin_ok (BufferGetBlockNumber (buffer ),vmbuffer ));
2084+
2085+ /*
2086+ * It's fine to use InvalidTransactionId here - this is only used
2087+ * when HEAP_INSERT_FROZEN is specified, which intentionally
2088+ * violates visibility rules.
2089+ */
2090+ visibilitymap_set (relation ,BufferGetBlockNumber (buffer ),buffer ,
2091+ InvalidXLogRecPtr ,vmbuffer ,
2092+ InvalidTransactionId ,
2093+ VISIBILITYMAP_ALL_VISIBLE |VISIBILITYMAP_ALL_FROZEN );
2094+ }
2095+
20282096UnlockReleaseBuffer (buffer );
20292097if (vmbuffer != InvalidBuffer )
20302098ReleaseBuffer (vmbuffer );
@@ -8708,6 +8776,10 @@ heap_xlog_insert(XLogReaderState *record)
87088776ItemPointerSetBlockNumber (& target_tid ,blkno );
87098777ItemPointerSetOffsetNumber (& target_tid ,xlrec -> offnum );
87108778
8779+ /* check that the mutually exclusive flags are not both set */
8780+ Assert (!((xlrec -> flags & XLH_INSERT_ALL_VISIBLE_CLEARED )&&
8781+ (xlrec -> flags & XLH_INSERT_ALL_FROZEN_SET )));
8782+
87118783/*
87128784 * The visibility map may need to be fixed even if the heap page is
87138785 * already up-to-date.
@@ -8925,6 +8997,10 @@ heap_xlog_multi_insert(XLogReaderState *record)
89258997if (xlrec -> flags & XLH_INSERT_ALL_VISIBLE_CLEARED )
89268998PageClearAllVisible (page );
89278999
9000+ /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
9001+ if (xlrec -> flags & XLH_INSERT_ALL_FROZEN_SET )
9002+ PageSetAllVisible (page );
9003+
89289004MarkBufferDirty (buffer );
89299005}
89309006if (BufferIsValid (buffer ))