@@ -921,6 +921,173 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
921
921
pfree (isnull );
922
922
}
923
923
924
+ static bool
925
+ heapam_scan_analyze_next_block (TableScanDesc sscan , BlockNumber blockno ,
926
+ BufferAccessStrategy bstrategy )
927
+ {
928
+ HeapScanDesc scan = (HeapScanDesc ) sscan ;
929
+
930
+ /*
931
+ * We must maintain a pin on the target page's buffer to ensure that
932
+ * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
933
+ * under us. Hence, pin the page until we are done looking at it. We
934
+ * also choose to hold sharelock on the buffer throughout --- we could
935
+ * release and re-acquire sharelock for each tuple, but since we aren't
936
+ * doing much work per tuple, the extra lock traffic is probably better
937
+ * avoided.
938
+ */
939
+ scan -> rs_cblock = blockno ;
940
+ scan -> rs_cindex = FirstOffsetNumber ;
941
+ scan -> rs_cbuf = ReadBufferExtended (scan -> rs_base .rs_rd , MAIN_FORKNUM ,
942
+ blockno , RBM_NORMAL , bstrategy );
943
+ LockBuffer (scan -> rs_cbuf , BUFFER_LOCK_SHARE );
944
+
945
+ /* in heap all blocks can contain tuples, so always return true */
946
+ return true;
947
+ }
948
+
949
+ static bool
950
+ heapam_scan_analyze_next_tuple (TableScanDesc sscan , TransactionId OldestXmin ,
951
+ double * liverows , double * deadrows ,
952
+ TupleTableSlot * slot )
953
+ {
954
+ HeapScanDesc scan = (HeapScanDesc ) sscan ;
955
+ Page targpage ;
956
+ OffsetNumber maxoffset ;
957
+ BufferHeapTupleTableSlot * hslot ;
958
+
959
+ Assert (TTS_IS_BUFFERTUPLE (slot ));
960
+
961
+ hslot = (BufferHeapTupleTableSlot * ) slot ;
962
+ targpage = BufferGetPage (scan -> rs_cbuf );
963
+ maxoffset = PageGetMaxOffsetNumber (targpage );
964
+
965
+ /* Inner loop over all tuples on the selected page */
966
+ for (; scan -> rs_cindex <= maxoffset ; scan -> rs_cindex ++ )
967
+ {
968
+ ItemId itemid ;
969
+ HeapTuple targtuple = & hslot -> base .tupdata ;
970
+ bool sample_it = false;
971
+
972
+ itemid = PageGetItemId (targpage , scan -> rs_cindex );
973
+
974
+ /*
975
+ * We ignore unused and redirect line pointers. DEAD line pointers
976
+ * should be counted as dead, because we need vacuum to run to get rid
977
+ * of them. Note that this rule agrees with the way that
978
+ * heap_page_prune() counts things.
979
+ */
980
+ if (!ItemIdIsNormal (itemid ))
981
+ {
982
+ if (ItemIdIsDead (itemid ))
983
+ * deadrows += 1 ;
984
+ continue ;
985
+ }
986
+
987
+ ItemPointerSet (& targtuple -> t_self , scan -> rs_cblock , scan -> rs_cindex );
988
+
989
+ targtuple -> t_tableOid = RelationGetRelid (scan -> rs_base .rs_rd );
990
+ targtuple -> t_data = (HeapTupleHeader ) PageGetItem (targpage , itemid );
991
+ targtuple -> t_len = ItemIdGetLength (itemid );
992
+
993
+ switch (HeapTupleSatisfiesVacuum (targtuple , OldestXmin , scan -> rs_cbuf ))
994
+ {
995
+ case HEAPTUPLE_LIVE :
996
+ sample_it = true;
997
+ * liverows += 1 ;
998
+ break ;
999
+
1000
+ case HEAPTUPLE_DEAD :
1001
+ case HEAPTUPLE_RECENTLY_DEAD :
1002
+ /* Count dead and recently-dead rows */
1003
+ * deadrows += 1 ;
1004
+ break ;
1005
+
1006
+ case HEAPTUPLE_INSERT_IN_PROGRESS :
1007
+
1008
+ /*
1009
+ * Insert-in-progress rows are not counted. We assume that
1010
+ * when the inserting transaction commits or aborts, it will
1011
+ * send a stats message to increment the proper count. This
1012
+ * works right only if that transaction ends after we finish
1013
+ * analyzing the table; if things happen in the other order,
1014
+ * its stats update will be overwritten by ours. However, the
1015
+ * error will be large only if the other transaction runs long
1016
+ * enough to insert many tuples, so assuming it will finish
1017
+ * after us is the safer option.
1018
+ *
1019
+ * A special case is that the inserting transaction might be
1020
+ * our own. In this case we should count and sample the row,
1021
+ * to accommodate users who load a table and analyze it in one
1022
+ * transaction. (pgstat_report_analyze has to adjust the
1023
+ * numbers we send to the stats collector to make this come
1024
+ * out right.)
1025
+ */
1026
+ if (TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetXmin (targtuple -> t_data )))
1027
+ {
1028
+ sample_it = true;
1029
+ * liverows += 1 ;
1030
+ }
1031
+ break ;
1032
+
1033
+ case HEAPTUPLE_DELETE_IN_PROGRESS :
1034
+
1035
+ /*
1036
+ * We count and sample delete-in-progress rows the same as
1037
+ * live ones, so that the stats counters come out right if the
1038
+ * deleting transaction commits after us, per the same
1039
+ * reasoning given above.
1040
+ *
1041
+ * If the delete was done by our own transaction, however, we
1042
+ * must count the row as dead to make pgstat_report_analyze's
1043
+ * stats adjustments come out right. (Note: this works out
1044
+ * properly when the row was both inserted and deleted in our
1045
+ * xact.)
1046
+ *
1047
+ * The net effect of these choices is that we act as though an
1048
+ * IN_PROGRESS transaction hasn't happened yet, except if it
1049
+ * is our own transaction, which we assume has happened.
1050
+ *
1051
+ * This approach ensures that we behave sanely if we see both
1052
+ * the pre-image and post-image rows for a row being updated
1053
+ * by a concurrent transaction: we will sample the pre-image
1054
+ * but not the post-image. We also get sane results if the
1055
+ * concurrent transaction never commits.
1056
+ */
1057
+ if (TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetUpdateXid (targtuple -> t_data )))
1058
+ deadrows += 1 ;
1059
+ else
1060
+ {
1061
+ sample_it = true;
1062
+ liverows += 1 ;
1063
+ }
1064
+ break ;
1065
+
1066
+ default :
1067
+ elog (ERROR , "unexpected HeapTupleSatisfiesVacuum result" );
1068
+ break ;
1069
+ }
1070
+
1071
+ if (sample_it )
1072
+ {
1073
+ ExecStoreBufferHeapTuple (targtuple , slot , scan -> rs_cbuf );
1074
+ scan -> rs_cindex ++ ;
1075
+
1076
+ /* note that we leave the buffer locked here! */
1077
+ return true;
1078
+ }
1079
+ }
1080
+
1081
+ /* Now release the lock and pin on the page */
1082
+ UnlockReleaseBuffer (scan -> rs_cbuf );
1083
+ scan -> rs_cbuf = InvalidBuffer ;
1084
+
1085
+ /* also prevent old slot contents from having pin on page */
1086
+ ExecClearTuple (slot );
1087
+
1088
+ return false;
1089
+ }
1090
+
924
1091
static double
925
1092
heapam_index_build_range_scan (Relation heapRelation ,
926
1093
Relation indexRelation ,
@@ -1743,6 +1910,9 @@ static const TableAmRoutine heapam_methods = {
1743
1910
.relation_nontransactional_truncate = heapam_relation_nontransactional_truncate ,
1744
1911
.relation_copy_data = heapam_relation_copy_data ,
1745
1912
.relation_copy_for_cluster = heapam_relation_copy_for_cluster ,
1913
+ .relation_vacuum = heap_vacuum_rel ,
1914
+ .scan_analyze_next_block = heapam_scan_analyze_next_block ,
1915
+ .scan_analyze_next_tuple = heapam_scan_analyze_next_tuple ,
1746
1916
.index_build_range_scan = heapam_index_build_range_scan ,
1747
1917
.index_validate_scan = heapam_index_validate_scan ,
1748
1918
};
0 commit comments