8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
19
19
#include "access/heapam.h"
20
20
#include "access/transam.h"
21
21
#include "access/tuptoaster.h"
22
+ #include "access/xact.h"
22
23
#include "catalog/index.h"
23
24
#include "catalog/indexing.h"
24
25
#include "catalog/namespace.h"
33
34
#include "pgstat.h"
34
35
#include "postmaster/autovacuum.h"
35
36
#include "storage/proc.h"
37
+ #include "storage/procarray.h"
36
38
#include "utils/acl.h"
37
39
#include "utils/datum.h"
38
40
#include "utils/lsyscache.h"
@@ -362,10 +364,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
362
364
* zero-column table.
363
365
*/
364
366
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 );
369
368
goto cleanup ;
370
369
}
371
370
@@ -481,9 +480,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
481
480
}
482
481
483
482
/* 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 );
487
484
}
488
485
489
486
/* We skip to here if there were no analyzable columns */
@@ -856,18 +853,23 @@ static int
856
853
acquire_sample_rows (Relation onerel , HeapTuple * rows , int targrows ,
857
854
double * totalrows , double * totaldeadrows )
858
855
{
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 */
861
859
double deadrows = 0 ; /* # dead rows seen */
862
860
double rowstoskip = -1 ; /* -1 means not set yet */
863
861
BlockNumber totalblocks ;
862
+ TransactionId OldestXmin ;
864
863
BlockSamplerData bs ;
865
864
double rstate ;
866
865
867
866
Assert (targrows > 1 );
868
867
869
868
totalblocks = RelationGetNumberOfBlocks (onerel );
870
869
870
+ /* Need a cutoff xmin for HeapTupleSatisfiesVacuum */
871
+ OldestXmin = GetOldestXmin (onerel -> rd_rel -> relisshared , true);
872
+
871
873
/* Prepare for sampling block numbers */
872
874
BlockSampler_Init (& bs , totalblocks , targrows );
873
875
/* Prepare for sampling rows */
@@ -888,28 +890,112 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
888
890
* We must maintain a pin on the target page's buffer to ensure that
889
891
* the maxoffset value stays good (else concurrent VACUUM might delete
890
892
* 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.
893
897
*/
894
898
targbuffer = ReadBufferWithStrategy (onerel , targblock , vac_strategy );
895
899
LockBuffer (targbuffer , BUFFER_LOCK_SHARE );
896
900
targpage = BufferGetPage (targbuffer );
897
901
maxoffset = PageGetMaxOffsetNumber (targpage );
898
- LockBuffer (targbuffer , BUFFER_LOCK_UNLOCK );
899
902
900
903
/* Inner loop over all tuples on the selected page */
901
904
for (targoffset = FirstOffsetNumber ; targoffset <= maxoffset ; targoffset ++ )
902
905
{
906
+ ItemId itemid ;
903
907
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
+ }
904
924
905
925
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 )
910
996
{
911
997
/*
912
- * The first targrows live rows are simply copied into the
998
+ * The first targrows sample rows are simply copied into the
913
999
* reservoir. Then we start replacing tuples in the sample
914
1000
* until we reach the end of the relation. This algorithm is
915
1001
* from Jeff Vitter's paper (see full citation below). It
@@ -927,11 +1013,11 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
927
1013
/*
928
1014
* t in Vitter's paper is the number of records already
929
1015
* 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.
932
1018
*/
933
1019
if (rowstoskip < 0 )
934
- rowstoskip = get_next_S (liverows , targrows , & rstate );
1020
+ rowstoskip = get_next_S (samplerows , targrows , & rstate );
935
1021
936
1022
if (rowstoskip <= 0 )
937
1023
{
@@ -949,18 +1035,12 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
949
1035
rowstoskip -= 1 ;
950
1036
}
951
1037
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 ;
959
1039
}
960
1040
}
961
1041
962
- /* Now release the pin on the page */
963
- ReleaseBuffer (targbuffer );
1042
+ /* Now release the lock and pin on the page */
1043
+ UnlockReleaseBuffer (targbuffer );
964
1044
}
965
1045
966
1046
/*
0 commit comments