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 */
364366if (!vacstmt -> vacuum )
365- pgstat_report_analyze (RelationGetRelid (onerel ),
366- onerel -> rd_rel -> relisshared ,
367- 0 ,0 );
368-
367+ pgstat_report_analyze (onerel ,0 ,0 );
369368gotocleanup ;
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 ,
857854double * 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 */
861859double deadrows = 0 ;/* # dead rows seen */
862860double rowstoskip = -1 ;/* -1 means not set yet */
863861BlockNumber totalblocks ;
862+ TransactionId OldestXmin ;
864863BlockSamplerData bs ;
865864double rstate ;
866865
867866Assert (targrows > 1 );
868867
869868totalblocks = 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 */
872874BlockSampler_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 */
894898targbuffer = ReadBufferWithStrategy (onerel ,targblock ,vac_strategy );
895899LockBuffer (targbuffer ,BUFFER_LOCK_SHARE );
896900targpage = BufferGetPage (targbuffer );
897901maxoffset = PageGetMaxOffsetNumber (targpage );
898- LockBuffer (targbuffer ,BUFFER_LOCK_UNLOCK );
899902
900903/* Inner loop over all tuples on the selected page */
901904for (targoffset = FirstOffsetNumber ;targoffset <=maxoffset ;targoffset ++ )
902905{
906+ ItemId itemid ;
903907HeapTupleData 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
905925ItemPointerSet (& 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 targrowslive rows are simply copied into the
998+ * The first targrowssample 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 ofliverows as
931- * t.
1016+ * must use the not-yet-incremented value ofsamplerows
1017+ *as t.
9321018 */
9331019if (rowstoskip < 0 )
934- rowstoskip = get_next_S (liverows ,targrows ,& rstate );
1020+ rowstoskip = get_next_S (samplerows ,targrows ,& rstate );
9351021
9361022if (rowstoskip <=0 )
9371023{
@@ -949,18 +1035,12 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
9491035rowstoskip -= 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 thelock and pin on the page */
1043+ UnlockReleaseBuffer (targbuffer );
9641044}
9651045
9661046/*