#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"
Go to the source code of this file.
Data Structures | |
struct | VacAttrStats |
Typedefs | |
typedef struct VacAttrStats * | VacAttrStatsP |
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 void(* AnalyzeAttrComputeStatsFunc)(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) |
typedef Datum(* AnalyzeAttrFetchFunc)(VacAttrStatsP stats, int rownum, bool *isNull) |
typedef struct VacAttrStats VacAttrStats |
typedef struct VacAttrStats* VacAttrStatsP |
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, <opr, &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; }
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); }
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 | ) |
Definition at line 1243 of file vacuum.c.
References AutoVacuumUpdateDelay(), CHECK_FOR_INTERRUPTS, InterruptPending, pg_usleep(), VacuumCostActive, VacuumCostBalance, VacuumCostDelay, and VacuumCostLimit.
Referenced by acquire_sample_rows(), btvacuumpage(), compute_array_stats(), compute_minimal_stats(), compute_range_stats(), compute_scalar_stats(), compute_tsvector_stats(), file_acquire_sample_rows(), ginbulkdelete(), ginInsertCleanup(), ginvacuumcleanup(), ginVacuumPostingTree(), gistbulkdelete(), gistvacuumcleanup(), hashbulkdelete(), lazy_scan_heap(), lazy_vacuum_heap(), spgprocesspending(), and spgvacuumpage().
{ /* Always check for interrupts */ CHECK_FOR_INTERRUPTS(); /* Nap if appropriate */ if (VacuumCostActive && !InterruptPending && VacuumCostBalance >= VacuumCostLimit) { int msec; msec = VacuumCostDelay * VacuumCostBalance / VacuumCostLimit; if (msec > VacuumCostDelay * 4) msec = VacuumCostDelay * 4; pg_usleep(msec * 1000L); VacuumCostBalance = 0; /* update balance values for workers */ AutoVacuumUpdateDelay(); /* Might have gotten an interrupt while sleeping */ CHECK_FOR_INTERRUPTS(); } }
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; } }
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().