Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Typedefs | Functions | Variables

vacuum.h File Reference

#include "access/htup.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "nodes/parsenodes.h"
#include "storage/buf.h"
#include "storage/lock.h"
#include "utils/relcache.h"
Include dependency graph for vacuum.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  VacAttrStats

Typedefs

typedef struct VacAttrStatsVacAttrStatsP
typedef Datum(* AnalyzeAttrFetchFunc )(VacAttrStatsP stats, int rownum, bool *isNull)
typedef void(* AnalyzeAttrComputeStatsFunc )(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
typedef struct VacAttrStats VacAttrStats

Functions

void vacuum (VacuumStmt *vacstmt, Oid relid, bool do_toast, BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
void vac_open_indexes (Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
void vac_close_indexes (int nindexes, Relation *Irel, LOCKMODE lockmode)
double vac_estimate_reltuples (Relation relation, bool is_analyze, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples)
void vac_update_relstats (Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti)
void vacuum_set_xid_limits (int freeze_min_age, int freeze_table_age, bool sharedRel, TransactionId *oldestXmin, TransactionId *freezeLimit, TransactionId *freezeTableLimit, MultiXactId *multiXactFrzLimit)
void vac_update_datfrozenxid (void)
void vacuum_delay_point (void)
void lazy_vacuum_rel (Relation onerel, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
void analyze_rel (Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
bool std_typanalyze (VacAttrStats *stats)
double anl_random_fract (void)
double anl_init_selection_state (int n)
double anl_get_next_S (double t, int n, double *stateptr)

Variables

PGDLLIMPORT int default_statistics_target
int vacuum_freeze_min_age
int vacuum_freeze_table_age

Typedef Documentation

typedef void(* AnalyzeAttrComputeStatsFunc)(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)

Definition at line 64 of file vacuum.h.

typedef Datum(* AnalyzeAttrFetchFunc)(VacAttrStatsP stats, int rownum, bool *isNull)

Definition at line 61 of file vacuum.h.

typedef struct VacAttrStats VacAttrStats
typedef struct VacAttrStats* VacAttrStatsP

Definition at line 59 of file vacuum.h.


Function Documentation

void analyze_rel ( Oid  relid,
VacuumStmt vacstmt,
BufferAccessStrategy  bstrategy 
)

Definition at line 117 of file analyze.c.

References FdwRoutine::AnalyzeForeignTable, CHECK_FOR_INTERRUPTS, ConditionalLockRelationOid(), do_analyze_rel(), elevel, ereport, errcode(), errmsg(), GetFdwRoutineForRelation(), GetUserId(), IsAutoVacuumWorkerProcess(), LOG, Log_autovacuum_min_duration, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyDatabaseId, MyPgXact, NoLock, NULL, VacuumStmt::options, PG_CATALOG_NAMESPACE, pg_class_ownercheck(), pg_database_ownercheck(), ProcArrayLock, RelationData::rd_rel, VacuumStmt::relation, relation_close(), RELATION_IS_OTHER_TEMP, RelationGetNumberOfBlocks, RelationGetRelationName, RelationGetRelid, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_RELATION, RangeVar::relname, ShareUpdateExclusiveLock, StatisticRelationId, try_relation_open(), VACOPT_NOWAIT, VACOPT_VACUUM, VACOPT_VERBOSE, PGXACT::vacuumFlags, and WARNING.

Referenced by vacuum().

{
    Relation    onerel;
    int         elevel;
    AcquireSampleRowsFunc acquirefunc = NULL;
    BlockNumber relpages = 0;

    /* Select logging level */
    if (vacstmt->options & VACOPT_VERBOSE)
        elevel = INFO;
    else
        elevel = DEBUG2;

    /* Set up static variables */
    vac_strategy = bstrategy;

    /*
     * Check for user-requested abort.
     */
    CHECK_FOR_INTERRUPTS();

    /*
     * Open the relation, getting ShareUpdateExclusiveLock to ensure that two
     * ANALYZEs don't run on it concurrently.  (This also locks out a
     * concurrent VACUUM, which doesn't matter much at the moment but might
     * matter if we ever try to accumulate stats on dead tuples.) If the rel
     * has been dropped since we last saw it, we don't need to process it.
     */
    if (!(vacstmt->options & VACOPT_NOWAIT))
        onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
    else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock))
        onerel = try_relation_open(relid, NoLock);
    else
    {
        onerel = NULL;
        if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
            ereport(LOG,
                    (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                  errmsg("skipping analyze of \"%s\" --- lock not available",
                         vacstmt->relation->relname)));
    }
    if (!onerel)
        return;

    /*
     * Check permissions --- this should match vacuum's check!
     */
    if (!(pg_class_ownercheck(RelationGetRelid(onerel), GetUserId()) ||
          (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))
    {
        /* No need for a WARNING if we already complained during VACUUM */
        if (!(vacstmt->options & VACOPT_VACUUM))
        {
            if (onerel->rd_rel->relisshared)
                ereport(WARNING,
                 (errmsg("skipping \"%s\" --- only superuser can analyze it",
                         RelationGetRelationName(onerel))));
            else if (onerel->rd_rel->relnamespace == PG_CATALOG_NAMESPACE)
                ereport(WARNING,
                        (errmsg("skipping \"%s\" --- only superuser or database owner can analyze it",
                                RelationGetRelationName(onerel))));
            else
                ereport(WARNING,
                        (errmsg("skipping \"%s\" --- only table or database owner can analyze it",
                                RelationGetRelationName(onerel))));
        }
        relation_close(onerel, ShareUpdateExclusiveLock);
        return;
    }

    /*
     * Silently ignore tables that are temp tables of other backends ---
     * trying to analyze these is rather pointless, since their contents are
     * probably not up-to-date on disk.  (We don't throw a warning here; it
     * would just lead to chatter during a database-wide ANALYZE.)
     */
    if (RELATION_IS_OTHER_TEMP(onerel))
    {
        relation_close(onerel, ShareUpdateExclusiveLock);
        return;
    }

    /*
     * We can ANALYZE any table except pg_statistic. See update_attstats
     */
    if (RelationGetRelid(onerel) == StatisticRelationId)
    {
        relation_close(onerel, ShareUpdateExclusiveLock);
        return;
    }

    /*
     * Check that it's a plain table, materialized view, or foreign table; we
     * used to do this in get_rel_oids() but seems safer to check after we've
     * locked the relation.
     */
    if (onerel->rd_rel->relkind == RELKIND_RELATION ||
        onerel->rd_rel->relkind == RELKIND_MATVIEW)
    {
        /* Regular table, so we'll use the regular row acquisition function */
        acquirefunc = acquire_sample_rows;
        /* Also get regular table's size */
        relpages = RelationGetNumberOfBlocks(onerel);
    }
    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;
        bool        ok = false;

        fdwroutine = GetFdwRoutineForRelation(onerel, false);

        if (fdwroutine->AnalyzeForeignTable != NULL)
            ok = fdwroutine->AnalyzeForeignTable(onerel,
                                                 &acquirefunc,
                                                 &relpages);

        if (!ok)
        {
            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 */
        if (!(vacstmt->options & VACOPT_VACUUM))
            ereport(WARNING,
                    (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
                            RelationGetRelationName(onerel))));
        relation_close(onerel, ShareUpdateExclusiveLock);
        return;
    }

    /*
     * OK, let's do it.  First let other backends know I'm in ANALYZE.
     */
    LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
    MyPgXact->vacuumFlags |= PROC_IN_ANALYZE;
    LWLockRelease(ProcArrayLock);

    /*
     * Do the normal non-recursive ANALYZE.
     */
    do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, false, elevel);

    /*
     * If there are child tables, do recursive ANALYZE.
     */
    if (onerel->rd_rel->relhassubclass)
        do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, true, elevel);

    /*
     * Close source relation now, but keep lock so that no one deletes it
     * before we commit.  (If someone did, they'd fail to clean up the entries
     * we made in pg_statistic.  Also, releasing the lock before commit would
     * expose us to concurrent-update failures in update_attstats.)
     */
    relation_close(onerel, NoLock);

    /*
     * Reset my PGXACT flag.  Note: we need this here, and not in vacuum_rel,
     * because the vacuum flag is cleared by the end-of-xact code.
     */
    LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
    MyPgXact->vacuumFlags &= ~PROC_IN_ANALYZE;
    LWLockRelease(ProcArrayLock);
}

double anl_get_next_S ( double  t,
int  n,
double *  stateptr 
)

Definition at line 1333 of file analyze.c.

References anl_random_fract(), and W.

Referenced by acquire_sample_rows(), analyze_row_processor(), and file_acquire_sample_rows().

{
    double      S;

    /* The magic constant here is T from Vitter's paper */
    if (t <= (22.0 * n))
    {
        /* Process records using Algorithm X until t is large enough */
        double      V,
                    quot;

        V = anl_random_fract(); /* Generate V */
        S = 0;
        t += 1;
        /* Note: "num" in Vitter's code is always equal to t - n */
        quot = (t - (double) n) / t;
        /* Find min S satisfying (4.1) */
        while (quot > V)
        {
            S += 1;
            t += 1;
            quot *= (t - (double) n) / t;
        }
    }
    else
    {
        /* Now apply Algorithm Z */
        double      W = *stateptr;
        double      term = t - (double) n + 1;

        for (;;)
        {
            double      numer,
                        numer_lim,
                        denom;
            double      U,
                        X,
                        lhs,
                        rhs,
                        y,
                        tmp;

            /* Generate U and X */
            U = anl_random_fract();
            X = t * (W - 1.0);
            S = floor(X);       /* S is tentatively set to floor(X) */
            /* Test if U <= h(S)/cg(X) in the manner of (6.3) */
            tmp = (t + 1) / term;
            lhs = exp(log(((U * tmp * tmp) * (term + S)) / (t + X)) / n);
            rhs = (((t + X) / (term + S)) * term) / t;
            if (lhs <= rhs)
            {
                W = rhs / lhs;
                break;
            }
            /* Test if U <= f(S)/cg(X) */
            y = (((U * (t + 1)) / term) * (t + S + 1)) / (t + X);
            if ((double) n < S)
            {
                denom = t;
                numer_lim = term + S;
            }
            else
            {
                denom = t - (double) n + S;
                numer_lim = t + 1;
            }
            for (numer = t + S; numer >= numer_lim; numer -= 1)
            {
                y *= numer / denom;
                denom -= 1;
            }
            W = exp(-log(anl_random_fract()) / n);      /* Generate W in advance */
            if (exp(log(y) / n) <= (t + X) / t)
                break;
        }
        *stateptr = W;
    }
    return S;
}

double anl_init_selection_state ( int  n  ) 

Definition at line 1326 of file analyze.c.

References anl_random_fract().

Referenced by acquire_sample_rows(), file_acquire_sample_rows(), and postgresAcquireSampleRowsFunc().

{
    /* Initial value of W (for use when Algorithm Z is first applied) */
    return exp(-log(anl_random_fract()) / n);
}

double anl_random_fract ( void   ) 

Definition at line 1306 of file analyze.c.

References MAX_RANDOM_VALUE, and random().

Referenced by acquire_sample_rows(), analyze_row_processor(), anl_get_next_S(), anl_init_selection_state(), BlockSampler_Next(), and file_acquire_sample_rows().

{
    return ((double) random() + 1) / ((double) MAX_RANDOM_VALUE + 2);
}

void lazy_vacuum_rel ( Relation  onerel,
VacuumStmt vacstmt,
BufferAccessStrategy  bstrategy 
)

Definition at line 168 of file vacuumlazy.c.

References elevel, ereport, errmsg(), FreeSpaceMapVacuum(), VacuumStmt::freeze_min_age, VacuumStmt::freeze_table_age, FreezeLimit, get_database_name(), get_namespace_name(), GetCurrentTimestamp(), LVRelStats::hasindex, IsAutoVacuumWorkerProcess(), lazy_scan_heap(), lazy_truncate_heap(), LVRelStats::lock_waiter_detected, LOG, Log_autovacuum_min_duration, MultiXactFrzLimit, MyDatabaseId, LVRelStats::new_rel_tuples, NoLock, LVRelStats::nonempty_pages, LVRelStats::num_index_scans, LVRelStats::old_rel_pages, LVRelStats::old_rel_tuples, OldestXmin, VacuumStmt::options, LVRelStats::pages_removed, palloc0(), pg_rusage_init(), pg_rusage_show(), pgstat_report_vacuum(), RelationData::rd_rel, LVRelStats::rel_pages, REL_TRUNCATE_FRACTION, REL_TRUNCATE_MINIMUM, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RELKIND_MATVIEW, RowExclusiveLock, LVRelStats::scanned_pages, TimestampDifference(), TimestampDifferenceExceeds(), TransactionIdPrecedesOrEquals(), LVRelStats::tuples_deleted, vac_close_indexes(), vac_open_indexes(), vac_update_relstats(), VACOPT_VERBOSE, vacuum_set_xid_limits(), VacuumPageDirty, VacuumPageHit, VacuumPageMiss, and visibilitymap_count().

Referenced by vacuum_rel().

{
    LVRelStats *vacrelstats;
    Relation   *Irel;
    int         nindexes;
    BlockNumber possibly_freeable;
    PGRUsage    ru0;
    TimestampTz starttime = 0;
    long        secs;
    int         usecs;
    double      read_rate,
                write_rate;
    bool        scan_all;
    TransactionId freezeTableLimit;
    BlockNumber new_rel_pages;
    double      new_rel_tuples;
    BlockNumber new_rel_allvisible;
    TransactionId new_frozen_xid;
    MultiXactId new_min_multi;

    /* measure elapsed time iff autovacuum logging requires it */
    if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
    {
        pg_rusage_init(&ru0);
        starttime = GetCurrentTimestamp();
    }

    if (vacstmt->options & VACOPT_VERBOSE)
        elevel = INFO;
    else
        elevel = DEBUG2;

    vac_strategy = bstrategy;

    vacuum_set_xid_limits(vacstmt->freeze_min_age, vacstmt->freeze_table_age,
                          onerel->rd_rel->relisshared,
                          &OldestXmin, &FreezeLimit, &freezeTableLimit,
                          &MultiXactFrzLimit);
    scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
                                             freezeTableLimit);

    vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));

    vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
    vacrelstats->old_rel_tuples = onerel->rd_rel->reltuples;
    vacrelstats->num_index_scans = 0;
    vacrelstats->pages_removed = 0;
    vacrelstats->lock_waiter_detected = false;

    /* Open all indexes of the relation */
    vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
    vacrelstats->hasindex = (nindexes > 0);

    /* Do the vacuuming */
    lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, scan_all);

    /* Done with indexes */
    vac_close_indexes(nindexes, Irel, NoLock);

    /*
     * Optionally truncate the relation.
     *
     * Don't even think about it unless we have a shot at releasing a goodly
     * number of pages.  Otherwise, the time taken isn't worth it.
     *
     * Leave a populated materialized view with at least one page.
     */
    if (onerel->rd_rel->relkind == RELKIND_MATVIEW &&
        vacrelstats->nonempty_pages == 0)
        vacrelstats->nonempty_pages = 1;

    possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
    if (possibly_freeable > 0 &&
        (possibly_freeable >= REL_TRUNCATE_MINIMUM ||
         possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION))
        lazy_truncate_heap(onerel, vacrelstats);

    /* Vacuum the Free Space Map */
    FreeSpaceMapVacuum(onerel);

    /*
     * Update statistics in pg_class.
     *
     * A corner case here is that if we scanned no pages at all because every
     * page is all-visible, we should not update relpages/reltuples, because
     * we have no new information to contribute.  In particular this keeps us
     * from replacing relpages=reltuples=0 (which means "unknown tuple
     * density") with nonzero relpages and reltuples=0 (which means "zero
     * tuple density") unless there's some actual evidence for the latter.
     *
     * We do update relallvisible even in the corner case, since if the table
     * is all-visible we'd definitely like to know that.  But clamp the value
     * to be not more than what we're setting relpages to.
     *
     * Also, don't change relfrozenxid if we skipped any pages, since then we
     * don't know for certain that all tuples have a newer xmin.
     */
    new_rel_pages = vacrelstats->rel_pages;
    new_rel_tuples = vacrelstats->new_rel_tuples;
    if (vacrelstats->scanned_pages == 0 && new_rel_pages > 0)
    {
        new_rel_pages = vacrelstats->old_rel_pages;
        new_rel_tuples = vacrelstats->old_rel_tuples;
    }

    new_rel_allvisible = visibilitymap_count(onerel);
    if (new_rel_allvisible > new_rel_pages)
        new_rel_allvisible = new_rel_pages;

    new_frozen_xid = FreezeLimit;
    if (vacrelstats->scanned_pages < vacrelstats->rel_pages)
        new_frozen_xid = InvalidTransactionId;

    new_min_multi = MultiXactFrzLimit;
    if (vacrelstats->scanned_pages < vacrelstats->rel_pages)
        new_min_multi = InvalidMultiXactId;

    vac_update_relstats(onerel,
                        new_rel_pages,
                        new_rel_tuples,
                        new_rel_allvisible,
                        vacrelstats->hasindex,
                        new_frozen_xid,
                        new_min_multi);

    /* report results to the stats collector, too */
    pgstat_report_vacuum(RelationGetRelid(onerel),
                          onerel->rd_rel->relisshared,
                          new_rel_tuples);

    /* and log the action if appropriate */
    if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
    {
        TimestampTz endtime = GetCurrentTimestamp();

        if (Log_autovacuum_min_duration == 0 ||
            TimestampDifferenceExceeds(starttime, endtime,
                                       Log_autovacuum_min_duration))
        {
            TimestampDifference(starttime, endtime, &secs, &usecs);

            read_rate = 0;
            write_rate = 0;
            if ((secs > 0) || (usecs > 0))
            {
                read_rate = (double) BLCKSZ *VacuumPageMiss / (1024 * 1024) /
                            (secs + usecs / 1000000.0);
                write_rate = (double) BLCKSZ *VacuumPageDirty / (1024 * 1024) /
                            (secs + usecs / 1000000.0);
            }
            ereport(LOG,
                    (errmsg("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n"
                            "pages: %d removed, %d remain\n"
                            "tuples: %.0f removed, %.0f remain\n"
                            "buffer usage: %d hits, %d misses, %d dirtied\n"
                    "avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"
                            "system usage: %s",
                            get_database_name(MyDatabaseId),
                            get_namespace_name(RelationGetNamespace(onerel)),
                            RelationGetRelationName(onerel),
                            vacrelstats->num_index_scans,
                            vacrelstats->pages_removed,
                            vacrelstats->rel_pages,
                            vacrelstats->tuples_deleted,
                            vacrelstats->new_rel_tuples,
                            VacuumPageHit,
                            VacuumPageMiss,
                            VacuumPageDirty,
                            read_rate, write_rate,
                            pg_rusage_show(&ru0))));
        }
    }
}

bool std_typanalyze ( VacAttrStats stats  ) 

Definition at line 1848 of file analyze.c.

References VacAttrStats::attr, VacAttrStats::attrtypid, VacAttrStats::compute_stats, default_statistics_target, StdAnalyzeData::eqfunc, StdAnalyzeData::eqopr, VacAttrStats::extra_data, get_opcode(), get_sort_group_operators(), StdAnalyzeData::ltopr, VacAttrStats::minrows, NULL, OidIsValid, and palloc().

Referenced by array_typanalyze(), and examine_attribute().

{
    Form_pg_attribute attr = stats->attr;
    Oid         ltopr;
    Oid         eqopr;
    StdAnalyzeData *mystats;

    /* If the attstattarget column is negative, use the default value */
    /* NB: it is okay to scribble on stats->attr since it's a copy */
    if (attr->attstattarget < 0)
        attr->attstattarget = default_statistics_target;

    /* Look for default "<" and "=" operators for column's type */
    get_sort_group_operators(stats->attrtypid,
                             false, false, false,
                             &ltopr, &eqopr, NULL,
                             NULL);

    /* If column has no "=" operator, we can't do much of anything */
    if (!OidIsValid(eqopr))
        return false;

    /* Save the operator info for compute_stats routines */
    mystats = (StdAnalyzeData *) palloc(sizeof(StdAnalyzeData));
    mystats->eqopr = eqopr;
    mystats->eqfunc = get_opcode(eqopr);
    mystats->ltopr = ltopr;
    stats->extra_data = mystats;

    /*
     * Determine which standard statistics algorithm to use
     */
    if (OidIsValid(ltopr))
    {
        /* Seems to be a scalar datatype */
        stats->compute_stats = compute_scalar_stats;
        /*--------------------
         * The following choice of minrows is based on the paper
         * "Random sampling for histogram construction: how much is enough?"
         * by Surajit Chaudhuri, Rajeev Motwani and Vivek Narasayya, in
         * Proceedings of ACM SIGMOD International Conference on Management
         * of Data, 1998, Pages 436-447.  Their Corollary 1 to Theorem 5
         * says that for table size n, histogram size k, maximum relative
         * error in bin size f, and error probability gamma, the minimum
         * random sample size is
         *      r = 4 * k * ln(2*n/gamma) / f^2
         * Taking f = 0.5, gamma = 0.01, n = 10^6 rows, we obtain
         *      r = 305.82 * k
         * Note that because of the log function, the dependence on n is
         * quite weak; even at n = 10^12, a 300*k sample gives <= 0.66
         * bin size error with probability 0.99.  So there's no real need to
         * scale for n, which is a good thing because we don't necessarily
         * know it at this point.
         *--------------------
         */
        stats->minrows = 300 * attr->attstattarget;
    }
    else
    {
        /* Can't do much but the minimal stuff */
        stats->compute_stats = compute_minimal_stats;
        /* Might as well use the same minrows as above */
        stats->minrows = 300 * attr->attstattarget;
    }

    return true;
}

void vac_close_indexes ( int  nindexes,
Relation Irel,
LOCKMODE  lockmode 
)

Definition at line 1222 of file vacuum.c.

References index_close(), NULL, and pfree().

Referenced by do_analyze_rel(), and lazy_vacuum_rel().

{
    if (Irel == NULL)
        return;

    while (nindexes--)
    {
        Relation    ind = Irel[nindexes];

        index_close(ind, lockmode);
    }
    pfree(Irel);
}

double vac_estimate_reltuples ( Relation  relation,
bool  is_analyze,
BlockNumber  total_pages,
BlockNumber  scanned_pages,
double  scanned_tuples 
)

Definition at line 501 of file vacuum.c.

References RelationData::rd_rel.

Referenced by acquire_sample_rows(), and lazy_scan_heap().

{
    BlockNumber old_rel_pages = relation->rd_rel->relpages;
    double      old_rel_tuples = relation->rd_rel->reltuples;
    double      old_density;
    double      new_density;
    double      multiplier;
    double      updated_density;

    /* If we did scan the whole table, just use the count as-is */
    if (scanned_pages >= total_pages)
        return scanned_tuples;

    /*
     * If scanned_pages is zero but total_pages isn't, keep the existing value
     * of reltuples.  (Note: callers should avoid updating the pg_class
     * statistics in this situation, since no new information has been
     * provided.)
     */
    if (scanned_pages == 0)
        return old_rel_tuples;

    /*
     * If old value of relpages is zero, old density is indeterminate; we
     * can't do much except scale up scanned_tuples to match total_pages.
     */
    if (old_rel_pages == 0)
        return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);

    /*
     * Okay, we've covered the corner cases.  The normal calculation is to
     * convert the old measurement to a density (tuples per page), then update
     * the density using an exponential-moving-average approach, and finally
     * compute reltuples as updated_density * total_pages.
     *
     * For ANALYZE, the moving average multiplier is just the fraction of the
     * table's pages we scanned.  This is equivalent to assuming that the
     * tuple density in the unscanned pages didn't change.  Of course, it
     * probably did, if the new density measurement is different. But over
     * repeated cycles, the value of reltuples will converge towards the
     * correct value, if repeated measurements show the same new density.
     *
     * For VACUUM, the situation is a bit different: we have looked at a
     * nonrandom sample of pages, but we know for certain that the pages we
     * didn't look at are precisely the ones that haven't changed lately.
     * Thus, there is a reasonable argument for doing exactly the same thing
     * as for the ANALYZE case, that is use the old density measurement as the
     * value for the unscanned pages.
     *
     * This logic could probably use further refinement.
     */
    old_density = old_rel_tuples / old_rel_pages;
    new_density = scanned_tuples / scanned_pages;
    multiplier = (double) scanned_pages / (double) total_pages;
    updated_density = old_density + (new_density - old_density) * multiplier;
    return floor(updated_density * total_pages + 0.5);
}

void vac_open_indexes ( Relation  relation,
LOCKMODE  lockmode,
int *  nindexes,
Relation **  Irel 
)

Definition at line 1179 of file vacuum.c.

References Assert, i, index_close(), index_open(), IndexIsReady, lfirst_oid, list_free(), list_length(), NoLock, palloc(), RelationData::rd_index, and RelationGetIndexList().

Referenced by do_analyze_rel(), and lazy_vacuum_rel().

{
    List       *indexoidlist;
    ListCell   *indexoidscan;
    int         i;

    Assert(lockmode != NoLock);

    indexoidlist = RelationGetIndexList(relation);

    /* allocate enough memory for all indexes */
    i = list_length(indexoidlist);

    if (i > 0)
        *Irel = (Relation *) palloc(i * sizeof(Relation));
    else
        *Irel = NULL;

    /* collect just the ready indexes */
    i = 0;
    foreach(indexoidscan, indexoidlist)
    {
        Oid         indexoid = lfirst_oid(indexoidscan);
        Relation    indrel;

        indrel = index_open(indexoid, lockmode);
        if (IndexIsReady(indrel->rd_index))
            (*Irel)[i++] = indrel;
        else
            index_close(indrel, lockmode);
    }

    *nindexes = i;

    list_free(indexoidlist);
}

void vac_update_datfrozenxid ( void   ) 

Definition at line 706 of file vacuum.c.

References AccessShareLock, Assert, DATABASEOID, DatabaseRelationId, elog, ERROR, ForceTransactionIdLimitUpdate(), GetOldestMultiXactId(), GetOldestXmin(), GETSTRUCT, heap_close, heap_freetuple(), heap_inplace_update(), heap_open(), HeapTupleIsValid, InvalidOid, MultiXactIdIsValid, MultiXactIdPrecedes(), MyDatabaseId, NULL, ObjectIdGetDatum, RelationRelationId, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_TOASTVALUE, RowExclusiveLock, SearchSysCacheCopy1, SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), TransactionIdIsNormal, TransactionIdPrecedes(), and vac_truncate_clog().

Referenced by do_autovacuum(), and vacuum().

{
    HeapTuple   tuple;
    Form_pg_database dbform;
    Relation    relation;
    SysScanDesc scan;
    HeapTuple   classTup;
    TransactionId newFrozenXid;
    MultiXactId newFrozenMulti;
    bool        dirty = false;

    /*
     * Initialize the "min" calculation with GetOldestXmin, which is a
     * reasonable approximation to the minimum relfrozenxid for not-yet-
     * committed pg_class entries for new tables; see AddNewRelationTuple().
     * So we cannot produce a wrong minimum by starting with this.
     */
    newFrozenXid = GetOldestXmin(true, true);

    /*
     * Similarly, initialize the MultiXact "min" with the value that would
     * be used on pg_class for new tables.  See AddNewRelationTuple().
     */
    newFrozenMulti = GetOldestMultiXactId();

    /*
     * We must seqscan pg_class to find the minimum Xid, because there is no
     * index that can help us here.
     */
    relation = heap_open(RelationRelationId, AccessShareLock);

    scan = systable_beginscan(relation, InvalidOid, false,
                              SnapshotNow, 0, NULL);

    while ((classTup = systable_getnext(scan)) != NULL)
    {
        Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);

        /*
         * Only consider heap and TOAST tables (anything else should have
         * InvalidTransactionId in relfrozenxid anyway.)
         */
        if (classForm->relkind != RELKIND_RELATION &&
            classForm->relkind != RELKIND_MATVIEW &&
            classForm->relkind != RELKIND_TOASTVALUE)
            continue;

        Assert(TransactionIdIsNormal(classForm->relfrozenxid));
        Assert(MultiXactIdIsValid(classForm->relminmxid));

        if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
            newFrozenXid = classForm->relfrozenxid;

        if (MultiXactIdPrecedes(classForm->relminmxid, newFrozenMulti))
            newFrozenMulti = classForm->relminmxid;
    }

    /* we're done with pg_class */
    systable_endscan(scan);
    heap_close(relation, AccessShareLock);

    Assert(TransactionIdIsNormal(newFrozenXid));
    Assert(MultiXactIdIsValid(newFrozenMulti));

    /* Now fetch the pg_database tuple we need to update. */
    relation = heap_open(DatabaseRelationId, RowExclusiveLock);

    /* Fetch a copy of the tuple to scribble on */
    tuple = SearchSysCacheCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
    if (!HeapTupleIsValid(tuple))
        elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
    dbform = (Form_pg_database) GETSTRUCT(tuple);

    /*
     * Don't allow datfrozenxid to go backward (probably can't happen anyway);
     * and detect the common case where it doesn't go forward either.
     */
    if (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid))
    {
        dbform->datfrozenxid = newFrozenXid;
        dirty = true;
    }

    /* ditto */
    if (MultiXactIdPrecedes(dbform->datminmxid, newFrozenMulti))
    {
        dbform->datminmxid = newFrozenMulti;
        dirty = true;
    }

    if (dirty)
        heap_inplace_update(relation, tuple);

    heap_freetuple(tuple);
    heap_close(relation, RowExclusiveLock);

    /*
     * If we were able to advance datfrozenxid, see if we can truncate
     * pg_clog. Also do it if the shared XID-wrap-limit info is stale, since
     * this action will update that too.
     */
    if (dirty || ForceTransactionIdLimitUpdate())
        vac_truncate_clog(newFrozenXid, newFrozenMulti);
}

void vac_update_relstats ( Relation  relation,
BlockNumber  num_pages,
double  num_tuples,
BlockNumber  num_all_visible_pages,
bool  hasindex,
TransactionId  frozenxid,
MultiXactId  minmulti 
)

Definition at line 593 of file vacuum.c.

References elog, ERROR, GETSTRUCT, heap_close, heap_inplace_update(), heap_open(), HeapTupleIsValid, MultiXactIdIsValid, MultiXactIdPrecedes(), NULL, ObjectIdGetDatum, RelationData::rd_rules, RelationGetRelid, RelationRelationId, RELOID, RowExclusiveLock, SearchSysCacheCopy1, TransactionIdIsNormal, TransactionIdPrecedes(), and RelationData::trigdesc.

Referenced by do_analyze_rel(), lazy_cleanup_index(), and lazy_vacuum_rel().

{
    Oid         relid = RelationGetRelid(relation);
    Relation    rd;
    HeapTuple   ctup;
    Form_pg_class pgcform;
    bool        dirty;

    rd = heap_open(RelationRelationId, RowExclusiveLock);

    /* Fetch a copy of the tuple to scribble on */
    ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
    if (!HeapTupleIsValid(ctup))
        elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
             relid);
    pgcform = (Form_pg_class) GETSTRUCT(ctup);

    /* Apply required updates, if any, to copied tuple */

    dirty = false;
    if (pgcform->relpages != (int32) num_pages)
    {
        pgcform->relpages = (int32) num_pages;
        dirty = true;
    }
    if (pgcform->reltuples != (float4) num_tuples)
    {
        pgcform->reltuples = (float4) num_tuples;
        dirty = true;
    }
    if (pgcform->relallvisible != (int32) num_all_visible_pages)
    {
        pgcform->relallvisible = (int32) num_all_visible_pages;
        dirty = true;
    }
    if (pgcform->relhasindex != hasindex)
    {
        pgcform->relhasindex = hasindex;
        dirty = true;
    }

    /*
     * If we have discovered that there are no indexes, then there's no
     * primary key either.  This could be done more thoroughly...
     */
    if (pgcform->relhaspkey && !hasindex)
    {
        pgcform->relhaspkey = false;
        dirty = true;
    }

    /* We also clear relhasrules and relhastriggers if needed */
    if (pgcform->relhasrules && relation->rd_rules == NULL)
    {
        pgcform->relhasrules = false;
        dirty = true;
    }
    if (pgcform->relhastriggers && relation->trigdesc == NULL)
    {
        pgcform->relhastriggers = false;
        dirty = true;
    }

    /*
     * relfrozenxid should never go backward.  Caller can pass
     * InvalidTransactionId if it has no new data.
     */
    if (TransactionIdIsNormal(frozenxid) &&
        TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid))
    {
        pgcform->relfrozenxid = frozenxid;
        dirty = true;
    }

    /* relminmxid must never go backward, either */
    if (MultiXactIdIsValid(minmulti) &&
        MultiXactIdPrecedes(pgcform->relminmxid, minmulti))
    {
        pgcform->relminmxid = minmulti;
        dirty = true;
    }

    /* If anything changed, write out the tuple. */
    if (dirty)
        heap_inplace_update(rd, ctup);

    heap_close(rd, RowExclusiveLock);
}

void vacuum ( VacuumStmt vacstmt,
Oid  relid,
bool  do_toast,
BufferAccessStrategy  bstrategy,
bool  for_wraparound,
bool  isTopLevel 
)

Definition at line 95 of file vacuum.c.

References ActiveSnapshotSet(), ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), analyze_rel(), Assert, BAS_VACUUM, CommitTransactionCommand(), cur, get_rel_oids(), GetAccessStrategy(), GetTransactionSnapshot(), IsAutoVacuumWorkerProcess(), IsInTransactionChain(), lfirst_oid, list_length(), MemoryContextDelete(), MemoryContextSwitchTo(), NIL, NULL, VacuumStmt::options, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pgstat_vacuum_stat(), PopActiveSnapshot(), PortalContext, PreventTransactionChain(), PushActiveSnapshot(), VacuumStmt::relation, StartTransactionCommand(), VacuumStmt::va_cols, vac_update_datfrozenxid(), VACOPT_ANALYZE, VACOPT_FREEZE, VACOPT_FULL, VACOPT_VACUUM, vacuum_rel(), VacuumCostActive, VacuumCostBalance, VacuumCostDelay, VacuumPageDirty, VacuumPageHit, and VacuumPageMiss.

Referenced by autovacuum_do_vac_analyze(), and standard_ProcessUtility().

{
    const char *stmttype;
    volatile bool in_outer_xact,
                use_own_xacts;
    List       *relations;

    /* sanity checks on options */
    Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
    Assert((vacstmt->options & VACOPT_VACUUM) ||
           !(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
    Assert((vacstmt->options & VACOPT_ANALYZE) || vacstmt->va_cols == NIL);

    stmttype = (vacstmt->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";

    /*
     * We cannot run VACUUM inside a user transaction block; if we were inside
     * a transaction, then our commit- and start-transaction-command calls
     * would not have the intended effect!  There are numerous other subtle
     * dependencies on this, too.
     *
     * ANALYZE (without VACUUM) can run either way.
     */
    if (vacstmt->options & VACOPT_VACUUM)
    {
        PreventTransactionChain(isTopLevel, stmttype);
        in_outer_xact = false;
    }
    else
        in_outer_xact = IsInTransactionChain(isTopLevel);

    /*
     * Send info about dead objects to the statistics collector, unless we are
     * in autovacuum --- autovacuum.c does this for itself.
     */
    if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
        pgstat_vacuum_stat();

    /*
     * Create special memory context for cross-transaction storage.
     *
     * Since it is a child of PortalContext, it will go away eventually even
     * if we suffer an error; there's no need for special abort cleanup logic.
     */
    vac_context = AllocSetContextCreate(PortalContext,
                                        "Vacuum",
                                        ALLOCSET_DEFAULT_MINSIZE,
                                        ALLOCSET_DEFAULT_INITSIZE,
                                        ALLOCSET_DEFAULT_MAXSIZE);

    /*
     * If caller didn't give us a buffer strategy object, make one in the
     * cross-transaction memory context.
     */
    if (bstrategy == NULL)
    {
        MemoryContext old_context = MemoryContextSwitchTo(vac_context);

        bstrategy = GetAccessStrategy(BAS_VACUUM);
        MemoryContextSwitchTo(old_context);
    }
    vac_strategy = bstrategy;

    /*
     * Build list of relations to process, unless caller gave us one. (If we
     * build one, we put it in vac_context for safekeeping.)
     */
    relations = get_rel_oids(relid, vacstmt->relation);

    /*
     * Decide whether we need to start/commit our own transactions.
     *
     * For VACUUM (with or without ANALYZE): always do so, so that we can
     * release locks as soon as possible.  (We could possibly use the outer
     * transaction for a one-table VACUUM, but handling TOAST tables would be
     * problematic.)
     *
     * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
     * start/commit our own transactions.  Also, there's no need to do so if
     * only processing one relation.  For multiple relations when not within a
     * transaction block, and also in an autovacuum worker, use own
     * transactions so we can release locks sooner.
     */
    if (vacstmt->options & VACOPT_VACUUM)
        use_own_xacts = true;
    else
    {
        Assert(vacstmt->options & VACOPT_ANALYZE);
        if (IsAutoVacuumWorkerProcess())
            use_own_xacts = true;
        else if (in_outer_xact)
            use_own_xacts = false;
        else if (list_length(relations) > 1)
            use_own_xacts = true;
        else
            use_own_xacts = false;
    }

    /*
     * vacuum_rel expects to be entered with no transaction active; it will
     * start and commit its own transaction.  But we are called by an SQL
     * command, and so we are executing inside a transaction already. We
     * commit the transaction started in PostgresMain() here, and start
     * another one before exiting to match the commit waiting for us back in
     * PostgresMain().
     */
    if (use_own_xacts)
    {
        /* ActiveSnapshot is not set by autovacuum */
        if (ActiveSnapshotSet())
            PopActiveSnapshot();

        /* matches the StartTransaction in PostgresMain() */
        CommitTransactionCommand();
    }

    /* Turn vacuum cost accounting on or off */
    PG_TRY();
    {
        ListCell   *cur;

        VacuumCostActive = (VacuumCostDelay > 0);
        VacuumCostBalance = 0;
        VacuumPageHit = 0;
        VacuumPageMiss = 0;
        VacuumPageDirty = 0;

        /*
         * Loop to process each selected relation.
         */
        foreach(cur, relations)
        {
            Oid         relid = lfirst_oid(cur);

            if (vacstmt->options & VACOPT_VACUUM)
            {
                if (!vacuum_rel(relid, vacstmt, do_toast, for_wraparound))
                    continue;
            }

            if (vacstmt->options & VACOPT_ANALYZE)
            {
                /*
                 * If using separate xacts, start one for analyze. Otherwise,
                 * we can use the outer transaction.
                 */
                if (use_own_xacts)
                {
                    StartTransactionCommand();
                    /* functions in indexes may want a snapshot set */
                    PushActiveSnapshot(GetTransactionSnapshot());
                }

                analyze_rel(relid, vacstmt, vac_strategy);

                if (use_own_xacts)
                {
                    PopActiveSnapshot();
                    CommitTransactionCommand();
                }
            }
        }
    }
    PG_CATCH();
    {
        /* Make sure cost accounting is turned off after error */
        VacuumCostActive = false;
        PG_RE_THROW();
    }
    PG_END_TRY();

    /* Turn off vacuum cost accounting */
    VacuumCostActive = false;

    /*
     * Finish up processing.
     */
    if (use_own_xacts)
    {
        /* here, we are not in a transaction */

        /*
         * This matches the CommitTransaction waiting for us in
         * PostgresMain().
         */
        StartTransactionCommand();
    }

    if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
    {
        /*
         * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
         * (autovacuum.c does this for itself.)
         */
        vac_update_datfrozenxid();
    }

    /*
     * Clean up working storage --- note we must do this after
     * StartTransactionCommand, else we might be trying to delete the active
     * context!
     */
    MemoryContextDelete(vac_context);
    vac_context = NULL;
}

void vacuum_delay_point ( void   ) 
void vacuum_set_xid_limits ( int  freeze_min_age,
int  freeze_table_age,
bool  sharedRel,
TransactionId oldestXmin,
TransactionId freezeLimit,
TransactionId freezeTableLimit,
MultiXactId multiXactFrzLimit 
)

Definition at line 381 of file vacuum.c.

References Assert, autovacuum_freeze_max_age, ereport, errhint(), errmsg(), FirstMultiXactId, GetOldestMultiXactId(), GetOldestXmin(), Min, NULL, ReadNewTransactionId(), TransactionIdIsNormal, TransactionIdPrecedes(), vacuum_freeze_min_age, vacuum_freeze_table_age, and WARNING.

Referenced by copy_heap_data(), and lazy_vacuum_rel().

{
    int         freezemin;
    TransactionId limit;
    TransactionId safeLimit;

    /*
     * We can always ignore processes running lazy vacuum.  This is because we
     * use these values only for deciding which tuples we must keep in the
     * tables.  Since lazy vacuum doesn't write its XID anywhere, it's safe to
     * ignore it.  In theory it could be problematic to ignore lazy vacuums in
     * a full vacuum, but keep in mind that only one vacuum process can be
     * working on a particular table at any time, and that each vacuum is
     * always an independent transaction.
     */
    *oldestXmin = GetOldestXmin(sharedRel, true);

    Assert(TransactionIdIsNormal(*oldestXmin));

    /*
     * Determine the minimum freeze age to use: as specified by the caller, or
     * vacuum_freeze_min_age, but in any case not more than half
     * autovacuum_freeze_max_age, so that autovacuums to prevent XID
     * wraparound won't occur too frequently.
     */
    freezemin = freeze_min_age;
    if (freezemin < 0)
        freezemin = vacuum_freeze_min_age;
    freezemin = Min(freezemin, autovacuum_freeze_max_age / 2);
    Assert(freezemin >= 0);

    /*
     * Compute the cutoff XID, being careful not to generate a "permanent" XID
     */
    limit = *oldestXmin - freezemin;
    if (!TransactionIdIsNormal(limit))
        limit = FirstNormalTransactionId;

    /*
     * If oldestXmin is very far back (in practice, more than
     * autovacuum_freeze_max_age / 2 XIDs old), complain and force a minimum
     * freeze age of zero.
     */
    safeLimit = ReadNewTransactionId() - autovacuum_freeze_max_age;
    if (!TransactionIdIsNormal(safeLimit))
        safeLimit = FirstNormalTransactionId;

    if (TransactionIdPrecedes(limit, safeLimit))
    {
        ereport(WARNING,
                (errmsg("oldest xmin is far in the past"),
                 errhint("Close open transactions soon to avoid wraparound problems.")));
        limit = *oldestXmin;
    }

    *freezeLimit = limit;

    if (freezeTableLimit != NULL)
    {
        int         freezetable;

        /*
         * Determine the table freeze age to use: as specified by the caller,
         * or vacuum_freeze_table_age, but in any case not more than
         * autovacuum_freeze_max_age * 0.95, so that if you have e.g nightly
         * VACUUM schedule, the nightly VACUUM gets a chance to freeze tuples
         * before anti-wraparound autovacuum is launched.
         */
        freezetable = freeze_table_age;
        if (freezetable < 0)
            freezetable = vacuum_freeze_table_age;
        freezetable = Min(freezetable, autovacuum_freeze_max_age * 0.95);
        Assert(freezetable >= 0);

        /*
         * Compute the cutoff XID, being careful not to generate a "permanent"
         * XID.
         */
        limit = ReadNewTransactionId() - freezetable;
        if (!TransactionIdIsNormal(limit))
            limit = FirstNormalTransactionId;

        *freezeTableLimit = limit;
    }

    if (multiXactFrzLimit != NULL)
    {
        MultiXactId mxLimit;

        /*
         * simplistic multixactid freezing: use the same freezing policy as
         * for Xids
         */
        mxLimit = GetOldestMultiXactId() - freezemin;
        if (mxLimit < FirstMultiXactId)
            mxLimit = FirstMultiXactId;

        *multiXactFrzLimit = mxLimit;
    }
}


Variable Documentation

PGDLLIMPORT int default_statistics_target

Definition at line 80 of file analyze.c.

Referenced by range_typanalyze(), std_typanalyze(), and ts_typanalyze().

Definition at line 56 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

Definition at line 57 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().