88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.116 2008/03/26 21:10:37 alvherre Exp $
11+ * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.117 2008/04/03 16:27:25 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
1919#include "access/heapam.h"
2020#include "access/transam.h"
2121#include "access/tuptoaster.h"
22+ #include "access/xact.h"
2223#include "catalog/index.h"
2324#include "catalog/indexing.h"
2425#include "catalog/namespace.h"
3334#include "pgstat.h"
3435#include "postmaster/autovacuum.h"
3536#include "storage/proc.h"
37+ #include "storage/procarray.h"
3638#include "utils/acl.h"
3739#include "utils/datum.h"
3840#include "utils/lsyscache.h"
@@ -362,10 +364,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
362364 * zero-column table.
363365 */
364366 if (!vacstmt -> vacuum )
365- pgstat_report_analyze (RelationGetRelid (onerel ),
366- onerel -> rd_rel -> relisshared ,
367- 0 , 0 );
368-
367+ pgstat_report_analyze (onerel , 0 , 0 );
369368 goto cleanup ;
370369 }
371370
@@ -481,9 +480,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
481480 }
482481
483482 /* report results to the stats collector, too */
484- pgstat_report_analyze (RelationGetRelid (onerel ),
485- onerel -> rd_rel -> relisshared ,
486- totalrows , totaldeadrows );
483+ pgstat_report_analyze (onerel , totalrows , totaldeadrows );
487484 }
488485
489486 /* We skip to here if there were no analyzable columns */
@@ -856,18 +853,23 @@ static int
856853acquire_sample_rows (Relation onerel , HeapTuple * rows , int targrows ,
857854 double * totalrows , double * totaldeadrows )
858855{
859- int numrows = 0 ; /* # rows collected */
860- double liverows = 0 ; /* # rows seen */
856+ int numrows = 0 ; /* # rows now in reservoir */
857+ double samplerows = 0 ; /* total # rows collected */
858+ double liverows = 0 ; /* # live rows seen */
861859 double deadrows = 0 ; /* # dead rows seen */
862860 double rowstoskip = -1 ; /* -1 means not set yet */
863861 BlockNumber totalblocks ;
862+ TransactionId OldestXmin ;
864863 BlockSamplerData bs ;
865864 double rstate ;
866865
867866 Assert (targrows > 1 );
868867
869868 totalblocks = RelationGetNumberOfBlocks (onerel );
870869
870+ /* Need a cutoff xmin for HeapTupleSatisfiesVacuum */
871+ OldestXmin = GetOldestXmin (onerel -> rd_rel -> relisshared , true);
872+
871873 /* Prepare for sampling block numbers */
872874 BlockSampler_Init (& bs , totalblocks , targrows );
873875 /* Prepare for sampling rows */
@@ -888,28 +890,112 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
888890 * We must maintain a pin on the target page's buffer to ensure that
889891 * the maxoffset value stays good (else concurrent VACUUM might delete
890892 * tuples out from under us). Hence, pin the page until we are done
891- * looking at it. We don't maintain a lock on the page, so tuples
892- * could get added to it, but we ignore such tuples.
893+ * looking at it. We also choose to hold sharelock on the buffer
894+ * throughout --- we could release and re-acquire sharelock for
895+ * each tuple, but since we aren't doing much work per tuple, the
896+ * extra lock traffic is probably better avoided.
893897 */
894898 targbuffer = ReadBufferWithStrategy (onerel , targblock , vac_strategy );
895899 LockBuffer (targbuffer , BUFFER_LOCK_SHARE );
896900 targpage = BufferGetPage (targbuffer );
897901 maxoffset = PageGetMaxOffsetNumber (targpage );
898- LockBuffer (targbuffer , BUFFER_LOCK_UNLOCK );
899902
900903 /* Inner loop over all tuples on the selected page */
901904 for (targoffset = FirstOffsetNumber ; targoffset <= maxoffset ; targoffset ++ )
902905 {
906+ ItemId itemid ;
903907 HeapTupleData targtuple ;
908+ bool sample_it = false;
909+
910+ itemid = PageGetItemId (targpage , targoffset );
911+
912+ /*
913+ * We ignore unused and redirect line pointers. DEAD line
914+ * pointers should be counted as dead, because we need vacuum
915+ * to run to get rid of them. Note that this rule agrees with
916+ * the way that heap_page_prune() counts things.
917+ */
918+ if (!ItemIdIsNormal (itemid ))
919+ {
920+ if (ItemIdIsDead (itemid ))
921+ deadrows += 1 ;
922+ continue ;
923+ }
904924
905925 ItemPointerSet (& targtuple .t_self , targblock , targoffset );
906- /* We use heap_release_fetch to avoid useless bufmgr traffic */
907- if (heap_release_fetch (onerel , SnapshotNow ,
908- & targtuple , & targbuffer ,
909- true, NULL ))
926+
927+ targtuple .t_data = (HeapTupleHeader ) PageGetItem (targpage , itemid );
928+ targtuple .t_len = ItemIdGetLength (itemid );
929+
930+ switch (HeapTupleSatisfiesVacuum (targtuple .t_data ,
931+ OldestXmin ,
932+ targbuffer ))
933+ {
934+ case HEAPTUPLE_LIVE :
935+ sample_it = true;
936+ liverows += 1 ;
937+ break ;
938+
939+ case HEAPTUPLE_DEAD :
940+ case HEAPTUPLE_RECENTLY_DEAD :
941+ /* Count dead and recently-dead rows */
942+ deadrows += 1 ;
943+ break ;
944+
945+ case HEAPTUPLE_INSERT_IN_PROGRESS :
946+ /*
947+ * Insert-in-progress rows are not counted. We assume
948+ * that when the inserting transaction commits or aborts,
949+ * it will send a stats message to increment the proper
950+ * count. This works right only if that transaction ends
951+ * after we finish analyzing the table; if things happen
952+ * in the other order, its stats update will be
953+ * overwritten by ours. However, the error will be
954+ * large only if the other transaction runs long enough
955+ * to insert many tuples, so assuming it will finish
956+ * after us is the safer option.
957+ *
958+ * A special case is that the inserting transaction might
959+ * be our own. In this case we should count and sample
960+ * the row, to accommodate users who load a table and
961+ * analyze it in one transaction. (pgstat_report_analyze
962+ * has to adjust the numbers we send to the stats collector
963+ * to make this come out right.)
964+ */
965+ if (TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetXmin (targtuple .t_data )))
966+ {
967+ sample_it = true;
968+ liverows += 1 ;
969+ }
970+ break ;
971+
972+ case HEAPTUPLE_DELETE_IN_PROGRESS :
973+ /*
974+ * We count delete-in-progress rows as still live, using
975+ * the same reasoning given above; but we don't bother to
976+ * include them in the sample.
977+ *
978+ * If the delete was done by our own transaction, however,
979+ * we must count the row as dead to make
980+ * pgstat_report_analyze's stats adjustments come out
981+ * right. (Note: this works out properly when the row
982+ * was both inserted and deleted in our xact.)
983+ */
984+ if (TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetXmax (targtuple .t_data )))
985+ deadrows += 1 ;
986+ else
987+ liverows += 1 ;
988+ break ;
989+
990+ default :
991+ elog (ERROR , "unexpected HeapTupleSatisfiesVacuum result" );
992+ break ;
993+ }
994+
995+ if (sample_it )
910996 {
911997 /*
912- * The first targrows live rows are simply copied into the
998+ * The first targrows sample rows are simply copied into the
913999 * reservoir. Then we start replacing tuples in the sample
9141000 * until we reach the end of the relation. This algorithm is
9151001 * from Jeff Vitter's paper (see full citation below). It
@@ -927,11 +1013,11 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
9271013 /*
9281014 * t in Vitter's paper is the number of records already
9291015 * processed. If we need to compute a new S value, we
930- * must use the not-yet-incremented value of liverows as
931- * t.
1016+ * must use the not-yet-incremented value of samplerows
1017+ * as t.
9321018 */
9331019 if (rowstoskip < 0 )
934- rowstoskip = get_next_S (liverows , targrows , & rstate );
1020+ rowstoskip = get_next_S (samplerows , targrows , & rstate );
9351021
9361022 if (rowstoskip <= 0 )
9371023 {
@@ -949,18 +1035,12 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
9491035 rowstoskip -= 1 ;
9501036 }
9511037
952- liverows += 1 ;
953- }
954- else
955- {
956- /* Count dead rows, but not empty slots */
957- if (targtuple .t_data != NULL )
958- deadrows += 1 ;
1038+ samplerows += 1 ;
9591039 }
9601040 }
9611041
962- /* Now release the pin on the page */
963- ReleaseBuffer (targbuffer );
1042+ /* Now release the lock and pin on the page */
1043+ UnlockReleaseBuffer (targbuffer );
9641044 }
9651045
9661046 /*
0 commit comments