PostgreSQL Source Code  git master
vacuum.c File Reference
#include "postgres.h"
#include <math.h>
#include "access/clog.h"
#include "access/commit_ts.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/index.h"
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for vacuum.c:

Go to the source code of this file.

Functions

static Listexpand_vacuum_rel (VacuumRelation *vrel, int options)
 
static Listget_all_vacuum_rels (int options)
 
static void vac_truncate_clog (TransactionId frozenXID, MultiXactId minMulti, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti)
 
static bool vacuum_rel (Oid relid, RangeVar *relation, VacuumParams *params, bool skip_privs)
 
static double compute_parallel_delay (void)
 
static VacOptValue get_vacoptval_from_boolean (DefElem *def)
 
static bool vac_tid_reaped (ItemPointer itemptr, void *state)
 
static int vac_cmp_itemptr (const void *left, const void *right)
 
void ExecVacuum (ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 
void vacuum (List *relations, VacuumParams *params, BufferAccessStrategy bstrategy, bool isTopLevel)
 
bool vacuum_is_permitted_for_relation (Oid relid, Form_pg_class reltuple, bits32 options)
 
Relation vacuum_open_relation (Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
 
bool vacuum_get_cutoffs (Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
 
bool vacuum_xid_failsafe_check (const struct VacuumCutoffs *cutoffs)
 
double vac_estimate_reltuples (Relation relation, 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, bool *frozenxid_updated, bool *minmulti_updated, bool in_outer_xact)
 
void vac_update_datfrozenxid (void)
 
void vac_open_indexes (Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
 
void vac_close_indexes (int nindexes, Relation *Irel, LOCKMODE lockmode)
 
void vacuum_delay_point (void)
 
IndexBulkDeleteResultvac_bulkdel_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat, VacDeadItems *dead_items)
 
IndexBulkDeleteResultvac_cleanup_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat)
 
Size vac_max_items_to_alloc_size (int max_items)
 

Variables

int vacuum_freeze_min_age
 
int vacuum_freeze_table_age
 
int vacuum_multixact_freeze_min_age
 
int vacuum_multixact_freeze_table_age
 
int vacuum_failsafe_age
 
int vacuum_multixact_failsafe_age
 
static MemoryContext vac_context = NULL
 
static BufferAccessStrategy vac_strategy
 
pg_atomic_uint32VacuumSharedCostBalance = NULL
 
pg_atomic_uint32VacuumActiveNWorkers = NULL
 
int VacuumCostBalanceLocal = 0
 

Function Documentation

◆ compute_parallel_delay()

static double compute_parallel_delay ( void  )
static

Definition at line 2282 of file vacuum.c.

2283 {
2284  double msec = 0;
2285  uint32 shared_balance;
2286  int nworkers;
2287 
2288  /* Parallel vacuum must be active */
2290 
2292 
2293  /* At least count itself */
2294  Assert(nworkers >= 1);
2295 
2296  /* Update the shared cost balance value atomically */
2298 
2299  /* Compute the total local balance for the current worker */
2301 
2302  if ((shared_balance >= VacuumCostLimit) &&
2303  (VacuumCostBalanceLocal > 0.5 * ((double) VacuumCostLimit / nworkers)))
2304  {
2305  /* Compute sleep time based on the local cost balance */
2309  }
2310 
2311  /*
2312  * Reset the local balance as we accumulated it into the shared value.
2313  */
2314  VacuumCostBalance = 0;
2315 
2316  return msec;
2317 }
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:396
static uint32 pg_atomic_add_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:381
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:236
unsigned int uint32
Definition: c.h:490
int VacuumCostLimit
Definition: globals.c:145
int VacuumCostBalance
Definition: globals.c:152
double VacuumCostDelay
Definition: globals.c:146
Assert(fmt[strlen(fmt) - 1] !='\n')
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:86
int VacuumCostBalanceLocal
Definition: vacuum.c:87
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:85

References Assert(), pg_atomic_add_fetch_u32(), pg_atomic_read_u32(), pg_atomic_sub_fetch_u32(), VacuumActiveNWorkers, VacuumCostBalance, VacuumCostBalanceLocal, VacuumCostDelay, VacuumCostLimit, and VacuumSharedCostBalance.

Referenced by vacuum_delay_point().

◆ ExecVacuum()

void ExecVacuum ( ParseState pstate,
VacuumStmt vacstmt,
bool  isTopLevel 
)

Definition at line 110 of file vacuum.c.

111 {
112  VacuumParams params;
113  bool verbose = false;
114  bool skip_locked = false;
115  bool analyze = false;
116  bool freeze = false;
117  bool full = false;
118  bool disable_page_skipping = false;
119  bool process_main = true;
120  bool process_toast = true;
121  bool skip_database_stats = false;
122  bool only_database_stats = false;
123  ListCell *lc;
124 
125  /* index_cleanup and truncate values unspecified for now */
128 
129  /* By default parallel vacuum is enabled */
130  params.nworkers = 0;
131 
132  /* Parse options list */
133  foreach(lc, vacstmt->options)
134  {
135  DefElem *opt = (DefElem *) lfirst(lc);
136 
137  /* Parse common options for VACUUM and ANALYZE */
138  if (strcmp(opt->defname, "verbose") == 0)
139  verbose = defGetBoolean(opt);
140  else if (strcmp(opt->defname, "skip_locked") == 0)
141  skip_locked = defGetBoolean(opt);
142  else if (!vacstmt->is_vacuumcmd)
143  ereport(ERROR,
144  (errcode(ERRCODE_SYNTAX_ERROR),
145  errmsg("unrecognized ANALYZE option \"%s\"", opt->defname),
146  parser_errposition(pstate, opt->location)));
147 
148  /* Parse options available on VACUUM */
149  else if (strcmp(opt->defname, "analyze") == 0)
150  analyze = defGetBoolean(opt);
151  else if (strcmp(opt->defname, "freeze") == 0)
152  freeze = defGetBoolean(opt);
153  else if (strcmp(opt->defname, "full") == 0)
154  full = defGetBoolean(opt);
155  else if (strcmp(opt->defname, "disable_page_skipping") == 0)
156  disable_page_skipping = defGetBoolean(opt);
157  else if (strcmp(opt->defname, "index_cleanup") == 0)
158  {
159  /* Interpret no string as the default, which is 'auto' */
160  if (!opt->arg)
162  else
163  {
164  char *sval = defGetString(opt);
165 
166  /* Try matching on 'auto' string, or fall back on boolean */
167  if (pg_strcasecmp(sval, "auto") == 0)
169  else
171  }
172  }
173  else if (strcmp(opt->defname, "process_main") == 0)
174  process_main = defGetBoolean(opt);
175  else if (strcmp(opt->defname, "process_toast") == 0)
176  process_toast = defGetBoolean(opt);
177  else if (strcmp(opt->defname, "truncate") == 0)
178  params.truncate = get_vacoptval_from_boolean(opt);
179  else if (strcmp(opt->defname, "parallel") == 0)
180  {
181  if (opt->arg == NULL)
182  {
183  ereport(ERROR,
184  (errcode(ERRCODE_SYNTAX_ERROR),
185  errmsg("parallel option requires a value between 0 and %d",
187  parser_errposition(pstate, opt->location)));
188  }
189  else
190  {
191  int nworkers;
192 
193  nworkers = defGetInt32(opt);
194  if (nworkers < 0 || nworkers > MAX_PARALLEL_WORKER_LIMIT)
195  ereport(ERROR,
196  (errcode(ERRCODE_SYNTAX_ERROR),
197  errmsg("parallel workers for vacuum must be between 0 and %d",
199  parser_errposition(pstate, opt->location)));
200 
201  /*
202  * Disable parallel vacuum, if user has specified parallel
203  * degree as zero.
204  */
205  if (nworkers == 0)
206  params.nworkers = -1;
207  else
208  params.nworkers = nworkers;
209  }
210  }
211  else if (strcmp(opt->defname, "skip_database_stats") == 0)
212  skip_database_stats = defGetBoolean(opt);
213  else if (strcmp(opt->defname, "only_database_stats") == 0)
214  only_database_stats = defGetBoolean(opt);
215  else
216  ereport(ERROR,
217  (errcode(ERRCODE_SYNTAX_ERROR),
218  errmsg("unrecognized VACUUM option \"%s\"", opt->defname),
219  parser_errposition(pstate, opt->location)));
220  }
221 
222  /* Set vacuum options */
223  params.options =
224  (vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE) |
225  (verbose ? VACOPT_VERBOSE : 0) |
226  (skip_locked ? VACOPT_SKIP_LOCKED : 0) |
227  (analyze ? VACOPT_ANALYZE : 0) |
228  (freeze ? VACOPT_FREEZE : 0) |
229  (full ? VACOPT_FULL : 0) |
230  (disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0) |
231  (process_main ? VACOPT_PROCESS_MAIN : 0) |
232  (process_toast ? VACOPT_PROCESS_TOAST : 0) |
233  (skip_database_stats ? VACOPT_SKIP_DATABASE_STATS : 0) |
234  (only_database_stats ? VACOPT_ONLY_DATABASE_STATS : 0);
235 
236  /* sanity checks on options */
238  Assert((params.options & VACOPT_VACUUM) ||
239  !(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
240 
241  if ((params.options & VACOPT_FULL) && params.nworkers > 0)
242  ereport(ERROR,
243  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
244  errmsg("VACUUM FULL cannot be performed in parallel")));
245 
246  /*
247  * Make sure VACOPT_ANALYZE is specified if any column lists are present.
248  */
249  if (!(params.options & VACOPT_ANALYZE))
250  {
251  foreach(lc, vacstmt->rels)
252  {
254 
255  if (vrel->va_cols != NIL)
256  ereport(ERROR,
257  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
258  errmsg("ANALYZE option must be specified when a column list is provided")));
259  }
260  }
261 
262  /*
263  * All freeze ages are zero if the FREEZE option is given; otherwise pass
264  * them as -1 which means to use the default values.
265  */
266  if (params.options & VACOPT_FREEZE)
267  {
268  params.freeze_min_age = 0;
269  params.freeze_table_age = 0;
270  params.multixact_freeze_min_age = 0;
271  params.multixact_freeze_table_age = 0;
272  }
273  else
274  {
275  params.freeze_min_age = -1;
276  params.freeze_table_age = -1;
277  params.multixact_freeze_min_age = -1;
278  params.multixact_freeze_table_age = -1;
279  }
280 
281  /* user-invoked vacuum is never "for wraparound" */
282  params.is_wraparound = false;
283 
284  /* user-invoked vacuum uses VACOPT_VERBOSE instead of log_min_duration */
285  params.log_min_duration = -1;
286 
287  /* Now go through the common routine */
288  vacuum(vacstmt->rels, &params, NULL, isTopLevel);
289 }
#define MAX_PARALLEL_WORKER_LIMIT
int32 defGetInt32(DefElem *def)
Definition: define.c:163
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
int verbose
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define NIL
Definition: pg_list.h:68
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
static long analyze(struct nfa *nfa)
Definition: regc_nfa.c:3044
char * defname
Definition: parsenodes.h:810
int location
Definition: parsenodes.h:814
Node * arg
Definition: parsenodes.h:811
int nworkers
Definition: vacuum.h:238
int freeze_table_age
Definition: vacuum.h:221
VacOptValue truncate
Definition: vacuum.h:231
bits32 options
Definition: vacuum.h:219
int freeze_min_age
Definition: vacuum.h:220
bool is_wraparound
Definition: vacuum.h:226
int multixact_freeze_min_age
Definition: vacuum.h:222
int multixact_freeze_table_age
Definition: vacuum.h:224
int log_min_duration
Definition: vacuum.h:227
VacOptValue index_cleanup
Definition: vacuum.h:230
List * options
Definition: parsenodes.h:3668
bool is_vacuumcmd
Definition: parsenodes.h:3670
List * rels
Definition: parsenodes.h:3669
static VacOptValue get_vacoptval_from_boolean(DefElem *def)
Definition: vacuum.c:2326
void vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy, bool isTopLevel)
Definition: vacuum.c:311
#define VACOPT_FREEZE
Definition: vacuum.h:186
#define VACOPT_SKIP_LOCKED
Definition: vacuum.h:188
#define VACOPT_VACUUM
Definition: vacuum.h:183
#define VACOPT_VERBOSE
Definition: vacuum.h:185
#define VACOPT_FULL
Definition: vacuum.h:187
#define VACOPT_SKIP_DATABASE_STATS
Definition: vacuum.h:192
@ VACOPTVALUE_AUTO
Definition: vacuum.h:206
@ VACOPTVALUE_UNSPECIFIED
Definition: vacuum.h:205
#define VACOPT_PROCESS_TOAST
Definition: vacuum.h:190
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition: vacuum.h:191
#define VACOPT_ONLY_DATABASE_STATS
Definition: vacuum.h:193
#define VACOPT_PROCESS_MAIN
Definition: vacuum.h:189
#define VACOPT_ANALYZE
Definition: vacuum.h:184

References analyze(), DefElem::arg, Assert(), defGetBoolean(), defGetInt32(), defGetString(), DefElem::defname, ereport, errcode(), errmsg(), ERROR, VacuumParams::freeze_min_age, VacuumParams::freeze_table_age, get_vacoptval_from_boolean(), VacuumParams::index_cleanup, VacuumStmt::is_vacuumcmd, VacuumParams::is_wraparound, lfirst, lfirst_node, DefElem::location, VacuumParams::log_min_duration, MAX_PARALLEL_WORKER_LIMIT, VacuumParams::multixact_freeze_min_age, VacuumParams::multixact_freeze_table_age, NIL, VacuumParams::nworkers, VacuumParams::options, VacuumStmt::options, parser_errposition(), pg_strcasecmp(), VacuumStmt::rels, VacuumParams::truncate, VacuumRelation::va_cols, VACOPT_ANALYZE, VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_FREEZE, VACOPT_FULL, VACOPT_ONLY_DATABASE_STATS, VACOPT_PROCESS_MAIN, VACOPT_PROCESS_TOAST, VACOPT_SKIP_DATABASE_STATS, VACOPT_SKIP_LOCKED, VACOPT_VACUUM, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_UNSPECIFIED, vacuum(), and verbose.

Referenced by standard_ProcessUtility().

◆ expand_vacuum_rel()

static List * expand_vacuum_rel ( VacuumRelation vrel,
int  options 
)
static

Definition at line 763 of file vacuum.c.

764 {
765  List *vacrels = NIL;
766  MemoryContext oldcontext;
767 
768  /* If caller supplied OID, there's nothing we need do here. */
769  if (OidIsValid(vrel->oid))
770  {
771  oldcontext = MemoryContextSwitchTo(vac_context);
772  vacrels = lappend(vacrels, vrel);
773  MemoryContextSwitchTo(oldcontext);
774  }
775  else
776  {
777  /* Process a specific relation, and possibly partitions thereof */
778  Oid relid;
779  HeapTuple tuple;
780  Form_pg_class classForm;
781  bool include_parts;
782  int rvr_opts;
783 
784  /*
785  * Since autovacuum workers supply OIDs when calling vacuum(), no
786  * autovacuum worker should reach this code.
787  */
789 
790  /*
791  * We transiently take AccessShareLock to protect the syscache lookup
792  * below, as well as find_all_inheritors's expectation that the caller
793  * holds some lock on the starting relation.
794  */
795  rvr_opts = (options & VACOPT_SKIP_LOCKED) ? RVR_SKIP_LOCKED : 0;
796  relid = RangeVarGetRelidExtended(vrel->relation,
798  rvr_opts,
799  NULL, NULL);
800 
801  /*
802  * If the lock is unavailable, emit the same log statement that
803  * vacuum_rel() and analyze_rel() would.
804  */
805  if (!OidIsValid(relid))
806  {
807  if (options & VACOPT_VACUUM)
809  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
810  errmsg("skipping vacuum of \"%s\" --- lock not available",
811  vrel->relation->relname)));
812  else
814  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
815  errmsg("skipping analyze of \"%s\" --- lock not available",
816  vrel->relation->relname)));
817  return vacrels;
818  }
819 
820  /*
821  * To check whether the relation is a partitioned table and its
822  * ownership, fetch its syscache entry.
823  */
824  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
825  if (!HeapTupleIsValid(tuple))
826  elog(ERROR, "cache lookup failed for relation %u", relid);
827  classForm = (Form_pg_class) GETSTRUCT(tuple);
828 
829  /*
830  * Make a returnable VacuumRelation for this rel if the user has the
831  * required privileges.
832  */
833  if (vacuum_is_permitted_for_relation(relid, classForm, options))
834  {
835  oldcontext = MemoryContextSwitchTo(vac_context);
836  vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
837  relid,
838  vrel->va_cols));
839  MemoryContextSwitchTo(oldcontext);
840  }
841 
842 
843  include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
844  ReleaseSysCache(tuple);
845 
846  /*
847  * If it is, make relation list entries for its partitions. Note that
848  * the list returned by find_all_inheritors() includes the passed-in
849  * OID, so we have to skip that. There's no point in taking locks on
850  * the individual partitions yet, and doing so would just add
851  * unnecessary deadlock risk. For this last reason we do not check
852  * yet the ownership of the partitions, which get added to the list to
853  * process. Ownership will be checked later on anyway.
854  */
855  if (include_parts)
856  {
857  List *part_oids = find_all_inheritors(relid, NoLock, NULL);
858  ListCell *part_lc;
859 
860  foreach(part_lc, part_oids)
861  {
862  Oid part_oid = lfirst_oid(part_lc);
863 
864  if (part_oid == relid)
865  continue; /* ignore original table */
866 
867  /*
868  * We omit a RangeVar since it wouldn't be appropriate to
869  * complain about failure to open one of these relations
870  * later.
871  */
872  oldcontext = MemoryContextSwitchTo(vac_context);
873  vacrels = lappend(vacrels, makeVacuumRelation(NULL,
874  part_oid,
875  vrel->va_cols));
876  MemoryContextSwitchTo(oldcontext);
877  }
878  }
879 
880  /*
881  * Release lock again. This means that by the time we actually try to
882  * process the table, it might be gone or renamed. In the former case
883  * we'll silently ignore it; in the latter case we'll process it
884  * anyway, but we must beware that the RangeVar doesn't necessarily
885  * identify it anymore. This isn't ideal, perhaps, but there's little
886  * practical alternative, since we're typically going to commit this
887  * transaction and begin a new one between now and then. Moreover,
888  * holding locks on multiple relations would create significant risk
889  * of deadlock.
890  */
892  }
893 
894  return vacrels;
895 }
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3324
#define OidIsValid(objectId)
Definition: c.h:759
#define WARNING
Definition: elog.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
List * lappend(List *list, void *datum)
Definition: list.c:338
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:228
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
VacuumRelation * makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
Definition: makefuncs.c:820
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:239
@ RVR_SKIP_LOCKED
Definition: namespace.h:73
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
#define lfirst_oid(lc)
Definition: pg_list.h:174
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
Definition: pg_list.h:54
char * relname
Definition: primnodes.h:74
RangeVar * relation
Definition: parsenodes.h:3683
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:866
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:818
@ RELOID
Definition: syscache.h:89
static MemoryContext vac_context
Definition: vacuum.c:77
bool vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple, bits32 options)
Definition: vacuum.c:595

References AccessShareLock, Assert(), elog(), ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), GETSTRUCT, HeapTupleIsValid, IsAutoVacuumWorkerProcess(), lappend(), lfirst_oid, makeVacuumRelation(), MemoryContextSwitchTo(), NIL, NoLock, ObjectIdGetDatum(), VacuumRelation::oid, OidIsValid, RangeVarGetRelidExtended(), VacuumRelation::relation, ReleaseSysCache(), RangeVar::relname, RELOID, RVR_SKIP_LOCKED, SearchSysCache1(), UnlockRelationOid(), VacuumRelation::va_cols, vac_context, VACOPT_SKIP_LOCKED, VACOPT_VACUUM, vacuum_is_permitted_for_relation(), and WARNING.

Referenced by vacuum().

◆ get_all_vacuum_rels()

static List * get_all_vacuum_rels ( int  options)
static

Definition at line 902 of file vacuum.c.

903 {
904  List *vacrels = NIL;
905  Relation pgclass;
906  TableScanDesc scan;
907  HeapTuple tuple;
908 
909  pgclass = table_open(RelationRelationId, AccessShareLock);
910 
911  scan = table_beginscan_catalog(pgclass, 0, NULL);
912 
913  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
914  {
915  Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
916  MemoryContext oldcontext;
917  Oid relid = classForm->oid;
918 
919  /*
920  * We include partitioned tables here; depending on which operation is
921  * to be performed, caller will decide whether to process or ignore
922  * them.
923  */
924  if (classForm->relkind != RELKIND_RELATION &&
925  classForm->relkind != RELKIND_MATVIEW &&
926  classForm->relkind != RELKIND_PARTITIONED_TABLE)
927  continue;
928 
929  /* check permissions of relation */
930  if (!vacuum_is_permitted_for_relation(relid, classForm, options))
931  continue;
932 
933  /*
934  * Build VacuumRelation(s) specifying the table OIDs to be processed.
935  * We omit a RangeVar since it wouldn't be appropriate to complain
936  * about failure to open one of these relations later.
937  */
938  oldcontext = MemoryContextSwitchTo(vac_context);
939  vacrels = lappend(vacrels, makeVacuumRelation(NULL,
940  relid,
941  NIL));
942  MemoryContextSwitchTo(oldcontext);
943  }
944 
945  table_endscan(scan);
946  table_close(pgclass, AccessShareLock);
947 
948  return vacrels;
949 }
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1093
@ ForwardScanDirection
Definition: sdir.h:28
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1011

References AccessShareLock, ForwardScanDirection, GETSTRUCT, heap_getnext(), lappend(), makeVacuumRelation(), MemoryContextSwitchTo(), NIL, table_beginscan_catalog(), table_close(), table_endscan(), table_open(), vac_context, and vacuum_is_permitted_for_relation().

Referenced by vacuum().

◆ get_vacoptval_from_boolean()

static VacOptValue get_vacoptval_from_boolean ( DefElem def)
static

Definition at line 2326 of file vacuum.c.

2327 {
2329 }
@ VACOPTVALUE_ENABLED
Definition: vacuum.h:208
@ VACOPTVALUE_DISABLED
Definition: vacuum.h:207

References defGetBoolean(), VACOPTVALUE_DISABLED, and VACOPTVALUE_ENABLED.

Referenced by ExecVacuum().

◆ vac_bulkdel_one_index()

IndexBulkDeleteResult* vac_bulkdel_one_index ( IndexVacuumInfo ivinfo,
IndexBulkDeleteResult istat,
VacDeadItems dead_items 
)

Definition at line 2337 of file vacuum.c.

2339 {
2340  /* Do bulk deletion */
2341  istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2342  (void *) dead_items);
2343 
2344  ereport(ivinfo->message_level,
2345  (errmsg("scanned index \"%s\" to remove %d row versions",
2346  RelationGetRelationName(ivinfo->index),
2347  dead_items->num_items)));
2348 
2349  return istat;
2350 }
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:699
#define RelationGetRelationName(relation)
Definition: rel.h:537
Relation index
Definition: genam.h:46
int message_level
Definition: genam.h:50
int num_items
Definition: vacuum.h:286
static bool vac_tid_reaped(ItemPointer itemptr, void *state)
Definition: vacuum.c:2398

References ereport, errmsg(), IndexVacuumInfo::index, index_bulk_delete(), IndexVacuumInfo::message_level, VacDeadItems::num_items, RelationGetRelationName, and vac_tid_reaped().

Referenced by lazy_vacuum_one_index(), and parallel_vacuum_process_one_index().

◆ vac_cleanup_one_index()

IndexBulkDeleteResult* vac_cleanup_one_index ( IndexVacuumInfo ivinfo,
IndexBulkDeleteResult istat 
)

Definition at line 2358 of file vacuum.c.

2359 {
2360  istat = index_vacuum_cleanup(ivinfo, istat);
2361 
2362  if (istat)
2363  ereport(ivinfo->message_level,
2364  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2365  RelationGetRelationName(ivinfo->index),
2366  istat->num_index_tuples,
2367  istat->num_pages),
2368  errdetail("%.0f index row versions were removed.\n"
2369  "%u index pages were newly deleted.\n"
2370  "%u index pages are currently deleted, of which %u are currently reusable.",
2371  istat->tuples_removed,
2372  istat->pages_newly_deleted,
2373  istat->pages_deleted, istat->pages_free)));
2374 
2375  return istat;
2376 }
int errdetail(const char *fmt,...)
Definition: elog.c:1202
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:720
BlockNumber pages_deleted
Definition: genam.h:81
BlockNumber pages_newly_deleted
Definition: genam.h:80
BlockNumber pages_free
Definition: genam.h:82
BlockNumber num_pages
Definition: genam.h:76
double tuples_removed
Definition: genam.h:79
double num_index_tuples
Definition: genam.h:78

References ereport, errdetail(), errmsg(), IndexVacuumInfo::index, index_vacuum_cleanup(), IndexVacuumInfo::message_level, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, RelationGetRelationName, and IndexBulkDeleteResult::tuples_removed.

Referenced by lazy_cleanup_one_index(), and parallel_vacuum_process_one_index().

◆ vac_close_indexes()

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

Definition at line 2190 of file vacuum.c.

2191 {
2192  if (Irel == NULL)
2193  return;
2194 
2195  while (nindexes--)
2196  {
2197  Relation ind = Irel[nindexes];
2198 
2199  index_close(ind, lockmode);
2200  }
2201  pfree(Irel);
2202 }
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
void pfree(void *pointer)
Definition: mcxt.c:1436

References index_close(), and pfree().

Referenced by do_analyze_rel(), heap_vacuum_rel(), and parallel_vacuum_main().

◆ vac_cmp_itemptr()

static int vac_cmp_itemptr ( const void *  left,
const void *  right 
)
static

Definition at line 2432 of file vacuum.c.

2433 {
2434  BlockNumber lblk,
2435  rblk;
2436  OffsetNumber loff,
2437  roff;
2438 
2439  lblk = ItemPointerGetBlockNumber((ItemPointer) left);
2440  rblk = ItemPointerGetBlockNumber((ItemPointer) right);
2441 
2442  if (lblk < rblk)
2443  return -1;
2444  if (lblk > rblk)
2445  return 1;
2446 
2447  loff = ItemPointerGetOffsetNumber((ItemPointer) left);
2448  roff = ItemPointerGetOffsetNumber((ItemPointer) right);
2449 
2450  if (loff < roff)
2451  return -1;
2452  if (loff > roff)
2453  return 1;
2454 
2455  return 0;
2456 }
uint32 BlockNumber
Definition: block.h:31
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
uint16 OffsetNumber
Definition: off.h:24

References ItemPointerGetBlockNumber(), and ItemPointerGetOffsetNumber().

Referenced by vac_tid_reaped().

◆ vac_estimate_reltuples()

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

Definition at line 1213 of file vacuum.c.

1217 {
1218  BlockNumber old_rel_pages = relation->rd_rel->relpages;
1219  double old_rel_tuples = relation->rd_rel->reltuples;
1220  double old_density;
1221  double unscanned_pages;
1222  double total_tuples;
1223 
1224  /* If we did scan the whole table, just use the count as-is */
1225  if (scanned_pages >= total_pages)
1226  return scanned_tuples;
1227 
1228  /*
1229  * When successive VACUUM commands scan the same few pages again and
1230  * again, without anything from the table really changing, there is a risk
1231  * that our beliefs about tuple density will gradually become distorted.
1232  * This might be caused by vacuumlazy.c implementation details, such as
1233  * its tendency to always scan the last heap page. Handle that here.
1234  *
1235  * If the relation is _exactly_ the same size according to the existing
1236  * pg_class entry, and only a few of its pages (less than 2%) were
1237  * scanned, keep the existing value of reltuples. Also keep the existing
1238  * value when only a subset of rel's pages <= a single page were scanned.
1239  *
1240  * (Note: we might be returning -1 here.)
1241  */
1242  if (old_rel_pages == total_pages &&
1243  scanned_pages < (double) total_pages * 0.02)
1244  return old_rel_tuples;
1245  if (scanned_pages <= 1)
1246  return old_rel_tuples;
1247 
1248  /*
1249  * If old density is unknown, we can't do much except scale up
1250  * scanned_tuples to match total_pages.
1251  */
1252  if (old_rel_tuples < 0 || old_rel_pages == 0)
1253  return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1254 
1255  /*
1256  * Okay, we've covered the corner cases. The normal calculation is to
1257  * convert the old measurement to a density (tuples per page), then
1258  * estimate the number of tuples in the unscanned pages using that figure,
1259  * and finally add on the number of tuples in the scanned pages.
1260  */
1261  old_density = old_rel_tuples / old_rel_pages;
1262  unscanned_pages = (double) total_pages - (double) scanned_pages;
1263  total_tuples = old_density * unscanned_pages + scanned_tuples;
1264  return floor(total_tuples + 0.5);
1265 }
Form_pg_class rd_rel
Definition: rel.h:110

References RelationData::rd_rel.

Referenced by lazy_scan_heap(), and statapprox_heap().

◆ vac_max_items_to_alloc_size()

Size vac_max_items_to_alloc_size ( int  max_items)

Definition at line 2383 of file vacuum.c.

2384 {
2385  Assert(max_items <= MAXDEADITEMS(MaxAllocSize));
2386 
2387  return offsetof(VacDeadItems, items) + sizeof(ItemPointerData) * max_items;
2388 }
struct ItemPointerData ItemPointerData
#define MaxAllocSize
Definition: memutils.h:40
#define MAXDEADITEMS(avail_mem)
Definition: vacuum.h:292

References Assert(), MaxAllocSize, and MAXDEADITEMS.

Referenced by dead_items_alloc(), and parallel_vacuum_init().

◆ vac_open_indexes()

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

Definition at line 2147 of file vacuum.c.

2149 {
2150  List *indexoidlist;
2151  ListCell *indexoidscan;
2152  int i;
2153 
2154  Assert(lockmode != NoLock);
2155 
2156  indexoidlist = RelationGetIndexList(relation);
2157 
2158  /* allocate enough memory for all indexes */
2159  i = list_length(indexoidlist);
2160 
2161  if (i > 0)
2162  *Irel = (Relation *) palloc(i * sizeof(Relation));
2163  else
2164  *Irel = NULL;
2165 
2166  /* collect just the ready indexes */
2167  i = 0;
2168  foreach(indexoidscan, indexoidlist)
2169  {
2170  Oid indexoid = lfirst_oid(indexoidscan);
2171  Relation indrel;
2172 
2173  indrel = index_open(indexoid, lockmode);
2174  if (indrel->rd_index->indisready)
2175  (*Irel)[i++] = indrel;
2176  else
2177  index_close(indrel, lockmode);
2178  }
2179 
2180  *nindexes = i;
2181 
2182  list_free(indexoidlist);
2183 }
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
int i
Definition: isn.c:73
void list_free(List *list)
Definition: list.c:1545
void * palloc(Size size)
Definition: mcxt.c:1210
static int list_length(const List *l)
Definition: pg_list.h:152
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4739
Form_pg_index rd_index
Definition: rel.h:190

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

Referenced by do_analyze_rel(), heap_vacuum_rel(), and parallel_vacuum_main().

◆ vac_tid_reaped()

static bool vac_tid_reaped ( ItemPointer  itemptr,
void *  state 
)
static

Definition at line 2398 of file vacuum.c.

2399 {
2400  VacDeadItems *dead_items = (VacDeadItems *) state;
2401  int64 litem,
2402  ritem,
2403  item;
2404  ItemPointer res;
2405 
2406  litem = itemptr_encode(&dead_items->items[0]);
2407  ritem = itemptr_encode(&dead_items->items[dead_items->num_items - 1]);
2408  item = itemptr_encode(itemptr);
2409 
2410  /*
2411  * Doing a simple bound check before bsearch() is useful to avoid the
2412  * extra cost of bsearch(), especially if dead items on the heap are
2413  * concentrated in a certain range. Since this function is called for
2414  * every index tuple, it pays to be really fast.
2415  */
2416  if (item < litem || item > ritem)
2417  return false;
2418 
2419  res = (ItemPointer) bsearch(itemptr,
2420  dead_items->items,
2421  dead_items->num_items,
2422  sizeof(ItemPointerData),
2423  vac_cmp_itemptr);
2424 
2425  return (res != NULL);
2426 }
static int64 itemptr_encode(ItemPointer itemptr)
Definition: index.h:185
ItemPointerData * ItemPointer
Definition: itemptr.h:49
ItemPointerData items[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuum.h:289
Definition: regguts.h:318
static int vac_cmp_itemptr(const void *left, const void *right)
Definition: vacuum.c:2432

References itemptr_encode(), VacDeadItems::items, VacDeadItems::num_items, res, and vac_cmp_itemptr().

Referenced by vac_bulkdel_one_index().

◆ vac_truncate_clog()

static void vac_truncate_clog ( TransactionId  frozenXID,
MultiXactId  minMulti,
TransactionId  lastSaneFrozenXid,
MultiXactId  lastSaneMinMulti 
)
static

Definition at line 1690 of file vacuum.c.

1694 {
1696  Relation relation;
1697  TableScanDesc scan;
1698  HeapTuple tuple;
1699  Oid oldestxid_datoid;
1700  Oid minmulti_datoid;
1701  bool bogus = false;
1702  bool frozenAlreadyWrapped = false;
1703 
1704  /* Restrict task to one backend per cluster; see SimpleLruTruncate(). */
1705  LWLockAcquire(WrapLimitsVacuumLock, LW_EXCLUSIVE);
1706 
1707  /* init oldest datoids to sync with my frozenXID/minMulti values */
1708  oldestxid_datoid = MyDatabaseId;
1709  minmulti_datoid = MyDatabaseId;
1710 
1711  /*
1712  * Scan pg_database to compute the minimum datfrozenxid/datminmxid
1713  *
1714  * Since vac_update_datfrozenxid updates datfrozenxid/datminmxid in-place,
1715  * the values could change while we look at them. Fetch each one just
1716  * once to ensure sane behavior of the comparison logic. (Here, as in
1717  * many other places, we assume that fetching or updating an XID in shared
1718  * storage is atomic.)
1719  *
1720  * Note: we need not worry about a race condition with new entries being
1721  * inserted by CREATE DATABASE. Any such entry will have a copy of some
1722  * existing DB's datfrozenxid, and that source DB cannot be ours because
1723  * of the interlock against copying a DB containing an active backend.
1724  * Hence the new entry will not reduce the minimum. Also, if two VACUUMs
1725  * concurrently modify the datfrozenxid's of different databases, the
1726  * worst possible outcome is that pg_xact is not truncated as aggressively
1727  * as it could be.
1728  */
1729  relation = table_open(DatabaseRelationId, AccessShareLock);
1730 
1731  scan = table_beginscan_catalog(relation, 0, NULL);
1732 
1733  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1734  {
1735  volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
1736  TransactionId datfrozenxid = dbform->datfrozenxid;
1737  TransactionId datminmxid = dbform->datminmxid;
1738 
1741 
1742  /*
1743  * If things are working properly, no database should have a
1744  * datfrozenxid or datminmxid that is "in the future". However, such
1745  * cases have been known to arise due to bugs in pg_upgrade. If we
1746  * see any entries that are "in the future", chicken out and don't do
1747  * anything. This ensures we won't truncate clog before those
1748  * databases have been scanned and cleaned up. (We will issue the
1749  * "already wrapped" warning if appropriate, though.)
1750  */
1751  if (TransactionIdPrecedes(lastSaneFrozenXid, datfrozenxid) ||
1752  MultiXactIdPrecedes(lastSaneMinMulti, datminmxid))
1753  bogus = true;
1754 
1755  if (TransactionIdPrecedes(nextXID, datfrozenxid))
1756  frozenAlreadyWrapped = true;
1757  else if (TransactionIdPrecedes(datfrozenxid, frozenXID))
1758  {
1759  frozenXID = datfrozenxid;
1760  oldestxid_datoid = dbform->oid;
1761  }
1762 
1763  if (MultiXactIdPrecedes(datminmxid, minMulti))
1764  {
1765  minMulti = datminmxid;
1766  minmulti_datoid = dbform->oid;
1767  }
1768  }
1769 
1770  table_endscan(scan);
1771 
1772  table_close(relation, AccessShareLock);
1773 
1774  /*
1775  * Do not truncate CLOG if we seem to have suffered wraparound already;
1776  * the computed minimum XID might be bogus. This case should now be
1777  * impossible due to the defenses in GetNewTransactionId, but we keep the
1778  * test anyway.
1779  */
1780  if (frozenAlreadyWrapped)
1781  {
1782  ereport(WARNING,
1783  (errmsg("some databases have not been vacuumed in over 2 billion transactions"),
1784  errdetail("You might have already suffered transaction-wraparound data loss.")));
1785  return;
1786  }
1787 
1788  /* chicken out if data is bogus in any other way */
1789  if (bogus)
1790  return;
1791 
1792  /*
1793  * Advance the oldest value for commit timestamps before truncating, so
1794  * that if a user requests a timestamp for a transaction we're truncating
1795  * away right after this point, they get NULL instead of an ugly "file not
1796  * found" error from slru.c. This doesn't matter for xact/multixact
1797  * because they are not subject to arbitrary lookups from users.
1798  */
1799  AdvanceOldestCommitTsXid(frozenXID);
1800 
1801  /*
1802  * Truncate CLOG, multixact and CommitTs to the oldest computed value.
1803  */
1804  TruncateCLOG(frozenXID, oldestxid_datoid);
1805  TruncateCommitTs(frozenXID);
1806  TruncateMultiXact(minMulti, minmulti_datoid);
1807 
1808  /*
1809  * Update the wrap limit for GetNewTransactionId and creation of new
1810  * MultiXactIds. Note: these functions will also signal the postmaster
1811  * for an(other) autovac cycle if needed. XXX should we avoid possibly
1812  * signaling twice?
1813  */
1814  SetTransactionIdLimit(frozenXID, oldestxid_datoid);
1815  SetMultiXactIdLimit(minMulti, minmulti_datoid, false);
1816 
1817  LWLockRelease(WrapLimitsVacuumLock);
1818 }
uint32 TransactionId
Definition: c.h:636
void TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
Definition: clog.c:878
void AdvanceOldestCommitTsXid(TransactionId oldestXact)
Definition: commit_ts.c:887
void TruncateCommitTs(TransactionId oldestXact)
Definition: commit_ts.c:834
Oid MyDatabaseId
Definition: globals.c:89
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1195
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1803
@ LW_EXCLUSIVE
Definition: lwlock.h:115
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3156
void SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid, bool is_startup)
Definition: multixact.c:2213
void TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
Definition: multixact.c:2941
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
TransactionId datfrozenxid
Definition: pg_database.h:56
TransactionId datminmxid
Definition: pg_database.h:59
FormData_pg_database * Form_pg_database
Definition: pg_database.h:90
FormData_pg_database
Definition: pg_database.h:83
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
static TransactionId ReadNextTransactionId(void)
Definition: transam.h:315
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
void SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
Definition: varsup.c:345

References AccessShareLock, AdvanceOldestCommitTsXid(), Assert(), datfrozenxid, datminmxid, ereport, errdetail(), errmsg(), FormData_pg_database, ForwardScanDirection, GETSTRUCT, heap_getnext(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MultiXactIdPrecedes(), MyDatabaseId, ReadNextTransactionId(), SetMultiXactIdLimit(), SetTransactionIdLimit(), table_beginscan_catalog(), table_close(), table_endscan(), table_open(), TransactionIdIsNormal, TransactionIdPrecedes(), TruncateCLOG(), TruncateCommitTs(), TruncateMultiXact(), and WARNING.

Referenced by vac_update_datfrozenxid().

◆ vac_update_datfrozenxid()

void vac_update_datfrozenxid ( void  )

Definition at line 1476 of file vacuum.c.

1477 {
1478  HeapTuple tuple;
1479  Form_pg_database dbform;
1480  Relation relation;
1481  SysScanDesc scan;
1482  HeapTuple classTup;
1483  TransactionId newFrozenXid;
1484  MultiXactId newMinMulti;
1485  TransactionId lastSaneFrozenXid;
1486  MultiXactId lastSaneMinMulti;
1487  bool bogus = false;
1488  bool dirty = false;
1489  ScanKeyData key[1];
1490 
1491  /*
1492  * Restrict this task to one backend per database. This avoids race
1493  * conditions that would move datfrozenxid or datminmxid backward. It
1494  * avoids calling vac_truncate_clog() with a datfrozenxid preceding a
1495  * datfrozenxid passed to an earlier vac_truncate_clog() call.
1496  */
1498 
1499  /*
1500  * Initialize the "min" calculation with
1501  * GetOldestNonRemovableTransactionId(), which is a reasonable
1502  * approximation to the minimum relfrozenxid for not-yet-committed
1503  * pg_class entries for new tables; see AddNewRelationTuple(). So we
1504  * cannot produce a wrong minimum by starting with this.
1505  */
1506  newFrozenXid = GetOldestNonRemovableTransactionId(NULL);
1507 
1508  /*
1509  * Similarly, initialize the MultiXact "min" with the value that would be
1510  * used on pg_class for new tables. See AddNewRelationTuple().
1511  */
1512  newMinMulti = GetOldestMultiXactId();
1513 
1514  /*
1515  * Identify the latest relfrozenxid and relminmxid values that we could
1516  * validly see during the scan. These are conservative values, but it's
1517  * not really worth trying to be more exact.
1518  */
1519  lastSaneFrozenXid = ReadNextTransactionId();
1520  lastSaneMinMulti = ReadNextMultiXactId();
1521 
1522  /*
1523  * We must seqscan pg_class to find the minimum Xid, because there is no
1524  * index that can help us here.
1525  */
1526  relation = table_open(RelationRelationId, AccessShareLock);
1527 
1528  scan = systable_beginscan(relation, InvalidOid, false,
1529  NULL, 0, NULL);
1530 
1531  while ((classTup = systable_getnext(scan)) != NULL)
1532  {
1533  Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
1534 
1535  /*
1536  * Only consider relations able to hold unfrozen XIDs (anything else
1537  * should have InvalidTransactionId in relfrozenxid anyway).
1538  */
1539  if (classForm->relkind != RELKIND_RELATION &&
1540  classForm->relkind != RELKIND_MATVIEW &&
1541  classForm->relkind != RELKIND_TOASTVALUE)
1542  {
1543  Assert(!TransactionIdIsValid(classForm->relfrozenxid));
1544  Assert(!MultiXactIdIsValid(classForm->relminmxid));
1545  continue;
1546  }
1547 
1548  /*
1549  * Some table AMs might not need per-relation xid / multixid horizons.
1550  * It therefore seems reasonable to allow relfrozenxid and relminmxid
1551  * to not be set (i.e. set to their respective Invalid*Id)
1552  * independently. Thus validate and compute horizon for each only if
1553  * set.
1554  *
1555  * If things are working properly, no relation should have a
1556  * relfrozenxid or relminmxid that is "in the future". However, such
1557  * cases have been known to arise due to bugs in pg_upgrade. If we
1558  * see any entries that are "in the future", chicken out and don't do
1559  * anything. This ensures we won't truncate clog & multixact SLRUs
1560  * before those relations have been scanned and cleaned up.
1561  */
1562 
1563  if (TransactionIdIsValid(classForm->relfrozenxid))
1564  {
1565  Assert(TransactionIdIsNormal(classForm->relfrozenxid));
1566 
1567  /* check for values in the future */
1568  if (TransactionIdPrecedes(lastSaneFrozenXid, classForm->relfrozenxid))
1569  {
1570  bogus = true;
1571  break;
1572  }
1573 
1574  /* determine new horizon */
1575  if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
1576  newFrozenXid = classForm->relfrozenxid;
1577  }
1578 
1579  if (MultiXactIdIsValid(classForm->relminmxid))
1580  {
1581  /* check for values in the future */
1582  if (MultiXactIdPrecedes(lastSaneMinMulti, classForm->relminmxid))
1583  {
1584  bogus = true;
1585  break;
1586  }
1587 
1588  /* determine new horizon */
1589  if (MultiXactIdPrecedes(classForm->relminmxid, newMinMulti))
1590  newMinMulti = classForm->relminmxid;
1591  }
1592  }
1593 
1594  /* we're done with pg_class */
1595  systable_endscan(scan);
1596  table_close(relation, AccessShareLock);
1597 
1598  /* chicken out if bogus data found */
1599  if (bogus)
1600  return;
1601 
1602  Assert(TransactionIdIsNormal(newFrozenXid));
1603  Assert(MultiXactIdIsValid(newMinMulti));
1604 
1605  /* Now fetch the pg_database tuple we need to update. */
1606  relation = table_open(DatabaseRelationId, RowExclusiveLock);
1607 
1608  /*
1609  * Get the pg_database tuple to scribble on. Note that this does not
1610  * directly rely on the syscache to avoid issues with flattened toast
1611  * values for the in-place update.
1612  */
1613  ScanKeyInit(&key[0],
1614  Anum_pg_database_oid,
1615  BTEqualStrategyNumber, F_OIDEQ,
1617 
1618  scan = systable_beginscan(relation, DatabaseOidIndexId, true,
1619  NULL, 1, key);
1620  tuple = systable_getnext(scan);
1621  tuple = heap_copytuple(tuple);
1622  systable_endscan(scan);
1623 
1624  if (!HeapTupleIsValid(tuple))
1625  elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
1626 
1627  dbform = (Form_pg_database) GETSTRUCT(tuple);
1628 
1629  /*
1630  * As in vac_update_relstats(), we ordinarily don't want to let
1631  * datfrozenxid go backward; but if it's "in the future" then it must be
1632  * corrupt and it seems best to overwrite it.
1633  */
1634  if (dbform->datfrozenxid != newFrozenXid &&
1635  (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid) ||
1636  TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid)))
1637  {
1638  dbform->datfrozenxid = newFrozenXid;
1639  dirty = true;
1640  }
1641  else
1642  newFrozenXid = dbform->datfrozenxid;
1643 
1644  /* Ditto for datminmxid */
1645  if (dbform->datminmxid != newMinMulti &&
1646  (MultiXactIdPrecedes(dbform->datminmxid, newMinMulti) ||
1647  MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid)))
1648  {
1649  dbform->datminmxid = newMinMulti;
1650  dirty = true;
1651  }
1652  else
1653  newMinMulti = dbform->datminmxid;
1654 
1655  if (dirty)
1656  heap_inplace_update(relation, tuple);
1657 
1658  heap_freetuple(tuple);
1659  table_close(relation, RowExclusiveLock);
1660 
1661  /*
1662  * If we were able to advance datfrozenxid or datminmxid, see if we can
1663  * truncate pg_xact and/or pg_multixact. Also do it if the shared
1664  * XID-wrap-limit info is stale, since this action will update that too.
1665  */
1666  if (dirty || ForceTransactionIdLimitUpdate())
1667  vac_truncate_clog(newFrozenXid, newMinMulti,
1668  lastSaneFrozenXid, lastSaneMinMulti);
1669 }
TransactionId MultiXactId
Definition: c.h:646
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:599
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:506
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
void heap_inplace_update(Relation relation, HeapTuple tuple)
Definition: heapam.c:5831
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
void LockDatabaseFrozenIds(LOCKMODE lockmode)
Definition: lmgr.c:498
#define ExclusiveLock
Definition: lockdefs.h:42
#define RowExclusiveLock
Definition: lockdefs.h:38
MultiXactId GetOldestMultiXactId(void)
Definition: multixact.c:2507
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:724
#define InvalidOid
Definition: postgres_ext.h:36
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:2034
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static void vac_truncate_clog(TransactionId frozenXID, MultiXactId minMulti, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti)
Definition: vacuum.c:1690
bool ForceTransactionIdLimitUpdate(void)
Definition: varsup.c:490

References AccessShareLock, Assert(), BTEqualStrategyNumber, elog(), ERROR, ExclusiveLock, ForceTransactionIdLimitUpdate(), GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), GETSTRUCT, heap_copytuple(), heap_freetuple(), heap_inplace_update(), HeapTupleIsValid, InvalidOid, sort-test::key, LockDatabaseFrozenIds(), MultiXactIdIsValid, MultiXactIdPrecedes(), MyDatabaseId, ObjectIdGetDatum(), ReadNextMultiXactId(), ReadNextTransactionId(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TransactionIdIsNormal, TransactionIdIsValid, TransactionIdPrecedes(), and vac_truncate_clog().

Referenced by do_autovacuum(), and vacuum().

◆ vac_update_relstats()

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

Definition at line 1309 of file vacuum.c.

1316 {
1317  Oid relid = RelationGetRelid(relation);
1318  Relation rd;
1319  HeapTuple ctup;
1320  Form_pg_class pgcform;
1321  bool dirty,
1322  futurexid,
1323  futuremxid;
1324  TransactionId oldfrozenxid;
1325  MultiXactId oldminmulti;
1326 
1327  rd = table_open(RelationRelationId, RowExclusiveLock);
1328 
1329  /* Fetch a copy of the tuple to scribble on */
1331  if (!HeapTupleIsValid(ctup))
1332  elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
1333  relid);
1334  pgcform = (Form_pg_class) GETSTRUCT(ctup);
1335 
1336  /* Apply statistical updates, if any, to copied tuple */
1337 
1338  dirty = false;
1339  if (pgcform->relpages != (int32) num_pages)
1340  {
1341  pgcform->relpages = (int32) num_pages;
1342  dirty = true;
1343  }
1344  if (pgcform->reltuples != (float4) num_tuples)
1345  {
1346  pgcform->reltuples = (float4) num_tuples;
1347  dirty = true;
1348  }
1349  if (pgcform->relallvisible != (int32) num_all_visible_pages)
1350  {
1351  pgcform->relallvisible = (int32) num_all_visible_pages;
1352  dirty = true;
1353  }
1354 
1355  /* Apply DDL updates, but not inside an outer transaction (see above) */
1356 
1357  if (!in_outer_xact)
1358  {
1359  /*
1360  * If we didn't find any indexes, reset relhasindex.
1361  */
1362  if (pgcform->relhasindex && !hasindex)
1363  {
1364  pgcform->relhasindex = false;
1365  dirty = true;
1366  }
1367 
1368  /* We also clear relhasrules and relhastriggers if needed */
1369  if (pgcform->relhasrules && relation->rd_rules == NULL)
1370  {
1371  pgcform->relhasrules = false;
1372  dirty = true;
1373  }
1374  if (pgcform->relhastriggers && relation->trigdesc == NULL)
1375  {
1376  pgcform->relhastriggers = false;
1377  dirty = true;
1378  }
1379  }
1380 
1381  /*
1382  * Update relfrozenxid, unless caller passed InvalidTransactionId
1383  * indicating it has no new data.
1384  *
1385  * Ordinarily, we don't let relfrozenxid go backwards. However, if the
1386  * stored relfrozenxid is "in the future" then it seems best to assume
1387  * it's corrupt, and overwrite with the oldest remaining XID in the table.
1388  * This should match vac_update_datfrozenxid() concerning what we consider
1389  * to be "in the future".
1390  */
1391  oldfrozenxid = pgcform->relfrozenxid;
1392  futurexid = false;
1393  if (frozenxid_updated)
1394  *frozenxid_updated = false;
1395  if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
1396  {
1397  bool update = false;
1398 
1399  if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
1400  update = true;
1401  else if (TransactionIdPrecedes(ReadNextTransactionId(), oldfrozenxid))
1402  futurexid = update = true;
1403 
1404  if (update)
1405  {
1406  pgcform->relfrozenxid = frozenxid;
1407  dirty = true;
1408  if (frozenxid_updated)
1409  *frozenxid_updated = true;
1410  }
1411  }
1412 
1413  /* Similarly for relminmxid */
1414  oldminmulti = pgcform->relminmxid;
1415  futuremxid = false;
1416  if (minmulti_updated)
1417  *minmulti_updated = false;
1418  if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
1419  {
1420  bool update = false;
1421 
1422  if (MultiXactIdPrecedes(oldminmulti, minmulti))
1423  update = true;
1424  else if (MultiXactIdPrecedes(ReadNextMultiXactId(), oldminmulti))
1425  futuremxid = update = true;
1426 
1427  if (update)
1428  {
1429  pgcform->relminmxid = minmulti;
1430  dirty = true;
1431  if (minmulti_updated)
1432  *minmulti_updated = true;
1433  }
1434  }
1435 
1436  /* If anything changed, write out the tuple. */
1437  if (dirty)
1438  heap_inplace_update(rd, ctup);
1439 
1441 
1442  if (futurexid)
1443  ereport(WARNING,
1445  errmsg_internal("overwrote invalid relfrozenxid value %u with new value %u for table \"%s\"",
1446  oldfrozenxid, frozenxid,
1447  RelationGetRelationName(relation))));
1448  if (futuremxid)
1449  ereport(WARNING,
1451  errmsg_internal("overwrote invalid relminmxid value %u with new value %u for table \"%s\"",
1452  oldminmulti, minmulti,
1453  RelationGetRelationName(relation))));
1454 }
signed int int32
Definition: c.h:478
float float4
Definition: c.h:613
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1156
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:41
#define RelationGetRelid(relation)
Definition: rel.h:503
TriggerDesc * trigdesc
Definition: rel.h:116
RuleLock * rd_rules
Definition: rel.h:114
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:182

References elog(), ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, GETSTRUCT, heap_inplace_update(), HeapTupleIsValid, MultiXactIdIsValid, MultiXactIdPrecedes(), ObjectIdGetDatum(), RelationData::rd_rules, ReadNextMultiXactId(), ReadNextTransactionId(), RelationGetRelationName, RelationGetRelid, RELOID, RowExclusiveLock, SearchSysCacheCopy1, table_close(), table_open(), TransactionIdIsNormal, TransactionIdPrecedes(), RelationData::trigdesc, and WARNING.

Referenced by do_analyze_rel(), heap_vacuum_rel(), and update_relstats_all_indexes().

◆ vacuum()

void vacuum ( List relations,
VacuumParams params,
BufferAccessStrategy  bstrategy,
bool  isTopLevel 
)

Definition at line 311 of file vacuum.c.

313 {
314  static bool in_vacuum = false;
315 
316  const char *stmttype;
317  volatile bool in_outer_xact,
318  use_own_xacts;
319 
320  Assert(params != NULL);
321 
322  stmttype = (params->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
323 
324  /*
325  * We cannot run VACUUM inside a user transaction block; if we were inside
326  * a transaction, then our commit- and start-transaction-command calls
327  * would not have the intended effect! There are numerous other subtle
328  * dependencies on this, too.
329  *
330  * ANALYZE (without VACUUM) can run either way.
331  */
332  if (params->options & VACOPT_VACUUM)
333  {
334  PreventInTransactionBlock(isTopLevel, stmttype);
335  in_outer_xact = false;
336  }
337  else
338  in_outer_xact = IsInTransactionBlock(isTopLevel);
339 
340  /*
341  * Due to static variables vac_context, anl_context and vac_strategy,
342  * vacuum() is not reentrant. This matters when VACUUM FULL or ANALYZE
343  * calls a hostile index expression that itself calls ANALYZE.
344  */
345  if (in_vacuum)
346  ereport(ERROR,
347  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
348  errmsg("%s cannot be executed from VACUUM or ANALYZE",
349  stmttype)));
350 
351  /*
352  * Sanity check DISABLE_PAGE_SKIPPING option.
353  */
354  if ((params->options & VACOPT_FULL) != 0 &&
355  (params->options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
356  ereport(ERROR,
357  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
358  errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
359 
360  /* sanity check for PROCESS_TOAST */
361  if ((params->options & VACOPT_FULL) != 0 &&
362  (params->options & VACOPT_PROCESS_TOAST) == 0)
363  ereport(ERROR,
364  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
365  errmsg("PROCESS_TOAST required with VACUUM FULL")));
366 
367  /* sanity check for ONLY_DATABASE_STATS */
368  if (params->options & VACOPT_ONLY_DATABASE_STATS)
369  {
370  Assert(params->options & VACOPT_VACUUM);
371  if (relations != NIL)
372  ereport(ERROR,
373  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
374  errmsg("ONLY_DATABASE_STATS cannot be specified with a list of tables")));
375  /* don't require people to turn off PROCESS_TOAST/MAIN explicitly */
376  if (params->options & ~(VACOPT_VACUUM |
381  ereport(ERROR,
382  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
383  errmsg("ONLY_DATABASE_STATS cannot be specified with other VACUUM options")));
384  }
385 
386  /*
387  * Create special memory context for cross-transaction storage.
388  *
389  * Since it is a child of PortalContext, it will go away eventually even
390  * if we suffer an error; there's no need for special abort cleanup logic.
391  */
393  "Vacuum",
395 
396  /*
397  * If caller didn't give us a buffer strategy object, make one in the
398  * cross-transaction memory context.
399  */
400  if (bstrategy == NULL)
401  {
403 
404  bstrategy = GetAccessStrategy(BAS_VACUUM);
405  MemoryContextSwitchTo(old_context);
406  }
407  vac_strategy = bstrategy;
408 
409  /*
410  * Build list of relation(s) to process, putting any new data in
411  * vac_context for safekeeping.
412  */
413  if (params->options & VACOPT_ONLY_DATABASE_STATS)
414  {
415  /* We don't process any tables in this case */
416  Assert(relations == NIL);
417  }
418  else if (relations != NIL)
419  {
420  List *newrels = NIL;
421  ListCell *lc;
422 
423  foreach(lc, relations)
424  {
426  List *sublist;
427  MemoryContext old_context;
428 
429  sublist = expand_vacuum_rel(vrel, params->options);
430  old_context = MemoryContextSwitchTo(vac_context);
431  newrels = list_concat(newrels, sublist);
432  MemoryContextSwitchTo(old_context);
433  }
434  relations = newrels;
435  }
436  else
437  relations = get_all_vacuum_rels(params->options);
438 
439  /*
440  * Decide whether we need to start/commit our own transactions.
441  *
442  * For VACUUM (with or without ANALYZE): always do so, so that we can
443  * release locks as soon as possible. (We could possibly use the outer
444  * transaction for a one-table VACUUM, but handling TOAST tables would be
445  * problematic.)
446  *
447  * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
448  * start/commit our own transactions. Also, there's no need to do so if
449  * only processing one relation. For multiple relations when not within a
450  * transaction block, and also in an autovacuum worker, use own
451  * transactions so we can release locks sooner.
452  */
453  if (params->options & VACOPT_VACUUM)
454  use_own_xacts = true;
455  else
456  {
457  Assert(params->options & VACOPT_ANALYZE);
459  use_own_xacts = true;
460  else if (in_outer_xact)
461  use_own_xacts = false;
462  else if (list_length(relations) > 1)
463  use_own_xacts = true;
464  else
465  use_own_xacts = false;
466  }
467 
468  /*
469  * vacuum_rel expects to be entered with no transaction active; it will
470  * start and commit its own transaction. But we are called by an SQL
471  * command, and so we are executing inside a transaction already. We
472  * commit the transaction started in PostgresMain() here, and start
473  * another one before exiting to match the commit waiting for us back in
474  * PostgresMain().
475  */
476  if (use_own_xacts)
477  {
478  Assert(!in_outer_xact);
479 
480  /* ActiveSnapshot is not set by autovacuum */
481  if (ActiveSnapshotSet())
483 
484  /* matches the StartTransaction in PostgresMain() */
486  }
487 
488  /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
489  PG_TRY();
490  {
491  ListCell *cur;
492 
493  in_vacuum = true;
495  VacuumCostBalance = 0;
496  VacuumPageHit = 0;
497  VacuumPageMiss = 0;
498  VacuumPageDirty = 0;
501  VacuumActiveNWorkers = NULL;
502 
503  /*
504  * Loop to process each selected relation.
505  */
506  foreach(cur, relations)
507  {
509 
510  if (params->options & VACOPT_VACUUM)
511  {
512  if (!vacuum_rel(vrel->oid, vrel->relation, params, false))
513  continue;
514  }
515 
516  if (params->options & VACOPT_ANALYZE)
517  {
518  /*
519  * If using separate xacts, start one for analyze. Otherwise,
520  * we can use the outer transaction.
521  */
522  if (use_own_xacts)
523  {
525  /* functions in indexes may want a snapshot set */
527  }
528 
529  analyze_rel(vrel->oid, vrel->relation, params,
530  vrel->va_cols, in_outer_xact, vac_strategy);
531 
532  if (use_own_xacts)
533  {
536  }
537  else
538  {
539  /*
540  * If we're not using separate xacts, better separate the
541  * ANALYZE actions with CCIs. This avoids trouble if user
542  * says "ANALYZE t, t".
543  */
545  }
546  }
547  }
548  }
549  PG_FINALLY();
550  {
551  in_vacuum = false;
552  VacuumCostActive = false;
553  }
554  PG_END_TRY();
555 
556  /*
557  * Finish up processing.
558  */
559  if (use_own_xacts)
560  {
561  /* here, we are not in a transaction */
562 
563  /*
564  * This matches the CommitTransaction waiting for us in
565  * PostgresMain().
566  */
568  }
569 
570  if ((params->options & VACOPT_VACUUM) &&
571  !(params->options & VACOPT_SKIP_DATABASE_STATS))
572  {
573  /*
574  * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
575  */
577  }
578 
579  /*
580  * Clean up working storage --- note we must do this after
581  * StartTransactionCommand, else we might be trying to delete the active
582  * context!
583  */
585  vac_context = NULL;
586 }
@ BAS_VACUUM
Definition: bufmgr.h:38
void analyze_rel(Oid relid, RangeVar *relation, VacuumParams *params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
Definition: analyze.c:121
struct cursor * cur
Definition: ecpg.c:28
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define PG_FINALLY(...)
Definition: elog.h:387
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:541
int64 VacuumPageHit
Definition: globals.c:148
int64 VacuumPageMiss
Definition: globals.c:149
bool VacuumCostActive
Definition: globals.c:153
int64 VacuumPageDirty
Definition: globals.c:150
List * list_concat(List *list1, const List *list2)
Definition: list.c:560
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387
MemoryContext PortalContext
Definition: mcxt.c:150
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:251
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:683
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:817
void PopActiveSnapshot(void)
Definition: snapmgr.c:778
static List * get_all_vacuum_rels(int options)
Definition: vacuum.c:902
static List * expand_vacuum_rel(VacuumRelation *vrel, int options)
Definition: vacuum.c:763
static BufferAccessStrategy vac_strategy
Definition: vacuum.c:78
void vac_update_datfrozenxid(void)
Definition: vacuum.c:1476
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, bool skip_privs)
Definition: vacuum.c:1841
bool IsInTransactionBlock(bool isTopLevel)
Definition: xact.c:3613
void CommandCounterIncrement(void)
Definition: xact.c:1078
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3482
void StartTransactionCommand(void)
Definition: xact.c:2938
void CommitTransactionCommand(void)
Definition: xact.c:3035

References ActiveSnapshotSet(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, analyze_rel(), Assert(), BAS_VACUUM, CommandCounterIncrement(), CommitTransactionCommand(), cur, ereport, errcode(), errmsg(), ERROR, expand_vacuum_rel(), get_all_vacuum_rels(), GetAccessStrategy(), GetTransactionSnapshot(), IsAutoVacuumWorkerProcess(), IsInTransactionBlock(), lfirst_node, list_concat(), list_length(), MemoryContextDelete(), MemoryContextSwitchTo(), NIL, VacuumRelation::oid, VacuumParams::options, PG_END_TRY, PG_FINALLY, PG_TRY, PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), PushActiveSnapshot(), VacuumRelation::relation, StartTransactionCommand(), VacuumRelation::va_cols, vac_context, vac_strategy, vac_update_datfrozenxid(), VACOPT_ANALYZE, VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_FULL, VACOPT_ONLY_DATABASE_STATS, VACOPT_PROCESS_MAIN, VACOPT_PROCESS_TOAST, VACOPT_SKIP_DATABASE_STATS, VACOPT_VACUUM, VACOPT_VERBOSE, vacuum_rel(), VacuumActiveNWorkers, VacuumCostActive, VacuumCostBalance, VacuumCostBalanceLocal, VacuumCostDelay, VacuumPageDirty, VacuumPageHit, VacuumPageMiss, and VacuumSharedCostBalance.

Referenced by autovacuum_do_vac_analyze(), ExecVacuum(), parallel_vacuum_index_is_parallel_safe(), and parallel_vacuum_process_all_indexes().

◆ vacuum_delay_point()

void vacuum_delay_point ( void  )

Definition at line 2211 of file vacuum.c.

2212 {
2213  double msec = 0;
2214 
2215  /* Always check for interrupts */
2217 
2219  return;
2220 
2221  /*
2222  * For parallel vacuum, the delay is computed based on the shared cost
2223  * balance. See compute_parallel_delay.
2224  */
2225  if (VacuumSharedCostBalance != NULL)
2226  msec = compute_parallel_delay();
2227  else if (VacuumCostBalance >= VacuumCostLimit)
2229 
2230  /* Nap if appropriate */
2231  if (msec > 0)
2232  {
2233  if (msec > VacuumCostDelay * 4)
2234  msec = VacuumCostDelay * 4;
2235 
2237  pg_usleep(msec * 1000);
2239 
2240  /*
2241  * We don't want to ignore postmaster death during very long vacuums
2242  * with vacuum_cost_delay configured. We can't use the usual
2243  * WaitLatch() approach here because we want microsecond-based sleep
2244  * durations above.
2245  */
2247  exit(1);
2248 
2249  VacuumCostBalance = 0;
2250 
2251  /* update balance values for workers */
2253 
2254  /* Might have gotten an interrupt while sleeping */
2256  }
2257 }
void AutoVacuumUpdateDelay(void)
Definition: autovacuum.c:1781
volatile sig_atomic_t InterruptPending
Definition: globals.c:30
bool IsUnderPostmaster
Definition: globals.c:113
exit(1)
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
#define PostmasterIsAlive()
Definition: pmsignal.h:102
void pg_usleep(long microsec)
Definition: signal.c:53
static double compute_parallel_delay(void)
Definition: vacuum.c:2282
@ WAIT_EVENT_VACUUM_DELAY
Definition: wait_event.h:152
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:271
static void pgstat_report_wait_end(void)
Definition: wait_event.h:287

References AutoVacuumUpdateDelay(), CHECK_FOR_INTERRUPTS, compute_parallel_delay(), exit(), InterruptPending, IsUnderPostmaster, pg_usleep(), pgstat_report_wait_end(), pgstat_report_wait_start(), PostmasterIsAlive, VacuumCostActive, VacuumCostBalance, VacuumCostDelay, VacuumCostLimit, VacuumSharedCostBalance, and WAIT_EVENT_VACUUM_DELAY.

Referenced by acquire_sample_rows(), blbulkdelete(), blvacuumcleanup(), btvacuumpage(), compute_array_stats(), compute_distinct_stats(), compute_index_stats(), compute_range_stats(), compute_scalar_stats(), compute_trivial_stats(), compute_tsvector_stats(), file_acquire_sample_rows(), ginbulkdelete(), ginInsertCleanup(), ginvacuumcleanup(), gistvacuumpage(), hashbucketcleanup(), lazy_scan_heap(), lazy_scan_skip(), lazy_vacuum_heap_rel(), spgprocesspending(), and spgvacuumpage().

◆ vacuum_get_cutoffs()

bool vacuum_get_cutoffs ( Relation  rel,
const VacuumParams params,
struct VacuumCutoffs cutoffs 
)

Definition at line 964 of file vacuum.c.

966 {
967  int freeze_min_age,
968  multixact_freeze_min_age,
969  freeze_table_age,
970  multixact_freeze_table_age,
971  effective_multixact_freeze_max_age;
972  TransactionId nextXID,
973  safeOldestXmin,
974  aggressiveXIDCutoff;
975  MultiXactId nextMXID,
976  safeOldestMxact,
977  aggressiveMXIDCutoff;
978 
979  /* Use mutable copies of freeze age parameters */
980  freeze_min_age = params->freeze_min_age;
981  multixact_freeze_min_age = params->multixact_freeze_min_age;
982  freeze_table_age = params->freeze_table_age;
983  multixact_freeze_table_age = params->multixact_freeze_table_age;
984 
985  /* Set pg_class fields in cutoffs */
986  cutoffs->relfrozenxid = rel->rd_rel->relfrozenxid;
987  cutoffs->relminmxid = rel->rd_rel->relminmxid;
988 
989  /*
990  * Acquire OldestXmin.
991  *
992  * We can always ignore processes running lazy vacuum. This is because we
993  * use these values only for deciding which tuples we must keep in the
994  * tables. Since lazy vacuum doesn't write its XID anywhere (usually no
995  * XID assigned), it's safe to ignore it. In theory it could be
996  * problematic to ignore lazy vacuums in a full vacuum, but keep in mind
997  * that only one vacuum process can be working on a particular table at
998  * any time, and that each vacuum is always an independent transaction.
999  */
1001 
1003  {
1004  TransactionId limit_xmin;
1005  TimestampTz limit_ts;
1006 
1008  &limit_xmin, &limit_ts))
1009  {
1010  /*
1011  * TODO: We should only set the threshold if we are pruning on the
1012  * basis of the increased limits. Not as crucial here as it is
1013  * for opportunistic pruning (which often happens at a much higher
1014  * frequency), but would still be a significant improvement.
1015  */
1016  SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin);
1017  cutoffs->OldestXmin = limit_xmin;
1018  }
1019  }
1020 
1022 
1023  /* Acquire OldestMxact */
1024  cutoffs->OldestMxact = GetOldestMultiXactId();
1026 
1027  /* Acquire next XID/next MXID values used to apply age-based settings */
1028  nextXID = ReadNextTransactionId();
1029  nextMXID = ReadNextMultiXactId();
1030 
1031  /*
1032  * Also compute the multixact age for which freezing is urgent. This is
1033  * normally autovacuum_multixact_freeze_max_age, but may be less if we are
1034  * short of multixact member space.
1035  */
1036  effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
1037 
1038  /*
1039  * Almost ready to set freeze output parameters; check if OldestXmin or
1040  * OldestMxact are held back to an unsafe degree before we start on that
1041  */
1042  safeOldestXmin = nextXID - autovacuum_freeze_max_age;
1043  if (!TransactionIdIsNormal(safeOldestXmin))
1044  safeOldestXmin = FirstNormalTransactionId;
1045  safeOldestMxact = nextMXID - effective_multixact_freeze_max_age;
1046  if (safeOldestMxact < FirstMultiXactId)
1047  safeOldestMxact = FirstMultiXactId;
1048  if (TransactionIdPrecedes(cutoffs->OldestXmin, safeOldestXmin))
1049  ereport(WARNING,
1050  (errmsg("cutoff for removing and freezing tuples is far in the past"),
1051  errhint("Close open transactions soon to avoid wraparound problems.\n"
1052  "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1053  if (MultiXactIdPrecedes(cutoffs->OldestMxact, safeOldestMxact))
1054  ereport(WARNING,
1055  (errmsg("cutoff for freezing multixacts is far in the past"),
1056  errhint("Close open transactions soon to avoid wraparound problems.\n"
1057  "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1058 
1059  /*
1060  * Determine the minimum freeze age to use: as specified by the caller, or
1061  * vacuum_freeze_min_age, but in any case not more than half
1062  * autovacuum_freeze_max_age, so that autovacuums to prevent XID
1063  * wraparound won't occur too frequently.
1064  */
1065  if (freeze_min_age < 0)
1066  freeze_min_age = vacuum_freeze_min_age;
1067  freeze_min_age = Min(freeze_min_age, autovacuum_freeze_max_age / 2);
1068  Assert(freeze_min_age >= 0);
1069 
1070  /* Compute FreezeLimit, being careful to generate a normal XID */
1071  cutoffs->FreezeLimit = nextXID - freeze_min_age;
1072  if (!TransactionIdIsNormal(cutoffs->FreezeLimit))
1074  /* FreezeLimit must always be <= OldestXmin */
1075  if (TransactionIdPrecedes(cutoffs->OldestXmin, cutoffs->FreezeLimit))
1076  cutoffs->FreezeLimit = cutoffs->OldestXmin;
1077 
1078  /*
1079  * Determine the minimum multixact freeze age to use: as specified by
1080  * caller, or vacuum_multixact_freeze_min_age, but in any case not more
1081  * than half effective_multixact_freeze_max_age, so that autovacuums to
1082  * prevent MultiXact wraparound won't occur too frequently.
1083  */
1084  if (multixact_freeze_min_age < 0)
1085  multixact_freeze_min_age = vacuum_multixact_freeze_min_age;
1086  multixact_freeze_min_age = Min(multixact_freeze_min_age,
1087  effective_multixact_freeze_max_age / 2);
1088  Assert(multixact_freeze_min_age >= 0);
1089 
1090  /* Compute MultiXactCutoff, being careful to generate a valid value */
1091  cutoffs->MultiXactCutoff = nextMXID - multixact_freeze_min_age;
1092  if (cutoffs->MultiXactCutoff < FirstMultiXactId)
1093  cutoffs->MultiXactCutoff = FirstMultiXactId;
1094  /* MultiXactCutoff must always be <= OldestMxact */
1095  if (MultiXactIdPrecedes(cutoffs->OldestMxact, cutoffs->MultiXactCutoff))
1096  cutoffs->MultiXactCutoff = cutoffs->OldestMxact;
1097 
1098  /*
1099  * Finally, figure out if caller needs to do an aggressive VACUUM or not.
1100  *
1101  * Determine the table freeze age to use: as specified by the caller, or
1102  * the value of the vacuum_freeze_table_age GUC, but in any case not more
1103  * than autovacuum_freeze_max_age * 0.95, so that if you have e.g nightly
1104  * VACUUM schedule, the nightly VACUUM gets a chance to freeze XIDs before
1105  * anti-wraparound autovacuum is launched.
1106  */
1107  if (freeze_table_age < 0)
1108  freeze_table_age = vacuum_freeze_table_age;
1109  freeze_table_age = Min(freeze_table_age, autovacuum_freeze_max_age * 0.95);
1110  Assert(freeze_table_age >= 0);
1111  aggressiveXIDCutoff = nextXID - freeze_table_age;
1112  if (!TransactionIdIsNormal(aggressiveXIDCutoff))
1113  aggressiveXIDCutoff = FirstNormalTransactionId;
1114  if (TransactionIdPrecedesOrEquals(rel->rd_rel->relfrozenxid,
1115  aggressiveXIDCutoff))
1116  return true;
1117 
1118  /*
1119  * Similar to the above, determine the table freeze age to use for
1120  * multixacts: as specified by the caller, or the value of the
1121  * vacuum_multixact_freeze_table_age GUC, but in any case not more than
1122  * effective_multixact_freeze_max_age * 0.95, so that if you have e.g.
1123  * nightly VACUUM schedule, the nightly VACUUM gets a chance to freeze
1124  * multixacts before anti-wraparound autovacuum is launched.
1125  */
1126  if (multixact_freeze_table_age < 0)
1127  multixact_freeze_table_age = vacuum_multixact_freeze_table_age;
1128  multixact_freeze_table_age =
1129  Min(multixact_freeze_table_age,
1130  effective_multixact_freeze_max_age * 0.95);
1131  Assert(multixact_freeze_table_age >= 0);
1132  aggressiveMXIDCutoff = nextMXID - multixact_freeze_table_age;
1133  if (aggressiveMXIDCutoff < FirstMultiXactId)
1134  aggressiveMXIDCutoff = FirstMultiXactId;
1135  if (MultiXactIdPrecedesOrEquals(rel->rd_rel->relminmxid,
1136  aggressiveMXIDCutoff))
1137  return true;
1138 
1139  /* Non-aggressive VACUUM */
1140  return false;
1141 }
int autovacuum_freeze_max_age
Definition: autovacuum.c:126
#define Min(x, y)
Definition: c.h:988
int64 TimestampTz
Definition: timestamp.h:39
int errhint(const char *fmt,...)
Definition: elog.c:1316
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3170
int MultiXactMemberFreezeThreshold(void)
Definition: multixact.c:2825
#define FirstMultiXactId
Definition: multixact.h:25
bool TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, Relation relation, TransactionId *limit_xid, TimestampTz *limit_ts)
Definition: snapmgr.c:1796
void SetOldSnapshotThresholdTimestamp(TimestampTz ts, TransactionId xlimit)
Definition: snapmgr.c:1717
static bool OldSnapshotThresholdActive(void)
Definition: snapmgr.h:102
TransactionId FreezeLimit
Definition: vacuum.h:276
TransactionId OldestXmin
Definition: vacuum.h:266
TransactionId relfrozenxid
Definition: vacuum.h:250
MultiXactId relminmxid
Definition: vacuum.h:251
MultiXactId MultiXactCutoff
Definition: vacuum.h:277
MultiXactId OldestMxact
Definition: vacuum.h:267
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:299
#define FirstNormalTransactionId
Definition: transam.h:34
int vacuum_freeze_min_age
Definition: vacuum.c:68
int vacuum_multixact_freeze_table_age
Definition: vacuum.c:71
int vacuum_freeze_table_age
Definition: vacuum.c:69
int vacuum_multixact_freeze_min_age
Definition: vacuum.c:70

References Assert(), autovacuum_freeze_max_age, ereport, errhint(), errmsg(), FirstMultiXactId, FirstNormalTransactionId, VacuumParams::freeze_min_age, VacuumParams::freeze_table_age, VacuumCutoffs::FreezeLimit, GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), Min, VacuumParams::multixact_freeze_min_age, VacuumParams::multixact_freeze_table_age, VacuumCutoffs::MultiXactCutoff, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdPrecedesOrEquals(), MultiXactMemberFreezeThreshold(), VacuumCutoffs::OldestMxact, VacuumCutoffs::OldestXmin, OldSnapshotThresholdActive(), RelationData::rd_rel, ReadNextMultiXactId(), ReadNextTransactionId(), VacuumCutoffs::relfrozenxid, VacuumCutoffs::relminmxid, SetOldSnapshotThresholdTimestamp(), TransactionIdIsNormal, TransactionIdLimitedForOldSnapshots(), TransactionIdPrecedes(), TransactionIdPrecedesOrEquals(), vacuum_freeze_min_age, vacuum_freeze_table_age, vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age, and WARNING.

Referenced by copy_table_data(), and heap_vacuum_rel().

◆ vacuum_is_permitted_for_relation()

bool vacuum_is_permitted_for_relation ( Oid  relid,
Form_pg_class  reltuple,
bits32  options 
)

Definition at line 595 of file vacuum.c.

597 {
598  char *relname;
599 
601 
602  /*----------
603  * A role has privileges to vacuum or analyze the relation if any of the
604  * following are true:
605  * - the role is a superuser
606  * - the role owns the relation
607  * - the role owns the current database and the relation is not shared
608  * - the role has been granted the MAINTAIN privilege on the relation
609  * - the role has privileges to vacuum/analyze any of the relation's
610  * partition ancestors
611  *----------
612  */
613  if ((object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) && !reltuple->relisshared) ||
616  return true;
617 
618  relname = NameStr(reltuple->relname);
619 
620  if ((options & VACOPT_VACUUM) != 0)
621  {
623  (errmsg("permission denied to vacuum \"%s\", skipping it",
624  relname)));
625 
626  /*
627  * For VACUUM ANALYZE, both logs could show up, but just generate
628  * information for VACUUM as that would be the first one to be
629  * processed.
630  */
631  return false;
632  }
633 
634  if ((options & VACOPT_ANALYZE) != 0)
636  (errmsg("permission denied to analyze \"%s\", skipping it",
637  relname)));
638 
639  return false;
640 }
@ ACLCHECK_OK
Definition: acl.h:183
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3976
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:3923
#define NameStr(name)
Definition: c.h:730
Oid GetUserId(void)
Definition: miscinit.c:510
#define ACL_MAINTAIN
Definition: parsenodes.h:97
NameData relname
Definition: pg_class.h:38
bool has_partition_ancestor_privs(Oid relid, Oid userid, AclMode acl)
Definition: tablecmds.c:16993

References ACL_MAINTAIN, ACLCHECK_OK, Assert(), ereport, errmsg(), GetUserId(), has_partition_ancestor_privs(), MyDatabaseId, NameStr, object_ownercheck(), pg_class_aclcheck(), relname, VACOPT_ANALYZE, VACOPT_VACUUM, and WARNING.

Referenced by analyze_rel(), expand_vacuum_rel(), get_all_vacuum_rels(), and vacuum_rel().

◆ vacuum_open_relation()

Relation vacuum_open_relation ( Oid  relid,
RangeVar relation,
bits32  options,
bool  verbose,
LOCKMODE  lmode 
)

Definition at line 651 of file vacuum.c.

653 {
654  Relation rel;
655  bool rel_lock = true;
656  int elevel;
657 
659 
660  /*
661  * Open the relation and get the appropriate lock on it.
662  *
663  * There's a race condition here: the relation may have gone away since
664  * the last time we saw it. If so, we don't need to vacuum or analyze it.
665  *
666  * If we've been asked not to wait for the relation lock, acquire it first
667  * in non-blocking mode, before calling try_relation_open().
668  */
669  if (!(options & VACOPT_SKIP_LOCKED))
670  rel = try_relation_open(relid, lmode);
671  else if (ConditionalLockRelationOid(relid, lmode))
672  rel = try_relation_open(relid, NoLock);
673  else
674  {
675  rel = NULL;
676  rel_lock = false;
677  }
678 
679  /* if relation is opened, leave */
680  if (rel)
681  return rel;
682 
683  /*
684  * Relation could not be opened, hence generate if possible a log
685  * informing on the situation.
686  *
687  * If the RangeVar is not defined, we do not have enough information to
688  * provide a meaningful log statement. Chances are that the caller has
689  * intentionally not provided this information so that this logging is
690  * skipped, anyway.
691  */
692  if (relation == NULL)
693  return NULL;
694 
695  /*
696  * Determine the log level.
697  *
698  * For manual VACUUM or ANALYZE, we emit a WARNING to match the log
699  * statements in the permission checks; otherwise, only log if the caller
700  * so requested.
701  */
703  elevel = WARNING;
704  else if (verbose)
705  elevel = LOG;
706  else
707  return NULL;
708 
709  if ((options & VACOPT_VACUUM) != 0)
710  {
711  if (!rel_lock)
712  ereport(elevel,
713  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
714  errmsg("skipping vacuum of \"%s\" --- lock not available",
715  relation->relname)));
716  else
717  ereport(elevel,
719  errmsg("skipping vacuum of \"%s\" --- relation no longer exists",
720  relation->relname)));
721 
722  /*
723  * For VACUUM ANALYZE, both logs could show up, but just generate
724  * information for VACUUM as that would be the first one to be
725  * processed.
726  */
727  return NULL;
728  }
729 
730  if ((options & VACOPT_ANALYZE) != 0)
731  {
732  if (!rel_lock)
733  ereport(elevel,
734  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
735  errmsg("skipping analyze of \"%s\" --- lock not available",
736  relation->relname)));
737  else
738  ereport(elevel,
740  errmsg("skipping analyze of \"%s\" --- relation no longer exists",
741  relation->relname)));
742  }
743 
744  return NULL;
745 }
#define LOG
Definition: elog.h:31
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:152
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:78
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89

References Assert(), ConditionalLockRelationOid(), ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), IsAutoVacuumWorkerProcess(), LOG, NoLock, RangeVar::relname, try_relation_open(), VACOPT_ANALYZE, VACOPT_SKIP_LOCKED, VACOPT_VACUUM, verbose, and WARNING.

Referenced by analyze_rel(), and vacuum_rel().

◆ vacuum_rel()

static bool vacuum_rel ( Oid  relid,
RangeVar relation,
VacuumParams params,
bool  skip_privs 
)
static

Definition at line 1841 of file vacuum.c.

1842 {
1843  LOCKMODE lmode;
1844  Relation rel;
1845  LockRelId lockrelid;
1846  Oid toast_relid;
1847  Oid save_userid;
1848  int save_sec_context;
1849  int save_nestlevel;
1850 
1851  Assert(params != NULL);
1852 
1853  /* Begin a transaction for vacuuming this relation */
1855 
1856  if (!(params->options & VACOPT_FULL))
1857  {
1858  /*
1859  * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
1860  * other concurrent VACUUMs know that they can ignore this one while
1861  * determining their OldestXmin. (The reason we don't set it during a
1862  * full VACUUM is exactly that we may have to run user-defined
1863  * functions for functional indexes, and we want to make sure that if
1864  * they use the snapshot set above, any tuples it requires can't get
1865  * removed from other tables. An index function that depends on the
1866  * contents of other tables is arguably broken, but we won't break it
1867  * here by violating transaction semantics.)
1868  *
1869  * We also set the VACUUM_FOR_WRAPAROUND flag, which is passed down by
1870  * autovacuum; it's used to avoid canceling a vacuum that was invoked
1871  * in an emergency.
1872  *
1873  * Note: these flags remain set until CommitTransaction or
1874  * AbortTransaction. We don't want to clear them until we reset
1875  * MyProc->xid/xmin, otherwise GetOldestNonRemovableTransactionId()
1876  * might appear to go backwards, which is probably Not Good. (We also
1877  * set PROC_IN_VACUUM *before* taking our own snapshot, so that our
1878  * xmin doesn't become visible ahead of setting the flag.)
1879  */
1880  LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
1882  if (params->is_wraparound)
1885  LWLockRelease(ProcArrayLock);
1886  }
1887 
1888  /*
1889  * Need to acquire a snapshot to prevent pg_subtrans from being truncated,
1890  * cutoff xids in local memory wrapping around, and to have updated xmin
1891  * horizons.
1892  */
1894 
1895  /*
1896  * Check for user-requested abort. Note we want this to be inside a
1897  * transaction, so xact.c doesn't issue useless WARNING.
1898  */
1900 
1901  /*
1902  * Determine the type of lock we want --- hard exclusive lock for a FULL
1903  * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
1904  * way, we can be sure that no other backend is vacuuming the same table.
1905  */
1906  lmode = (params->options & VACOPT_FULL) ?
1908 
1909  /* open the relation and get the appropriate lock on it */
1910  rel = vacuum_open_relation(relid, relation, params->options,
1911  params->log_min_duration >= 0, lmode);
1912 
1913  /* leave if relation could not be opened or locked */
1914  if (!rel)
1915  {
1918  return false;
1919  }
1920 
1921  /*
1922  * Check if relation needs to be skipped based on privileges. This check
1923  * happens also when building the relation list to vacuum for a manual
1924  * operation, and needs to be done additionally here as VACUUM could
1925  * happen across multiple transactions where privileges could have changed
1926  * in-between. Make sure to only generate logs for VACUUM in this case.
1927  */
1928  if (!skip_privs &&
1930  rel->rd_rel,
1931  params->options & VACOPT_VACUUM))
1932  {
1933  relation_close(rel, lmode);
1936  return false;
1937  }
1938 
1939  /*
1940  * Check that it's of a vacuumable relkind.
1941  */
1942  if (rel->rd_rel->relkind != RELKIND_RELATION &&
1943  rel->rd_rel->relkind != RELKIND_MATVIEW &&
1944  rel->rd_rel->relkind != RELKIND_TOASTVALUE &&
1945  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1946  {
1947  ereport(WARNING,
1948  (errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
1949  RelationGetRelationName(rel))));
1950  relation_close(rel, lmode);
1953  return false;
1954  }
1955 
1956  /*
1957  * Silently ignore tables that are temp tables of other backends ---
1958  * trying to vacuum these will lead to great unhappiness, since their
1959  * contents are probably not up-to-date on disk. (We don't throw a
1960  * warning here; it would just lead to chatter during a database-wide
1961  * VACUUM.)
1962  */
1963  if (RELATION_IS_OTHER_TEMP(rel))
1964  {
1965  relation_close(rel, lmode);
1968  return false;
1969  }
1970 
1971  /*
1972  * Silently ignore partitioned tables as there is no work to be done. The
1973  * useful work is on their child partitions, which have been queued up for
1974  * us separately.
1975  */
1976  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1977  {
1978  relation_close(rel, lmode);
1981  /* It's OK to proceed with ANALYZE on this table */
1982  return true;
1983  }
1984 
1985  /*
1986  * Get a session-level lock too. This will protect our access to the
1987  * relation across multiple transactions, so that we can vacuum the
1988  * relation's TOAST table (if any) secure in the knowledge that no one is
1989  * deleting the parent relation.
1990  *
1991  * NOTE: this cannot block, even if someone else is waiting for access,
1992  * because the lock manager knows that both lock requests are from the
1993  * same process.
1994  */
1995  lockrelid = rel->rd_lockInfo.lockRelId;
1996  LockRelationIdForSession(&lockrelid, lmode);
1997 
1998  /*
1999  * Set index_cleanup option based on index_cleanup reloption if it wasn't
2000  * specified in VACUUM command, or when running in an autovacuum worker
2001  */
2002  if (params->index_cleanup == VACOPTVALUE_UNSPECIFIED)
2003  {
2004  StdRdOptIndexCleanup vacuum_index_cleanup;
2005 
2006  if (rel->rd_options == NULL)
2007  vacuum_index_cleanup = STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO;
2008  else
2009  vacuum_index_cleanup =
2010  ((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup;
2011 
2012  if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO)
2013  params->index_cleanup = VACOPTVALUE_AUTO;
2014  else if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON)
2016  else
2017  {
2018  Assert(vacuum_index_cleanup ==
2021  }
2022  }
2023 
2024  /*
2025  * Set truncate option based on truncate reloption if it wasn't specified
2026  * in VACUUM command, or when running in an autovacuum worker
2027  */
2028  if (params->truncate == VACOPTVALUE_UNSPECIFIED)
2029  {
2030  if (rel->rd_options == NULL ||
2031  ((StdRdOptions *) rel->rd_options)->vacuum_truncate)
2032  params->truncate = VACOPTVALUE_ENABLED;
2033  else
2034  params->truncate = VACOPTVALUE_DISABLED;
2035  }
2036 
2037  /*
2038  * Remember the relation's TOAST relation for later, if the caller asked
2039  * us to process it. In VACUUM FULL, though, the toast table is
2040  * automatically rebuilt by cluster_rel so we shouldn't recurse to it,
2041  * unless PROCESS_MAIN is disabled.
2042  */
2043  if ((params->options & VACOPT_PROCESS_TOAST) != 0 &&
2044  ((params->options & VACOPT_FULL) == 0 ||
2045  (params->options & VACOPT_PROCESS_MAIN) == 0))
2046  toast_relid = rel->rd_rel->reltoastrelid;
2047  else
2048  toast_relid = InvalidOid;
2049 
2050  /*
2051  * Switch to the table owner's userid, so that any index functions are run
2052  * as that user. Also lock down security-restricted operations and
2053  * arrange to make GUC variable changes local to this command. (This is
2054  * unnecessary, but harmless, for lazy VACUUM.)
2055  */
2056  GetUserIdAndSecContext(&save_userid, &save_sec_context);
2057  SetUserIdAndSecContext(rel->rd_rel->relowner,
2058  save_sec_context | SECURITY_RESTRICTED_OPERATION);
2059  save_nestlevel = NewGUCNestLevel();
2060 
2061  /*
2062  * If PROCESS_MAIN is set (the default), it's time to vacuum the main
2063  * relation. Otherwise, we can skip this part. If processing the TOAST
2064  * table is required (e.g., PROCESS_TOAST is set), we force PROCESS_MAIN
2065  * to be set when we recurse to the TOAST table.
2066  */
2067  if (params->options & VACOPT_PROCESS_MAIN)
2068  {
2069  /*
2070  * Do the actual work --- either FULL or "lazy" vacuum
2071  */
2072  if (params->options & VACOPT_FULL)
2073  {
2074  ClusterParams cluster_params = {0};
2075 
2076  /* close relation before vacuuming, but hold lock until commit */
2077  relation_close(rel, NoLock);
2078  rel = NULL;
2079 
2080  if ((params->options & VACOPT_VERBOSE) != 0)
2081  cluster_params.options |= CLUOPT_VERBOSE;
2082 
2083  /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
2084  cluster_rel(relid, InvalidOid, &cluster_params);
2085  }
2086  else
2087  table_relation_vacuum(rel, params, vac_strategy);
2088  }
2089 
2090  /* Roll back any GUC changes executed by index functions */
2091  AtEOXact_GUC(false, save_nestlevel);
2092 
2093  /* Restore userid and security context */
2094  SetUserIdAndSecContext(save_userid, save_sec_context);
2095 
2096  /* all done with this class, but hold lock until commit */
2097  if (rel)
2098  relation_close(rel, NoLock);
2099 
2100  /*
2101  * Complete the transaction and free all temporary memory used.
2102  */
2105 
2106  /*
2107  * If the relation has a secondary toast rel, vacuum that too while we
2108  * still hold the session lock on the main table. Note however that
2109  * "analyze" will not get done on the toast table. This is good, because
2110  * the toaster always uses hardcoded index access and statistics are
2111  * totally unimportant for toast relations.
2112  */
2113  if (toast_relid != InvalidOid)
2114  {
2115  VacuumParams toast_vacuum_params;
2116 
2117  /* force VACOPT_PROCESS_MAIN so vacuum_rel() processes it */
2118  memcpy(&toast_vacuum_params, params, sizeof(VacuumParams));
2119  toast_vacuum_params.options |= VACOPT_PROCESS_MAIN;
2120 
2121  vacuum_rel(toast_relid, NULL, &toast_vacuum_params, true);
2122  }
2123 
2124  /*
2125  * Now release the session-level lock on the main table.
2126  */
2127  UnlockRelationIdForSession(&lockrelid, lmode);
2128 
2129  /* Report that we really did it. */
2130  return true;
2131 }
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
Definition: cluster.c:314
#define CLUOPT_VERBOSE
Definition: cluster.h:23
int NewGUCNestLevel(void)
Definition: guc.c:2201
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2215
void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:398
void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:411
int LOCKMODE
Definition: lockdefs.h:26
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:305
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:631
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:638
#define PROC_IN_VACUUM
Definition: proc.h:57
#define PROC_VACUUM_FOR_WRAPAROUND
Definition: proc.h:59
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:658
StdRdOptIndexCleanup
Definition: rel.h:328
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO
Definition: rel.h:329
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF
Definition: rel.h:330
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON
Definition: rel.h:331
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
PGPROC * MyProc
Definition: proc.c:66
PROC_HDR * ProcGlobal
Definition: proc.c:78
bits32 options
Definition: cluster.h:30
LockRelId lockRelId
Definition: rel.h:45
Definition: rel.h:38
uint8 statusFlags
Definition: proc.h:233
int pgxactoff
Definition: proc.h:188
uint8 * statusFlags
Definition: proc.h:377
LockInfoData rd_lockInfo
Definition: rel.h:113
bytea * rd_options
Definition: rel.h:173
static void table_relation_vacuum(Relation rel, struct VacuumParams *params, BufferAccessStrategy bstrategy)
Definition: tableam.h:1712
Relation vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
Definition: vacuum.c:651

References AccessExclusiveLock, Assert(), AtEOXact_GUC(), CHECK_FOR_INTERRUPTS, CLUOPT_VERBOSE, cluster_rel(), CommitTransactionCommand(), ereport, errmsg(), GetTransactionSnapshot(), GetUserIdAndSecContext(), VacuumParams::index_cleanup, InvalidOid, VacuumParams::is_wraparound, LockRelationIdForSession(), LockInfoData::lockRelId, VacuumParams::log_min_duration, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyProc, NewGUCNestLevel(), NoLock, ClusterParams::options, VacuumParams::options, PGPROC::pgxactoff, PopActiveSnapshot(), PROC_IN_VACUUM, PROC_VACUUM_FOR_WRAPAROUND, ProcGlobal, PushActiveSnapshot(), RelationData::rd_lockInfo, RelationData::rd_options, RelationData::rd_rel, relation_close(), RELATION_IS_OTHER_TEMP, RelationGetRelationName, RelationGetRelid, SECURITY_RESTRICTED_OPERATION, SetUserIdAndSecContext(), ShareUpdateExclusiveLock, StartTransactionCommand(), PGPROC::statusFlags, PROC_HDR::statusFlags, STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO, STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF, STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON, table_relation_vacuum(), VacuumParams::truncate, UnlockRelationIdForSession(), vac_strategy, VACOPT_FULL, VACOPT_PROCESS_MAIN, VACOPT_PROCESS_TOAST, VACOPT_VACUUM, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_DISABLED, VACOPTVALUE_ENABLED, VACOPTVALUE_UNSPECIFIED, vacuum_is_permitted_for_relation(), vacuum_open_relation(), and WARNING.

Referenced by vacuum().

◆ vacuum_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( const struct VacuumCutoffs cutoffs)

Definition at line 1151 of file vacuum.c.

1152 {
1153  TransactionId relfrozenxid = cutoffs->relfrozenxid;
1154  MultiXactId relminmxid = cutoffs->relminmxid;
1155  TransactionId xid_skip_limit;
1156  MultiXactId multi_skip_limit;
1157  int skip_index_vacuum;
1158 
1159  Assert(TransactionIdIsNormal(relfrozenxid));
1160  Assert(MultiXactIdIsValid(relminmxid));
1161 
1162  /*
1163  * Determine the index skipping age to use. In any case no less than
1164  * autovacuum_freeze_max_age * 1.05.
1165  */
1166  skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
1167 
1168  xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
1169  if (!TransactionIdIsNormal(xid_skip_limit))
1170  xid_skip_limit = FirstNormalTransactionId;
1171 
1172  if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
1173  {
1174  /* The table's relfrozenxid is too old */
1175  return true;
1176  }
1177 
1178  /*
1179  * Similar to above, determine the index skipping age to use for
1180  * multixact. In any case no less than autovacuum_multixact_freeze_max_age *
1181  * 1.05.
1182  */
1183  skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
1185 
1186  multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
1187  if (multi_skip_limit < FirstMultiXactId)
1188  multi_skip_limit = FirstMultiXactId;
1189 
1190  if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
1191  {
1192  /* The table's relminmxid is too old */
1193  return true;
1194  }
1195 
1196  return false;
1197 }
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:127
#define Max(x, y)
Definition: c.h:982
int vacuum_multixact_failsafe_age
Definition: vacuum.c:73
int vacuum_failsafe_age
Definition: vacuum.c:72

References Assert(), autovacuum_freeze_max_age, autovacuum_multixact_freeze_max_age, FirstMultiXactId, FirstNormalTransactionId, Max, MultiXactIdIsValid, MultiXactIdPrecedes(), ReadNextMultiXactId(), ReadNextTransactionId(), VacuumCutoffs::relfrozenxid, VacuumCutoffs::relminmxid, TransactionIdIsNormal, TransactionIdPrecedes(), vacuum_failsafe_age, and vacuum_multixact_failsafe_age.

Referenced by lazy_check_wraparound_failsafe().

Variable Documentation

◆ vac_context

MemoryContext vac_context = NULL
static

Definition at line 77 of file vacuum.c.

Referenced by expand_vacuum_rel(), get_all_vacuum_rels(), and vacuum().

◆ vac_strategy

BufferAccessStrategy vac_strategy
static

Definition at line 78 of file vacuum.c.

Referenced by vacuum(), and vacuum_rel().

◆ vacuum_failsafe_age

int vacuum_failsafe_age

Definition at line 72 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

int vacuum_freeze_min_age

Definition at line 68 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_freeze_table_age

int vacuum_freeze_table_age

Definition at line 69 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_failsafe_age

int vacuum_multixact_failsafe_age

Definition at line 73 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_multixact_freeze_min_age

int vacuum_multixact_freeze_min_age

Definition at line 70 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_freeze_table_age

int vacuum_multixact_freeze_table_age

Definition at line 71 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

int VacuumCostBalanceLocal = 0

◆ VacuumSharedCostBalance

pg_atomic_uint32* VacuumSharedCostBalance = NULL