@ -30,6 +30,7 @@
# include "commands/tablecmds.h"
# include "commands/tablecmds.h"
# include "commands/vacuum.h"
# include "commands/vacuum.h"
# include "executor/executor.h"
# include "executor/executor.h"
# include "foreign/fdwapi.h"
# include "miscadmin.h"
# include "miscadmin.h"
# include "nodes/nodeFuncs.h"
# include "nodes/nodeFuncs.h"
# include "parser/parse_oper.h"
# include "parser/parse_oper.h"
@ -78,14 +79,12 @@ typedef struct AnlIndexData
int default_statistics_target = 100 ;
int default_statistics_target = 100 ;
/* A few variables that don't seem worth passing around as parameters */
/* A few variables that don't seem worth passing around as parameters */
static int elevel = - 1 ;
static MemoryContext anl_context = NULL ;
static MemoryContext anl_context = NULL ;
static BufferAccessStrategy vac_strategy ;
static BufferAccessStrategy vac_strategy ;
static void do_analyze_rel ( Relation onerel , VacuumStmt * vacstmt , bool inh ) ;
static void do_analyze_rel ( Relation onerel , VacuumStmt * vacstmt ,
AcquireSampleRowsFunc acquirefunc , bool inh , int elevel ) ;
static void BlockSampler_Init ( BlockSampler bs , BlockNumber nblocks ,
static void BlockSampler_Init ( BlockSampler bs , BlockNumber nblocks ,
int samplesize ) ;
int samplesize ) ;
static bool BlockSampler_HasMore ( BlockSampler bs ) ;
static bool BlockSampler_HasMore ( BlockSampler bs ) ;
@ -96,13 +95,12 @@ static void compute_index_stats(Relation onerel, double totalrows,
MemoryContext col_context ) ;
MemoryContext col_context ) ;
static VacAttrStats * examine_attribute ( Relation onerel , int attnum ,
static VacAttrStats * examine_attribute ( Relation onerel , int attnum ,
Node * index_expr ) ;
Node * index_expr ) ;
static int acquire_sample_rows ( Relation onerel , HeapTuple * rows ,
static int acquire_sample_rows ( Relation onerel , int elevel ,
int targrows , double * totalrows , double * totaldeadrows ) ;
HeapTuple * rows , int targrows ,
static double random_fract ( void ) ;
double * totalrows , double * totaldeadrows ,
static double init_selection_state ( int n ) ;
BlockNumber * totalpages ) ;
static double get_next_S ( double t , int n , double * stateptr ) ;
static int compare_rows ( const void * a , const void * b ) ;
static int compare_rows ( const void * a , const void * b ) ;
static int acquire_inherited_sample_rows ( Relation onerel ,
static int acquire_inherited_sample_rows ( Relation onerel , int elev el ,
HeapTuple * rows , int targrows ,
HeapTuple * rows , int targrows ,
double * totalrows , double * totaldeadrows ) ;
double * totalrows , double * totaldeadrows ) ;
static void update_attstats ( Oid relid , bool inh ,
static void update_attstats ( Oid relid , bool inh ,
@ -118,13 +116,16 @@ void
analyze_rel ( Oid relid , VacuumStmt * vacstmt , BufferAccessStrategy bstrategy )
analyze_rel ( Oid relid , VacuumStmt * vacstmt , BufferAccessStrategy bstrategy )
{
{
Relation onerel ;
Relation onerel ;
int elevel ;
AcquireSampleRowsFunc acquirefunc ;
/* Set up static variables */
/* Select logging level */
if ( vacstmt - > options & VACOPT_VERBOSE )
if ( vacstmt - > options & VACOPT_VERBOSE )
elevel = INFO ;
elevel = INFO ;
else
else
elevel = DEBUG2 ;
elevel = DEBUG2 ;
/* Set up static variables */
vac_strategy = bstrategy ;
vac_strategy = bstrategy ;
/*
/*
@ -182,10 +183,40 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
}
}
/*
/*
* Check that it ' s a plain table ; we used to do this in get_rel_oids ( ) but
* Check that it ' s a plain table or foreign table ; we used to do this
* seems safer to check after we ' ve locked the relation .
* in get_rel_oids ( ) but seems safer to check after we ' ve locked the
* relation .
*/
*/
if ( onerel - > rd_rel - > relkind ! = RELKIND_RELATION )
if ( onerel - > rd_rel - > relkind = = RELKIND_RELATION )
{
/* Regular table, so we'll use the regular row acquisition function */
acquirefunc = acquire_sample_rows ;
}
else if ( onerel - > rd_rel - > relkind = = RELKIND_FOREIGN_TABLE )
{
/*
* For a foreign table , call the FDW ' s hook function to see whether it
* supports analysis .
*/
FdwRoutine * fdwroutine ;
fdwroutine = GetFdwRoutineByRelId ( RelationGetRelid ( onerel ) ) ;
if ( fdwroutine - > AnalyzeForeignTable ! = NULL )
acquirefunc = fdwroutine - > AnalyzeForeignTable ( onerel ) ;
else
acquirefunc = NULL ;
if ( acquirefunc = = NULL )
{
ereport ( WARNING ,
( errmsg ( " skipping \" %s \" --- cannot analyze this foreign table " ,
RelationGetRelationName ( onerel ) ) ) ) ;
relation_close ( onerel , ShareUpdateExclusiveLock ) ;
return ;
}
}
else
{
{
/* No need for a WARNING if we already complained during VACUUM */
/* No need for a WARNING if we already complained during VACUUM */
if ( ! ( vacstmt - > options & VACOPT_VACUUM ) )
if ( ! ( vacstmt - > options & VACOPT_VACUUM ) )
@ -227,13 +258,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
/*
/*
* Do the normal non - recursive ANALYZE .
* Do the normal non - recursive ANALYZE .
*/
*/
do_analyze_rel ( onerel , vacstmt , false ) ;
do_analyze_rel ( onerel , vacstmt , acquirefunc , false , elevel ) ;
/*
/*
* If there are child tables , do recursive ANALYZE .
* If there are child tables , do recursive ANALYZE .
*/
*/
if ( onerel - > rd_rel - > relhassubclass )
if ( onerel - > rd_rel - > relhassubclass )
do_analyze_rel ( onerel , vacstmt , true ) ;
do_analyze_rel ( onerel , vacstmt , acquirefunc , true , elevel ) ;
/*
/*
* Close source relation now , but keep lock so that no one deletes it
* Close source relation now , but keep lock so that no one deletes it
@ -254,9 +285,15 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
/*
/*
* do_analyze_rel ( ) - - analyze one relation , recursively or not
* do_analyze_rel ( ) - - analyze one relation , recursively or not
*
* Note that " acquirefunc " is only relevant for the non - inherited case .
* If we supported foreign tables in inheritance trees ,
* acquire_inherited_sample_rows would need to determine the appropriate
* acquirefunc for each child table .
*/
*/
static void
static void
do_analyze_rel ( Relation onerel , VacuumStmt * vacstmt , bool inh )
do_analyze_rel ( Relation onerel , VacuumStmt * vacstmt ,
AcquireSampleRowsFunc acquirefunc , bool inh , int elevel )
{
{
int attr_cnt ,
int attr_cnt ,
tcnt ,
tcnt ,
@ -271,6 +308,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, bool inh)
numrows ;
numrows ;
double totalrows ,
double totalrows ,
totaldeadrows ;
totaldeadrows ;
BlockNumber totalpages ;
HeapTuple * rows ;
HeapTuple * rows ;
PGRUsage ru0 ;
PGRUsage ru0 ;
TimestampTz starttime = 0 ;
TimestampTz starttime = 0 ;
@ -447,11 +485,17 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, bool inh)
*/
*/
rows = ( HeapTuple * ) palloc ( targrows * sizeof ( HeapTuple ) ) ;
rows = ( HeapTuple * ) palloc ( targrows * sizeof ( HeapTuple ) ) ;
if ( inh )
if ( inh )
numrows = acquire_inherited_sample_rows ( onerel , rows , targrows ,
{
numrows = acquire_inherited_sample_rows ( onerel , elevel ,
rows , targrows ,
& totalrows , & totaldeadrows ) ;
& totalrows , & totaldeadrows ) ;
totalpages = 0 ; /* not needed in this path */
}
else
else
numrows = acquire_sample_rows ( onerel , rows , targrows ,
numrows = ( * acquirefunc ) ( onerel , elevel ,
& totalrows , & totaldeadrows ) ;
rows , targrows ,
& totalrows , & totaldeadrows ,
& totalpages ) ;
/*
/*
* Compute the statistics . Temporary results during the calculations for
* Compute the statistics . Temporary results during the calculations for
@ -532,7 +576,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, bool inh)
*/
*/
if ( ! inh )
if ( ! inh )
vac_update_relstats ( onerel ,
vac_update_relstats ( onerel ,
RelationGetNumberOfBlocks ( onerel ) ,
totalpages ,
totalrows ,
totalrows ,
visibilitymap_count ( onerel ) ,
visibilitymap_count ( onerel ) ,
hasindex ,
hasindex ,
@ -947,8 +991,8 @@ BlockSampler_Next(BlockSampler bs)
* Knuth says to skip the current block with probability 1 - k / K .
* Knuth says to skip the current block with probability 1 - k / K .
* If we are to skip , we should advance t ( hence decrease K ) , and
* If we are to skip , we should advance t ( hence decrease K ) , and
* repeat the same probabilistic test for the next block . The naive
* repeat the same probabilistic test for the next block . The naive
* implementation thus requires a random_fract ( ) call for each block
* implementation thus requires an anl_ random_fract( ) call for each block
* number . But we can reduce this to one random_fract ( ) call per
* number . But we can reduce this to one anl_ random_fract( ) call per
* selected block , by noting that each time the while - test succeeds ,
* selected block , by noting that each time the while - test succeeds ,
* we can reinterpret V as a uniform random number in the range 0 to p .
* we can reinterpret V as a uniform random number in the range 0 to p .
* Therefore , instead of choosing a new V , we just adjust p to be
* Therefore , instead of choosing a new V , we just adjust p to be
@ -963,7 +1007,7 @@ BlockSampler_Next(BlockSampler bs)
* less than k , which means that we cannot fail to select enough blocks .
* less than k , which means that we cannot fail to select enough blocks .
* - - - - - - - - - -
* - - - - - - - - - -
*/
*/
V = random_fract ( ) ;
V = anl_ random_fract( ) ;
p = 1.0 - ( double ) k / ( double ) K ;
p = 1.0 - ( double ) k / ( double ) K ;
while ( V < p )
while ( V < p )
{
{
@ -988,6 +1032,7 @@ BlockSampler_Next(BlockSampler bs)
* The actual number of rows selected is returned as the function result .
* The actual number of rows selected is returned as the function result .
* We also estimate the total numbers of live and dead rows in the table ,
* We also estimate the total numbers of live and dead rows in the table ,
* and return them into * totalrows and * totaldeadrows , respectively .
* and return them into * totalrows and * totaldeadrows , respectively .
* Also , the number of pages in the relation is returned into * totalpages .
*
*
* The returned list of tuples is in order by physical position in the table .
* The returned list of tuples is in order by physical position in the table .
* ( We will rely on this later to derive correlation estimates . )
* ( We will rely on this later to derive correlation estimates . )
@ -1014,8 +1059,10 @@ BlockSampler_Next(BlockSampler bs)
* density near the start of the table .
* density near the start of the table .
*/
*/
static int
static int
acquire_sample_rows ( Relation onerel , HeapTuple * rows , int targrows ,
acquire_sample_rows ( Relation onerel , int elevel ,
double * totalrows , double * totaldeadrows )
HeapTuple * rows , int targrows ,
double * totalrows , double * totaldeadrows ,
BlockNumber * totalpages )
{
{
int numrows = 0 ; /* # rows now in reservoir */
int numrows = 0 ; /* # rows now in reservoir */
double samplerows = 0 ; /* total # rows collected */
double samplerows = 0 ; /* total # rows collected */
@ -1030,6 +1077,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
Assert ( targrows > 0 ) ;
Assert ( targrows > 0 ) ;
totalblocks = RelationGetNumberOfBlocks ( onerel ) ;
totalblocks = RelationGetNumberOfBlocks ( onerel ) ;
* totalpages = totalblocks ;
/* Need a cutoff xmin for HeapTupleSatisfiesVacuum */
/* Need a cutoff xmin for HeapTupleSatisfiesVacuum */
OldestXmin = GetOldestXmin ( onerel - > rd_rel - > relisshared , true ) ;
OldestXmin = GetOldestXmin ( onerel - > rd_rel - > relisshared , true ) ;
@ -1037,7 +1085,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
/* Prepare for sampling block numbers */
/* Prepare for sampling block numbers */
BlockSampler_Init ( & bs , totalblocks , targrows ) ;
BlockSampler_Init ( & bs , totalblocks , targrows ) ;
/* Prepare for sampling rows */
/* Prepare for sampling rows */
rstate = init_selection_state ( targrows ) ;
rstate = anl_ init_selection_state( targrows ) ;
/* Outer loop over blocks to sample */
/* Outer loop over blocks to sample */
while ( BlockSampler_HasMore ( & bs ) )
while ( BlockSampler_HasMore ( & bs ) )
@ -1184,7 +1232,8 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
* t .
* t .
*/
*/
if ( rowstoskip < 0 )
if ( rowstoskip < 0 )
rowstoskip = get_next_S ( samplerows , targrows , & rstate ) ;
rowstoskip = anl_get_next_S ( samplerows , targrows ,
& rstate ) ;
if ( rowstoskip < = 0 )
if ( rowstoskip < = 0 )
{
{
@ -1192,7 +1241,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
* Found a suitable tuple , so save it , replacing one
* Found a suitable tuple , so save it , replacing one
* old tuple at random
* old tuple at random
*/
*/
int k = ( int ) ( targrows * random_fract ( ) ) ;
int k = ( int ) ( targrows * anl_ random_fract( ) ) ;
Assert ( k > = 0 & & k < targrows ) ;
Assert ( k > = 0 & & k < targrows ) ;
heap_freetuple ( rows [ k ] ) ;
heap_freetuple ( rows [ k ] ) ;
@ -1252,8 +1301,8 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
}
}
/* Select a random value R uniformly distributed in (0 - 1) */
/* Select a random value R uniformly distributed in (0 - 1) */
static double
double
random_fract ( void )
anl_ random_fract( void )
{
{
return ( ( double ) random ( ) + 1 ) / ( ( double ) MAX_RANDOM_VALUE + 2 ) ;
return ( ( double ) random ( ) + 1 ) / ( ( double ) MAX_RANDOM_VALUE + 2 ) ;
}
}
@ -1266,21 +1315,21 @@ random_fract(void)
* It is computed primarily based on t , the number of records already read .
* It is computed primarily based on t , the number of records already read .
* The only extra state needed between calls is W , a random state variable .
* The only extra state needed between calls is W , a random state variable .
*
*
* init_selection_state computes the initial W value .
* anl_ init_selection_state computes the initial W value .
*
*
* Given that we ' ve already read t records ( t > = n ) , get_next_S
* Given that we ' ve already read t records ( t > = n ) , anl_ get_next_S
* determines the number of records to skip before the next record is
* determines the number of records to skip before the next record is
* processed .
* processed .
*/
*/
static double
double
init_selection_state ( int n )
anl_ init_selection_state( int n )
{
{
/* Initial value of W (for use when Algorithm Z is first applied) */
/* Initial value of W (for use when Algorithm Z is first applied) */
return exp ( - log ( random_fract ( ) ) / n ) ;
return exp ( - log ( anl_ random_fract( ) ) / n ) ;
}
}
static double
double
get_next_S ( double t , int n , double * stateptr )
anl_ get_next_S( double t , int n , double * stateptr )
{
{
double S ;
double S ;
@ -1291,7 +1340,7 @@ get_next_S(double t, int n, double *stateptr)
double V ,
double V ,
quot ;
quot ;
V = random_fract ( ) ; /* Generate V */
V = anl_ random_fract( ) ; /* Generate V */
S = 0 ;
S = 0 ;
t + = 1 ;
t + = 1 ;
/* Note: "num" in Vitter's code is always equal to t - n */
/* Note: "num" in Vitter's code is always equal to t - n */
@ -1323,7 +1372,7 @@ get_next_S(double t, int n, double *stateptr)
tmp ;
tmp ;
/* Generate U and X */
/* Generate U and X */
U = random_fract ( ) ;
U = anl_ random_fract( ) ;
X = t * ( W - 1.0 ) ;
X = t * ( W - 1.0 ) ;
S = floor ( X ) ; /* S is tentatively set to floor(X) */
S = floor ( X ) ; /* S is tentatively set to floor(X) */
/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@ -1352,7 +1401,7 @@ get_next_S(double t, int n, double *stateptr)
y * = numer / denom ;
y * = numer / denom ;
denom - = 1 ;
denom - = 1 ;
}
}
W = exp ( - log ( random_fract ( ) ) / n ) ; /* Generate W in advance */
W = exp ( - log ( anl_ random_fract( ) ) / n ) ; /* Generate W in advance */
if ( exp ( log ( y ) / n ) < = ( t + X ) / t )
if ( exp ( log ( y ) / n ) < = ( t + X ) / t )
break ;
break ;
}
}
@ -1389,12 +1438,13 @@ compare_rows(const void *a, const void *b)
/*
/*
* acquire_inherited_sample_rows - - acquire sample rows from inheritance tree
* acquire_inherited_sample_rows - - acquire sample rows from inheritance tree
*
*
* This has the same API as acquire_sample_rows , except that rows are
* This has largely the same API as acquire_sample_rows , except that rows are
* collected from all inheritance children as well as the specified table .
* collected from all inheritance children as well as the specified table .
* We fail and return zero if there are no inheritance children .
* We fail and return zero if there are no inheritance children .
*/
*/
static int
static int
acquire_inherited_sample_rows ( Relation onerel , HeapTuple * rows , int targrows ,
acquire_inherited_sample_rows ( Relation onerel , int elevel ,
HeapTuple * rows , int targrows ,
double * totalrows , double * totaldeadrows )
double * totalrows , double * totaldeadrows )
{
{
List * tableOIDs ;
List * tableOIDs ;
@ -1431,6 +1481,11 @@ acquire_inherited_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
/*
/*
* Count the blocks in all the relations . The result could overflow
* Count the blocks in all the relations . The result could overflow
* BlockNumber , so we use double arithmetic .
* BlockNumber , so we use double arithmetic .
*
* XXX eventually we will probably want to allow child tables that are
* foreign tables . Since we can ' t do RelationGetNumberOfBlocks on a
* foreign table , it ' s not very clear what fraction of the total to assign
* to it here .
*/
*/
rels = ( Relation * ) palloc ( list_length ( tableOIDs ) * sizeof ( Relation ) ) ;
rels = ( Relation * ) palloc ( list_length ( tableOIDs ) * sizeof ( Relation ) ) ;
relblocks = ( double * ) palloc ( list_length ( tableOIDs ) * sizeof ( double ) ) ;
relblocks = ( double * ) palloc ( list_length ( tableOIDs ) * sizeof ( double ) ) ;
@ -1485,13 +1540,16 @@ acquire_inherited_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
int childrows ;
int childrows ;
double trows ,
double trows ,
tdrows ;
tdrows ;
BlockNumber tpages ;
/* Fetch a random sample of the child's rows */
/* Fetch a random sample of the child's rows */
childrows = acquire_sample_rows ( childrel ,
childrows = acquire_sample_rows ( childrel ,
elevel ,
rows + numrows ,
rows + numrows ,
childtargrows ,
childtargrows ,
& trows ,
& trows ,
& tdrows ) ;
& tdrows ,
& tpages ) ;
/* We may need to convert from child's rowtype to parent's */
/* We may need to convert from child's rowtype to parent's */
if ( childrows > 0 & &
if ( childrows > 0 & &