@@ -921,6 +921,173 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
921921 pfree (isnull );
922922}
923923
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+
9241091static double
9251092heapam_index_build_range_scan (Relation heapRelation ,
9261093 Relation indexRelation ,
@@ -1743,6 +1910,9 @@ static const TableAmRoutine heapam_methods = {
17431910 .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate ,
17441911 .relation_copy_data = heapam_relation_copy_data ,
17451912 .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 ,
17461916 .index_build_range_scan = heapam_index_build_range_scan ,
17471917 .index_validate_scan = heapam_index_validate_scan ,
17481918};
0 commit comments