@@ -1880,8 +1880,12 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
1880
1880
TransactionId xid = GetCurrentTransactionId ();
1881
1881
HeapTuple heaptup ;
1882
1882
Buffer buffer ;
1883
+ Page page = NULL ;
1883
1884
Buffer vmbuffer = InvalidBuffer ;
1885
+ bool starting_with_empty_page ;
1884
1886
bool all_visible_cleared = false;
1887
+ bool all_frozen_set = false;
1888
+ uint8 vmstatus = 0 ;
1885
1889
1886
1890
/*
1887
1891
* Fill in tuple header fields and toast the tuple if necessary.
@@ -1894,11 +1898,36 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
1894
1898
/*
1895
1899
* Find buffer to insert this tuple into. If the page is all visible,
1896
1900
* 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.
1897
1904
*/
1898
1905
buffer = RelationGetBufferForTuple (relation , heaptup -> t_len ,
1899
1906
InvalidBuffer , options , bistate ,
1900
1907
& vmbuffer , NULL );
1901
1908
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
+
1902
1931
/*
1903
1932
* We're about to do the actual insert -- but check for conflict first, to
1904
1933
* avoid possibly having to roll back work we've just done.
@@ -1922,14 +1951,28 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
1922
1951
RelationPutHeapTuple (relation , buffer , heaptup ,
1923
1952
(options & HEAP_INSERT_SPECULATIVE ) != 0 );
1924
1953
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 ))
1926
1962
{
1927
1963
all_visible_cleared = true;
1928
1964
PageClearAllVisible (BufferGetPage (buffer ));
1929
1965
visibilitymap_clear (relation ,
1930
1966
ItemPointerGetBlockNumber (& (heaptup -> t_self )),
1931
1967
vmbuffer , VISIBILITYMAP_VALID_BITS );
1932
1968
}
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
+ }
1933
1976
1934
1977
/*
1935
1978
* XXX Should we set PageSetPrunable on this page ?
@@ -1977,6 +2020,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
1977
2020
xlrec .flags = 0 ;
1978
2021
if (all_visible_cleared )
1979
2022
xlrec .flags |= XLH_INSERT_ALL_VISIBLE_CLEARED ;
2023
+ if (all_frozen_set )
2024
+ xlrec .flags = XLH_INSERT_ALL_FROZEN_SET ;
1980
2025
if (options & HEAP_INSERT_SPECULATIVE )
1981
2026
xlrec .flags |= XLH_INSERT_IS_SPECULATIVE ;
1982
2027
Assert (ItemPointerGetBlockNumber (& heaptup -> t_self ) == BufferGetBlockNumber (buffer ));
@@ -2025,6 +2070,29 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
2025
2070
2026
2071
END_CRIT_SECTION ();
2027
2072
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
+
2028
2096
UnlockReleaseBuffer (buffer );
2029
2097
if (vmbuffer != InvalidBuffer )
2030
2098
ReleaseBuffer (vmbuffer );
@@ -8708,6 +8776,10 @@ heap_xlog_insert(XLogReaderState *record)
8708
8776
ItemPointerSetBlockNumber (& target_tid , blkno );
8709
8777
ItemPointerSetOffsetNumber (& target_tid , xlrec -> offnum );
8710
8778
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
+
8711
8783
/*
8712
8784
* The visibility map may need to be fixed even if the heap page is
8713
8785
* already up-to-date.
@@ -8925,6 +8997,10 @@ heap_xlog_multi_insert(XLogReaderState *record)
8925
8997
if (xlrec -> flags & XLH_INSERT_ALL_VISIBLE_CLEARED )
8926
8998
PageClearAllVisible (page );
8927
8999
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
+
8928
9004
MarkBufferDirty (buffer );
8929
9005
}
8930
9006
if (BufferIsValid (buffer ))
0 commit comments