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/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/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)
 
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_relation_owner (Oid relid, Form_pg_class reltuple, bits32 options)
 
Relation vacuum_open_relation (Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
 
void vacuum_set_xid_limits (Relation rel, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, TransactionId *oldestXmin, TransactionId *freezeLimit, TransactionId *xidFullScanLimit, MultiXactId *multiXactCutoff, MultiXactId *mxactFullScanLimit)
 
bool vacuum_xid_failsafe_check (TransactionId relfrozenxid, MultiXactId relminmxid)
 
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 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 2220 of file vacuum.c.

2221 {
2222  double msec = 0;
2223  uint32 shared_balance;
2224  int nworkers;
2225 
2226  /* Parallel vacuum must be active */
2228 
2230 
2231  /* At least count itself */
2232  Assert(nworkers >= 1);
2233 
2234  /* Update the shared cost balance value atomically */
2236 
2237  /* Compute the total local balance for the current worker */
2239 
2240  if ((shared_balance >= VacuumCostLimit) &&
2241  (VacuumCostBalanceLocal > 0.5 * ((double) VacuumCostLimit / nworkers)))
2242  {
2243  /* Compute sleep time based on the local cost balance */
2247  }
2248 
2249  /*
2250  * Reset the local balance as we accumulated it into the shared value.
2251  */
2252  VacuumCostBalance = 0;
2253 
2254  return msec;
2255 }
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:401
static uint32 pg_atomic_add_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:386
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:241
unsigned int uint32
Definition: c.h:441
int VacuumCostLimit
Definition: globals.c:144
int VacuumCostBalance
Definition: globals.c:151
double VacuumCostDelay
Definition: globals.c:145
Assert(fmt[strlen(fmt) - 1] !='\n')
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:84
int VacuumCostBalanceLocal
Definition: vacuum.c:85
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:83

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 107 of file vacuum.c.

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

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

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_relation_owner(), and WARNING.

Referenced by vacuum().

◆ get_all_vacuum_rels()

static List * get_all_vacuum_rels ( int  options)
static

Definition at line 891 of file vacuum.c.

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

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

Referenced by vacuum().

◆ get_vacoptval_from_boolean()

static VacOptValue get_vacoptval_from_boolean ( DefElem def)
static

Definition at line 2264 of file vacuum.c.

2265 {
2267 }
@ VACOPTVALUE_ENABLED
Definition: vacuum.h:205
@ VACOPTVALUE_DISABLED
Definition: vacuum.h:204

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 2275 of file vacuum.c.

2277 {
2278  /* Do bulk deletion */
2279  istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2280  (void *) dead_items);
2281 
2282  ereport(ivinfo->message_level,
2283  (errmsg("scanned index \"%s\" to remove %d row versions",
2284  RelationGetRelationName(ivinfo->index),
2285  dead_items->num_items)));
2286 
2287  return istat;
2288 }
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:691
#define RelationGetRelationName(relation)
Definition: rel.h:512
Relation index
Definition: genam.h:46
int message_level
Definition: genam.h:50
int num_items
Definition: vacuum.h:244
static bool vac_tid_reaped(ItemPointer itemptr, void *state)
Definition: vacuum.c:2336

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 2296 of file vacuum.c.

2297 {
2298  istat = index_vacuum_cleanup(ivinfo, istat);
2299 
2300  if (istat)
2301  ereport(ivinfo->message_level,
2302  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2303  RelationGetRelationName(ivinfo->index),
2304  istat->num_index_tuples,
2305  istat->num_pages),
2306  errdetail("%.0f index row versions were removed.\n"
2307  "%u index pages were newly deleted.\n"
2308  "%u index pages are currently deleted, of which %u are currently reusable.",
2309  istat->tuples_removed,
2310  istat->pages_newly_deleted,
2311  istat->pages_deleted, istat->pages_free)));
2312 
2313  return istat;
2314 }
int errdetail(const char *fmt,...)
Definition: elog.c:1037
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:712
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 2135 of file vacuum.c.

2136 {
2137  if (Irel == NULL)
2138  return;
2139 
2140  while (nindexes--)
2141  {
2142  Relation ind = Irel[nindexes];
2143 
2144  index_close(ind, lockmode);
2145  }
2146  pfree(Irel);
2147 }
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
void pfree(void *pointer)
Definition: mcxt.c:1169

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 2370 of file vacuum.c.

2371 {
2372  BlockNumber lblk,
2373  rblk;
2374  OffsetNumber loff,
2375  roff;
2376 
2377  lblk = ItemPointerGetBlockNumber((ItemPointer) left);
2378  rblk = ItemPointerGetBlockNumber((ItemPointer) right);
2379 
2380  if (lblk < rblk)
2381  return -1;
2382  if (lblk > rblk)
2383  return 1;
2384 
2385  loff = ItemPointerGetOffsetNumber((ItemPointer) left);
2386  roff = ItemPointerGetOffsetNumber((ItemPointer) right);
2387 
2388  if (loff < roff)
2389  return -1;
2390  if (loff > roff)
2391  return 1;
2392 
2393  return 0;
2394 }
uint32 BlockNumber
Definition: block.h:31
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
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 1230 of file vacuum.c.

1234 {
1235  BlockNumber old_rel_pages = relation->rd_rel->relpages;
1236  double old_rel_tuples = relation->rd_rel->reltuples;
1237  double old_density;
1238  double unscanned_pages;
1239  double total_tuples;
1240 
1241  /* If we did scan the whole table, just use the count as-is */
1242  if (scanned_pages >= total_pages)
1243  return scanned_tuples;
1244 
1245  /*
1246  * If scanned_pages is zero but total_pages isn't, keep the existing value
1247  * of reltuples. (Note: we might be returning -1 in this case.)
1248  */
1249  if (scanned_pages == 0)
1250  return old_rel_tuples;
1251 
1252  /*
1253  * If old density is unknown, we can't do much except scale up
1254  * scanned_tuples to match total_pages.
1255  */
1256  if (old_rel_tuples < 0 || old_rel_pages == 0)
1257  return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1258 
1259  /*
1260  * Okay, we've covered the corner cases. The normal calculation is to
1261  * convert the old measurement to a density (tuples per page), then
1262  * estimate the number of tuples in the unscanned pages using that figure,
1263  * and finally add on the number of tuples in the scanned pages.
1264  */
1265  old_density = old_rel_tuples / old_rel_pages;
1266  unscanned_pages = (double) total_pages - (double) scanned_pages;
1267  total_tuples = old_density * unscanned_pages + scanned_tuples;
1268  return floor(total_tuples + 0.5);
1269 }
Form_pg_class rd_rel
Definition: rel.h:109

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)
inline

Definition at line 2321 of file vacuum.c.

2322 {
2323  Assert(max_items <= MAXDEADITEMS(MaxAllocSize));
2324 
2325  return offsetof(VacDeadItems, items) + sizeof(ItemPointerData) * max_items;
2326 }
#define offsetof(type, field)
Definition: c.h:727
struct ItemPointerData ItemPointerData
#define MaxAllocSize
Definition: memutils.h:40
#define MAXDEADITEMS(avail_mem)
Definition: vacuum.h:250

References Assert(), MaxAllocSize, MAXDEADITEMS, and offsetof.

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 2092 of file vacuum.c.

2094 {
2095  List *indexoidlist;
2096  ListCell *indexoidscan;
2097  int i;
2098 
2099  Assert(lockmode != NoLock);
2100 
2101  indexoidlist = RelationGetIndexList(relation);
2102 
2103  /* allocate enough memory for all indexes */
2104  i = list_length(indexoidlist);
2105 
2106  if (i > 0)
2107  *Irel = (Relation *) palloc(i * sizeof(Relation));
2108  else
2109  *Irel = NULL;
2110 
2111  /* collect just the ready indexes */
2112  i = 0;
2113  foreach(indexoidscan, indexoidlist)
2114  {
2115  Oid indexoid = lfirst_oid(indexoidscan);
2116  Relation indrel;
2117 
2118  indrel = index_open(indexoid, lockmode);
2119  if (indrel->rd_index->indisready)
2120  (*Irel)[i++] = indrel;
2121  else
2122  index_close(indrel, lockmode);
2123  }
2124 
2125  *nindexes = i;
2126 
2127  list_free(indexoidlist);
2128 }
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:1505
void * palloc(Size size)
Definition: mcxt.c:1062
static int list_length(const List *l)
Definition: pg_list.h:149
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4649
Form_pg_index rd_index
Definition: rel.h:188

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 2336 of file vacuum.c.

2337 {
2338  VacDeadItems *dead_items = (VacDeadItems *) state;
2339  int64 litem,
2340  ritem,
2341  item;
2342  ItemPointer res;
2343 
2344  litem = itemptr_encode(&dead_items->items[0]);
2345  ritem = itemptr_encode(&dead_items->items[dead_items->num_items - 1]);
2346  item = itemptr_encode(itemptr);
2347 
2348  /*
2349  * Doing a simple bound check before bsearch() is useful to avoid the
2350  * extra cost of bsearch(), especially if dead items on the heap are
2351  * concentrated in a certain range. Since this function is called for
2352  * every index tuple, it pays to be really fast.
2353  */
2354  if (item < litem || item > ritem)
2355  return false;
2356 
2357  res = (ItemPointer) bsearch((void *) itemptr,
2358  (void *) dead_items->items,
2359  dead_items->num_items,
2360  sizeof(ItemPointerData),
2361  vac_cmp_itemptr);
2362 
2363  return (res != NULL);
2364 }
static int64 itemptr_encode(ItemPointer itemptr)
Definition: index.h:185
ItemPointerData * ItemPointer
Definition: itemptr.h:49
ItemPointerData items[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuum.h:247
Definition: regguts.h:318
static int vac_cmp_itemptr(const void *left, const void *right)
Definition: vacuum.c:2370

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 1654 of file vacuum.c.

1658 {
1660  Relation relation;
1661  TableScanDesc scan;
1662  HeapTuple tuple;
1663  Oid oldestxid_datoid;
1664  Oid minmulti_datoid;
1665  bool bogus = false;
1666  bool frozenAlreadyWrapped = false;
1667 
1668  /* Restrict task to one backend per cluster; see SimpleLruTruncate(). */
1669  LWLockAcquire(WrapLimitsVacuumLock, LW_EXCLUSIVE);
1670 
1671  /* init oldest datoids to sync with my frozenXID/minMulti values */
1672  oldestxid_datoid = MyDatabaseId;
1673  minmulti_datoid = MyDatabaseId;
1674 
1675  /*
1676  * Scan pg_database to compute the minimum datfrozenxid/datminmxid
1677  *
1678  * Since vac_update_datfrozenxid updates datfrozenxid/datminmxid in-place,
1679  * the values could change while we look at them. Fetch each one just
1680  * once to ensure sane behavior of the comparison logic. (Here, as in
1681  * many other places, we assume that fetching or updating an XID in shared
1682  * storage is atomic.)
1683  *
1684  * Note: we need not worry about a race condition with new entries being
1685  * inserted by CREATE DATABASE. Any such entry will have a copy of some
1686  * existing DB's datfrozenxid, and that source DB cannot be ours because
1687  * of the interlock against copying a DB containing an active backend.
1688  * Hence the new entry will not reduce the minimum. Also, if two VACUUMs
1689  * concurrently modify the datfrozenxid's of different databases, the
1690  * worst possible outcome is that pg_xact is not truncated as aggressively
1691  * as it could be.
1692  */
1693  relation = table_open(DatabaseRelationId, AccessShareLock);
1694 
1695  scan = table_beginscan_catalog(relation, 0, NULL);
1696 
1697  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1698  {
1699  volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
1700  TransactionId datfrozenxid = dbform->datfrozenxid;
1701  TransactionId datminmxid = dbform->datminmxid;
1702 
1705 
1706  /*
1707  * If things are working properly, no database should have a
1708  * datfrozenxid or datminmxid that is "in the future". However, such
1709  * cases have been known to arise due to bugs in pg_upgrade. If we
1710  * see any entries that are "in the future", chicken out and don't do
1711  * anything. This ensures we won't truncate clog before those
1712  * databases have been scanned and cleaned up. (We will issue the
1713  * "already wrapped" warning if appropriate, though.)
1714  */
1715  if (TransactionIdPrecedes(lastSaneFrozenXid, datfrozenxid) ||
1716  MultiXactIdPrecedes(lastSaneMinMulti, datminmxid))
1717  bogus = true;
1718 
1719  if (TransactionIdPrecedes(nextXID, datfrozenxid))
1720  frozenAlreadyWrapped = true;
1721  else if (TransactionIdPrecedes(datfrozenxid, frozenXID))
1722  {
1723  frozenXID = datfrozenxid;
1724  oldestxid_datoid = dbform->oid;
1725  }
1726 
1727  if (MultiXactIdPrecedes(datminmxid, minMulti))
1728  {
1729  minMulti = datminmxid;
1730  minmulti_datoid = dbform->oid;
1731  }
1732  }
1733 
1734  table_endscan(scan);
1735 
1736  table_close(relation, AccessShareLock);
1737 
1738  /*
1739  * Do not truncate CLOG if we seem to have suffered wraparound already;
1740  * the computed minimum XID might be bogus. This case should now be
1741  * impossible due to the defenses in GetNewTransactionId, but we keep the
1742  * test anyway.
1743  */
1744  if (frozenAlreadyWrapped)
1745  {
1746  ereport(WARNING,
1747  (errmsg("some databases have not been vacuumed in over 2 billion transactions"),
1748  errdetail("You might have already suffered transaction-wraparound data loss.")));
1749  return;
1750  }
1751 
1752  /* chicken out if data is bogus in any other way */
1753  if (bogus)
1754  return;
1755 
1756  /*
1757  * Advance the oldest value for commit timestamps before truncating, so
1758  * that if a user requests a timestamp for a transaction we're truncating
1759  * away right after this point, they get NULL instead of an ugly "file not
1760  * found" error from slru.c. This doesn't matter for xact/multixact
1761  * because they are not subject to arbitrary lookups from users.
1762  */
1763  AdvanceOldestCommitTsXid(frozenXID);
1764 
1765  /*
1766  * Truncate CLOG, multixact and CommitTs to the oldest computed value.
1767  */
1768  TruncateCLOG(frozenXID, oldestxid_datoid);
1769  TruncateCommitTs(frozenXID);
1770  TruncateMultiXact(minMulti, minmulti_datoid);
1771 
1772  /*
1773  * Update the wrap limit for GetNewTransactionId and creation of new
1774  * MultiXactIds. Note: these functions will also signal the postmaster
1775  * for an(other) autovac cycle if needed. XXX should we avoid possibly
1776  * signaling twice?
1777  */
1778  SetTransactionIdLimit(frozenXID, oldestxid_datoid);
1779  SetMultiXactIdLimit(minMulti, minmulti_datoid, false);
1780 
1781  LWLockRelease(WrapLimitsVacuumLock);
1782 }
uint32 TransactionId
Definition: c.h:587
void TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
Definition: clog.c:879
void AdvanceOldestCommitTsXid(TransactionId oldestXact)
Definition: commit_ts.c:905
void TruncateCommitTs(TransactionId oldestXact)
Definition: commit_ts.c:852
Oid MyDatabaseId
Definition: globals.c:88
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1199
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1803
@ LW_EXCLUSIVE
Definition: lwlock.h:104
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3159
void SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid, bool is_startup)
Definition: multixact.c:2213
void TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
Definition: multixact.c:2944
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
TransactionId datfrozenxid
Definition: pg_database.h:59
TransactionId datminmxid
Definition: pg_database.h:62
FormData_pg_database * Form_pg_database
Definition: pg_database.h:78
FormData_pg_database
Definition: pg_database.h:71
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
static TransactionId ReadNextTransactionId(void)
Definition: transam.h:316
#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 1440 of file vacuum.c.

1441 {
1442  HeapTuple tuple;
1443  Form_pg_database dbform;
1444  Relation relation;
1445  SysScanDesc scan;
1446  HeapTuple classTup;
1447  TransactionId newFrozenXid;
1448  MultiXactId newMinMulti;
1449  TransactionId lastSaneFrozenXid;
1450  MultiXactId lastSaneMinMulti;
1451  bool bogus = false;
1452  bool dirty = false;
1453  ScanKeyData key[1];
1454 
1455  /*
1456  * Restrict this task to one backend per database. This avoids race
1457  * conditions that would move datfrozenxid or datminmxid backward. It
1458  * avoids calling vac_truncate_clog() with a datfrozenxid preceding a
1459  * datfrozenxid passed to an earlier vac_truncate_clog() call.
1460  */
1462 
1463  /*
1464  * Initialize the "min" calculation with
1465  * GetOldestNonRemovableTransactionId(), which is a reasonable
1466  * approximation to the minimum relfrozenxid for not-yet-committed
1467  * pg_class entries for new tables; see AddNewRelationTuple(). So we
1468  * cannot produce a wrong minimum by starting with this.
1469  */
1470  newFrozenXid = GetOldestNonRemovableTransactionId(NULL);
1471 
1472  /*
1473  * Similarly, initialize the MultiXact "min" with the value that would be
1474  * used on pg_class for new tables. See AddNewRelationTuple().
1475  */
1476  newMinMulti = GetOldestMultiXactId();
1477 
1478  /*
1479  * Identify the latest relfrozenxid and relminmxid values that we could
1480  * validly see during the scan. These are conservative values, but it's
1481  * not really worth trying to be more exact.
1482  */
1483  lastSaneFrozenXid = ReadNextTransactionId();
1484  lastSaneMinMulti = ReadNextMultiXactId();
1485 
1486  /*
1487  * We must seqscan pg_class to find the minimum Xid, because there is no
1488  * index that can help us here.
1489  */
1490  relation = table_open(RelationRelationId, AccessShareLock);
1491 
1492  scan = systable_beginscan(relation, InvalidOid, false,
1493  NULL, 0, NULL);
1494 
1495  while ((classTup = systable_getnext(scan)) != NULL)
1496  {
1497  Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
1498 
1499  /*
1500  * Only consider relations able to hold unfrozen XIDs (anything else
1501  * should have InvalidTransactionId in relfrozenxid anyway).
1502  */
1503  if (classForm->relkind != RELKIND_RELATION &&
1504  classForm->relkind != RELKIND_MATVIEW &&
1505  classForm->relkind != RELKIND_TOASTVALUE)
1506  {
1507  Assert(!TransactionIdIsValid(classForm->relfrozenxid));
1508  Assert(!MultiXactIdIsValid(classForm->relminmxid));
1509  continue;
1510  }
1511 
1512  /*
1513  * Some table AMs might not need per-relation xid / multixid horizons.
1514  * It therefore seems reasonable to allow relfrozenxid and relminmxid
1515  * to not be set (i.e. set to their respective Invalid*Id)
1516  * independently. Thus validate and compute horizon for each only if
1517  * set.
1518  *
1519  * If things are working properly, no relation should have a
1520  * relfrozenxid or relminmxid that is "in the future". However, such
1521  * cases have been known to arise due to bugs in pg_upgrade. If we
1522  * see any entries that are "in the future", chicken out and don't do
1523  * anything. This ensures we won't truncate clog & multixact SLRUs
1524  * before those relations have been scanned and cleaned up.
1525  */
1526 
1527  if (TransactionIdIsValid(classForm->relfrozenxid))
1528  {
1529  Assert(TransactionIdIsNormal(classForm->relfrozenxid));
1530 
1531  /* check for values in the future */
1532  if (TransactionIdPrecedes(lastSaneFrozenXid, classForm->relfrozenxid))
1533  {
1534  bogus = true;
1535  break;
1536  }
1537 
1538  /* determine new horizon */
1539  if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
1540  newFrozenXid = classForm->relfrozenxid;
1541  }
1542 
1543  if (MultiXactIdIsValid(classForm->relminmxid))
1544  {
1545  /* check for values in the future */
1546  if (MultiXactIdPrecedes(lastSaneMinMulti, classForm->relminmxid))
1547  {
1548  bogus = true;
1549  break;
1550  }
1551 
1552  /* determine new horizon */
1553  if (MultiXactIdPrecedes(classForm->relminmxid, newMinMulti))
1554  newMinMulti = classForm->relminmxid;
1555  }
1556  }
1557 
1558  /* we're done with pg_class */
1559  systable_endscan(scan);
1560  table_close(relation, AccessShareLock);
1561 
1562  /* chicken out if bogus data found */
1563  if (bogus)
1564  return;
1565 
1566  Assert(TransactionIdIsNormal(newFrozenXid));
1567  Assert(MultiXactIdIsValid(newMinMulti));
1568 
1569  /* Now fetch the pg_database tuple we need to update. */
1570  relation = table_open(DatabaseRelationId, RowExclusiveLock);
1571 
1572  /*
1573  * Get the pg_database tuple to scribble on. Note that this does not
1574  * directly rely on the syscache to avoid issues with flattened toast
1575  * values for the in-place update.
1576  */
1577  ScanKeyInit(&key[0],
1578  Anum_pg_database_oid,
1579  BTEqualStrategyNumber, F_OIDEQ,
1581 
1582  scan = systable_beginscan(relation, DatabaseOidIndexId, true,
1583  NULL, 1, key);
1584  tuple = systable_getnext(scan);
1585  tuple = heap_copytuple(tuple);
1586  systable_endscan(scan);
1587 
1588  if (!HeapTupleIsValid(tuple))
1589  elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
1590 
1591  dbform = (Form_pg_database) GETSTRUCT(tuple);
1592 
1593  /*
1594  * As in vac_update_relstats(), we ordinarily don't want to let
1595  * datfrozenxid go backward; but if it's "in the future" then it must be
1596  * corrupt and it seems best to overwrite it.
1597  */
1598  if (dbform->datfrozenxid != newFrozenXid &&
1599  (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid) ||
1600  TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid)))
1601  {
1602  dbform->datfrozenxid = newFrozenXid;
1603  dirty = true;
1604  }
1605  else
1606  newFrozenXid = dbform->datfrozenxid;
1607 
1608  /* Ditto for datminmxid */
1609  if (dbform->datminmxid != newMinMulti &&
1610  (MultiXactIdPrecedes(dbform->datminmxid, newMinMulti) ||
1611  MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid)))
1612  {
1613  dbform->datminmxid = newMinMulti;
1614  dirty = true;
1615  }
1616  else
1617  newMinMulti = dbform->datminmxid;
1618 
1619  if (dirty)
1620  heap_inplace_update(relation, tuple);
1621 
1622  heap_freetuple(tuple);
1623  table_close(relation, RowExclusiveLock);
1624 
1625  /*
1626  * If we were able to advance datfrozenxid or datminmxid, see if we can
1627  * truncate pg_xact and/or pg_multixact. Also do it if the shared
1628  * XID-wrap-limit info is stale, since this action will update that too.
1629  */
1630  if (dirty || ForceTransactionIdLimitUpdate())
1631  vac_truncate_clog(newFrozenXid, newMinMulti,
1632  lastSaneFrozenXid, lastSaneMinMulti);
1633 }
TransactionId MultiXactId
Definition: c.h:597
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:598
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:505
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:386
void heap_inplace_update(Relation relation, HeapTuple tuple)
Definition: heapam.c:5981
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:470
#define ExclusiveLock
Definition: lockdefs.h:42
#define RowExclusiveLock
Definition: lockdefs.h:38
MultiXactId GetOldestMultiXactId(void)
Definition: multixact.c:2506
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:723
#define InvalidOid
Definition: postgres_ext.h:36
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:2005
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:1654
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  in_outer_xact 
)

Definition at line 1313 of file vacuum.c.

1319 {
1320  Oid relid = RelationGetRelid(relation);
1321  Relation rd;
1322  HeapTuple ctup;
1323  Form_pg_class pgcform;
1324  bool dirty;
1325 
1326  rd = table_open(RelationRelationId, RowExclusiveLock);
1327 
1328  /* Fetch a copy of the tuple to scribble on */
1330  if (!HeapTupleIsValid(ctup))
1331  elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
1332  relid);
1333  pgcform = (Form_pg_class) GETSTRUCT(ctup);
1334 
1335  /* Apply statistical updates, if any, to copied tuple */
1336 
1337  dirty = false;
1338  if (pgcform->relpages != (int32) num_pages)
1339  {
1340  pgcform->relpages = (int32) num_pages;
1341  dirty = true;
1342  }
1343  if (pgcform->reltuples != (float4) num_tuples)
1344  {
1345  pgcform->reltuples = (float4) num_tuples;
1346  dirty = true;
1347  }
1348  if (pgcform->relallvisible != (int32) num_all_visible_pages)
1349  {
1350  pgcform->relallvisible = (int32) num_all_visible_pages;
1351  dirty = true;
1352  }
1353 
1354  /* Apply DDL updates, but not inside an outer transaction (see above) */
1355 
1356  if (!in_outer_xact)
1357  {
1358  /*
1359  * If we didn't find any indexes, reset relhasindex.
1360  */
1361  if (pgcform->relhasindex && !hasindex)
1362  {
1363  pgcform->relhasindex = false;
1364  dirty = true;
1365  }
1366 
1367  /* We also clear relhasrules and relhastriggers if needed */
1368  if (pgcform->relhasrules && relation->rd_rules == NULL)
1369  {
1370  pgcform->relhasrules = false;
1371  dirty = true;
1372  }
1373  if (pgcform->relhastriggers && relation->trigdesc == NULL)
1374  {
1375  pgcform->relhastriggers = false;
1376  dirty = true;
1377  }
1378  }
1379 
1380  /*
1381  * Update relfrozenxid, unless caller passed InvalidTransactionId
1382  * indicating it has no new data.
1383  *
1384  * Ordinarily, we don't let relfrozenxid go backwards: if things are
1385  * working correctly, the only way the new frozenxid could be older would
1386  * be if a previous VACUUM was done with a tighter freeze_min_age, in
1387  * which case we don't want to forget the work it already did. However,
1388  * if the stored relfrozenxid is "in the future", then it must be corrupt
1389  * and it seems best to overwrite it with the cutoff we used this time.
1390  * This should match vac_update_datfrozenxid() concerning what we consider
1391  * to be "in the future".
1392  */
1393  if (TransactionIdIsNormal(frozenxid) &&
1394  pgcform->relfrozenxid != frozenxid &&
1395  (TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid) ||
1397  pgcform->relfrozenxid)))
1398  {
1399  pgcform->relfrozenxid = frozenxid;
1400  dirty = true;
1401  }
1402 
1403  /* Similarly for relminmxid */
1404  if (MultiXactIdIsValid(minmulti) &&
1405  pgcform->relminmxid != minmulti &&
1406  (MultiXactIdPrecedes(pgcform->relminmxid, minmulti) ||
1407  MultiXactIdPrecedes(ReadNextMultiXactId(), pgcform->relminmxid)))
1408  {
1409  pgcform->relminmxid = minmulti;
1410  dirty = true;
1411  }
1412 
1413  /* If anything changed, write out the tuple. */
1414  if (dirty)
1415  heap_inplace_update(rd, ctup);
1416 
1418 }
signed int int32
Definition: c.h:429
float float4
Definition: c.h:564
#define RelationGetRelid(relation)
Definition: rel.h:478
TriggerDesc * trigdesc
Definition: rel.h:115
RuleLock * rd_rules
Definition: rel.h:113
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:177

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

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

◆ vacuum()

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

Definition at line 298 of file vacuum.c.

300 {
301  static bool in_vacuum = false;
302 
303  const char *stmttype;
304  volatile bool in_outer_xact,
305  use_own_xacts;
306 
307  Assert(params != NULL);
308 
309  stmttype = (params->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
310 
311  /*
312  * We cannot run VACUUM inside a user transaction block; if we were inside
313  * a transaction, then our commit- and start-transaction-command calls
314  * would not have the intended effect! There are numerous other subtle
315  * dependencies on this, too.
316  *
317  * ANALYZE (without VACUUM) can run either way.
318  */
319  if (params->options & VACOPT_VACUUM)
320  {
321  PreventInTransactionBlock(isTopLevel, stmttype);
322  in_outer_xact = false;
323  }
324  else
325  in_outer_xact = IsInTransactionBlock(isTopLevel);
326 
327  /*
328  * Due to static variables vac_context, anl_context and vac_strategy,
329  * vacuum() is not reentrant. This matters when VACUUM FULL or ANALYZE
330  * calls a hostile index expression that itself calls ANALYZE.
331  */
332  if (in_vacuum)
333  ereport(ERROR,
334  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
335  errmsg("%s cannot be executed from VACUUM or ANALYZE",
336  stmttype)));
337 
338  /*
339  * Sanity check DISABLE_PAGE_SKIPPING option.
340  */
341  if ((params->options & VACOPT_FULL) != 0 &&
342  (params->options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
343  ereport(ERROR,
344  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
345  errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
346 
347  /* sanity check for PROCESS_TOAST */
348  if ((params->options & VACOPT_FULL) != 0 &&
349  (params->options & VACOPT_PROCESS_TOAST) == 0)
350  ereport(ERROR,
351  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
352  errmsg("PROCESS_TOAST required with VACUUM FULL")));
353 
354  /*
355  * Send info about dead objects to the statistics collector, unless we are
356  * in autovacuum --- autovacuum.c does this for itself.
357  */
358  if ((params->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
360 
361  /*
362  * Create special memory context for cross-transaction storage.
363  *
364  * Since it is a child of PortalContext, it will go away eventually even
365  * if we suffer an error; there's no need for special abort cleanup logic.
366  */
368  "Vacuum",
370 
371  /*
372  * If caller didn't give us a buffer strategy object, make one in the
373  * cross-transaction memory context.
374  */
375  if (bstrategy == NULL)
376  {
378 
379  bstrategy = GetAccessStrategy(BAS_VACUUM);
380  MemoryContextSwitchTo(old_context);
381  }
382  vac_strategy = bstrategy;
383 
384  /*
385  * Build list of relation(s) to process, putting any new data in
386  * vac_context for safekeeping.
387  */
388  if (relations != NIL)
389  {
390  List *newrels = NIL;
391  ListCell *lc;
392 
393  foreach(lc, relations)
394  {
396  List *sublist;
397  MemoryContext old_context;
398 
399  sublist = expand_vacuum_rel(vrel, params->options);
400  old_context = MemoryContextSwitchTo(vac_context);
401  newrels = list_concat(newrels, sublist);
402  MemoryContextSwitchTo(old_context);
403  }
404  relations = newrels;
405  }
406  else
407  relations = get_all_vacuum_rels(params->options);
408 
409  /*
410  * Decide whether we need to start/commit our own transactions.
411  *
412  * For VACUUM (with or without ANALYZE): always do so, so that we can
413  * release locks as soon as possible. (We could possibly use the outer
414  * transaction for a one-table VACUUM, but handling TOAST tables would be
415  * problematic.)
416  *
417  * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
418  * start/commit our own transactions. Also, there's no need to do so if
419  * only processing one relation. For multiple relations when not within a
420  * transaction block, and also in an autovacuum worker, use own
421  * transactions so we can release locks sooner.
422  */
423  if (params->options & VACOPT_VACUUM)
424  use_own_xacts = true;
425  else
426  {
427  Assert(params->options & VACOPT_ANALYZE);
429  use_own_xacts = true;
430  else if (in_outer_xact)
431  use_own_xacts = false;
432  else if (list_length(relations) > 1)
433  use_own_xacts = true;
434  else
435  use_own_xacts = false;
436  }
437 
438  /*
439  * vacuum_rel expects to be entered with no transaction active; it will
440  * start and commit its own transaction. But we are called by an SQL
441  * command, and so we are executing inside a transaction already. We
442  * commit the transaction started in PostgresMain() here, and start
443  * another one before exiting to match the commit waiting for us back in
444  * PostgresMain().
445  */
446  if (use_own_xacts)
447  {
448  Assert(!in_outer_xact);
449 
450  /* ActiveSnapshot is not set by autovacuum */
451  if (ActiveSnapshotSet())
453 
454  /* matches the StartTransaction in PostgresMain() */
456  }
457 
458  /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
459  PG_TRY();
460  {
461  ListCell *cur;
462 
463  in_vacuum = true;
465  VacuumCostBalance = 0;
466  VacuumPageHit = 0;
467  VacuumPageMiss = 0;
468  VacuumPageDirty = 0;
471  VacuumActiveNWorkers = NULL;
472 
473  /*
474  * Loop to process each selected relation.
475  */
476  foreach(cur, relations)
477  {
479 
480  if (params->options & VACOPT_VACUUM)
481  {
482  if (!vacuum_rel(vrel->oid, vrel->relation, params))
483  continue;
484  }
485 
486  if (params->options & VACOPT_ANALYZE)
487  {
488  /*
489  * If using separate xacts, start one for analyze. Otherwise,
490  * we can use the outer transaction.
491  */
492  if (use_own_xacts)
493  {
495  /* functions in indexes may want a snapshot set */
497  }
498 
499  analyze_rel(vrel->oid, vrel->relation, params,
500  vrel->va_cols, in_outer_xact, vac_strategy);
501 
502  if (use_own_xacts)
503  {
506  }
507  else
508  {
509  /*
510  * If we're not using separate xacts, better separate the
511  * ANALYZE actions with CCIs. This avoids trouble if user
512  * says "ANALYZE t, t".
513  */
515  }
516  }
517  }
518  }
519  PG_FINALLY();
520  {
521  in_vacuum = false;
522  VacuumCostActive = false;
523  }
524  PG_END_TRY();
525 
526  /*
527  * Finish up processing.
528  */
529  if (use_own_xacts)
530  {
531  /* here, we are not in a transaction */
532 
533  /*
534  * This matches the CommitTransaction waiting for us in
535  * PostgresMain().
536  */
538  }
539 
540  if ((params->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
541  {
542  /*
543  * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
544  * (autovacuum.c does this for itself.)
545  */
547  }
548 
549  /*
550  * Clean up working storage --- note we must do this after
551  * StartTransactionCommand, else we might be trying to delete the active
552  * context!
553  */
555  vac_context = NULL;
556 }
@ BAS_VACUUM
Definition: bufmgr.h:33
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_END_TRY()
Definition: elog.h:324
#define PG_TRY()
Definition: elog.h:299
#define PG_FINALLY()
Definition: elog.h:316
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:542
int64 VacuumPageHit
Definition: globals.c:147
int64 VacuumPageMiss
Definition: globals.c:148
bool VacuumCostActive
Definition: globals.c:152
int64 VacuumPageDirty
Definition: globals.c:149
List * list_concat(List *list1, const List *list2)
Definition: list.c:540
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
MemoryContext PortalContext
Definition: mcxt.c:57
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
void pgstat_vacuum_stat(void)
Definition: pgstat.c:1121
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:813
void PopActiveSnapshot(void)
Definition: snapmgr.c:774
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
static List * get_all_vacuum_rels(int options)
Definition: vacuum.c:891
static List * expand_vacuum_rel(VacuumRelation *vrel, int options)
Definition: vacuum.c:752
static BufferAccessStrategy vac_strategy
Definition: vacuum.c:76
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
Definition: vacuum.c:1805
void vac_update_datfrozenxid(void)
Definition: vacuum.c:1440
bool IsInTransactionBlock(bool isTopLevel)
Definition: xact.c:3563
void CommandCounterIncrement(void)
Definition: xact.c:1073
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3450
void StartTransactionCommand(void)
Definition: xact.c:2909
void CommitTransactionCommand(void)
Definition: xact.c:3010

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, pgstat_vacuum_stat(), 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_PROCESS_TOAST, VACOPT_VACUUM, 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 2156 of file vacuum.c.

2157 {
2158  double msec = 0;
2159 
2160  /* Always check for interrupts */
2162 
2164  return;
2165 
2166  /*
2167  * For parallel vacuum, the delay is computed based on the shared cost
2168  * balance. See compute_parallel_delay.
2169  */
2170  if (VacuumSharedCostBalance != NULL)
2171  msec = compute_parallel_delay();
2172  else if (VacuumCostBalance >= VacuumCostLimit)
2174 
2175  /* Nap if appropriate */
2176  if (msec > 0)
2177  {
2178  if (msec > VacuumCostDelay * 4)
2179  msec = VacuumCostDelay * 4;
2180 
2181  (void) WaitLatch(MyLatch,
2183  msec,
2186 
2187  VacuumCostBalance = 0;
2188 
2189  /* update balance values for workers */
2191 
2192  /* Might have gotten an interrupt while sleeping */
2194  }
2195 }
void AutoVacuumUpdateDelay(void)
Definition: autovacuum.c:1784
volatile sig_atomic_t InterruptPending
Definition: globals.c:30
struct Latch * MyLatch
Definition: globals.c:57
void ResetLatch(Latch *latch)
Definition: latch.c:660
int WaitLatch(Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info)
Definition: latch.c:452
#define WL_TIMEOUT
Definition: latch.h:128
#define WL_EXIT_ON_PM_DEATH
Definition: latch.h:130
#define WL_LATCH_SET
Definition: latch.h:125
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:120
static double compute_parallel_delay(void)
Definition: vacuum.c:2220
@ WAIT_EVENT_VACUUM_DELAY
Definition: wait_event.h:147

References AutoVacuumUpdateDelay(), CHECK_FOR_INTERRUPTS, compute_parallel_delay(), InterruptPending, MyLatch, ResetLatch(), VacuumCostActive, VacuumCostBalance, VacuumCostDelay, VacuumCostLimit, VacuumSharedCostBalance, WAIT_EVENT_VACUUM_DELAY, WaitLatch(), WL_EXIT_ON_PM_DEATH, WL_LATCH_SET, and WL_TIMEOUT.

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_vacuum_heap_rel(), spgprocesspending(), and spgvacuumpage().

◆ vacuum_is_relation_owner()

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

Definition at line 566 of file vacuum.c.

567 {
568  char *relname;
569 
571 
572  /*
573  * Check permissions.
574  *
575  * We allow the user to vacuum or analyze a table if he is superuser, the
576  * table owner, or the database owner (but in the latter case, only if
577  * it's not a shared relation). pg_class_ownercheck includes the
578  * superuser case.
579  *
580  * Note we choose to treat permissions failure as a WARNING and keep
581  * trying to vacuum or analyze the rest of the DB --- is this appropriate?
582  */
583  if (pg_class_ownercheck(relid, GetUserId()) ||
584  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !reltuple->relisshared))
585  return true;
586 
587  relname = NameStr(reltuple->relname);
588 
589  if ((options & VACOPT_VACUUM) != 0)
590  {
591  if (reltuple->relisshared)
593  (errmsg("skipping \"%s\" --- only superuser can vacuum it",
594  relname)));
595  else if (reltuple->relnamespace == PG_CATALOG_NAMESPACE)
597  (errmsg("skipping \"%s\" --- only superuser or database owner can vacuum it",
598  relname)));
599  else
601  (errmsg("skipping \"%s\" --- only table or database owner can vacuum it",
602  relname)));
603 
604  /*
605  * For VACUUM ANALYZE, both logs could show up, but just generate
606  * information for VACUUM as that would be the first one to be
607  * processed.
608  */
609  return false;
610  }
611 
612  if ((options & VACOPT_ANALYZE) != 0)
613  {
614  if (reltuple->relisshared)
616  (errmsg("skipping \"%s\" --- only superuser can analyze it",
617  relname)));
618  else if (reltuple->relnamespace == PG_CATALOG_NAMESPACE)
620  (errmsg("skipping \"%s\" --- only superuser or database owner can analyze it",
621  relname)));
622  else
624  (errmsg("skipping \"%s\" --- only table or database owner can analyze it",
625  relname)));
626  }
627 
628  return false;
629 }
bool pg_database_ownercheck(Oid db_oid, Oid roleid)
Definition: aclchk.c:5238
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4820
#define NameStr(name)
Definition: c.h:681
Oid GetUserId(void)
Definition: miscinit.c:495
NameData relname
Definition: pg_class.h:38

References Assert(), ereport, errmsg(), GetUserId(), MyDatabaseId, NameStr, pg_class_ownercheck(), pg_database_ownercheck(), 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 640 of file vacuum.c.

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

Definition at line 1805 of file vacuum.c.

1806 {
1807  LOCKMODE lmode;
1808  Relation rel;
1809  LockRelId lockrelid;
1810  Oid toast_relid;
1811  Oid save_userid;
1812  int save_sec_context;
1813  int save_nestlevel;
1814 
1815  Assert(params != NULL);
1816 
1817  /* Begin a transaction for vacuuming this relation */
1819 
1820  if (!(params->options & VACOPT_FULL))
1821  {
1822  /*
1823  * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
1824  * other concurrent VACUUMs know that they can ignore this one while
1825  * determining their OldestXmin. (The reason we don't set it during a
1826  * full VACUUM is exactly that we may have to run user-defined
1827  * functions for functional indexes, and we want to make sure that if
1828  * they use the snapshot set above, any tuples it requires can't get
1829  * removed from other tables. An index function that depends on the
1830  * contents of other tables is arguably broken, but we won't break it
1831  * here by violating transaction semantics.)
1832  *
1833  * We also set the VACUUM_FOR_WRAPAROUND flag, which is passed down by
1834  * autovacuum; it's used to avoid canceling a vacuum that was invoked
1835  * in an emergency.
1836  *
1837  * Note: these flags remain set until CommitTransaction or
1838  * AbortTransaction. We don't want to clear them until we reset
1839  * MyProc->xid/xmin, otherwise GetOldestNonRemovableTransactionId()
1840  * might appear to go backwards, which is probably Not Good. (We also
1841  * set PROC_IN_VACUUM *before* taking our own snapshot, so that our
1842  * xmin doesn't become visible ahead of setting the flag.)
1843  */
1844  LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
1846  if (params->is_wraparound)
1849  LWLockRelease(ProcArrayLock);
1850  }
1851 
1852  /*
1853  * Need to acquire a snapshot to prevent pg_subtrans from being truncated,
1854  * cutoff xids in local memory wrapping around, and to have updated xmin
1855  * horizons.
1856  */
1858 
1859  /*
1860  * Check for user-requested abort. Note we want this to be inside a
1861  * transaction, so xact.c doesn't issue useless WARNING.
1862  */
1864 
1865  /*
1866  * Determine the type of lock we want --- hard exclusive lock for a FULL
1867  * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
1868  * way, we can be sure that no other backend is vacuuming the same table.
1869  */
1870  lmode = (params->options & VACOPT_FULL) ?
1872 
1873  /* open the relation and get the appropriate lock on it */
1874  rel = vacuum_open_relation(relid, relation, params->options,
1875  params->log_min_duration >= 0, lmode);
1876 
1877  /* leave if relation could not be opened or locked */
1878  if (!rel)
1879  {
1882  return false;
1883  }
1884 
1885  /*
1886  * Check if relation needs to be skipped based on ownership. This check
1887  * happens also when building the relation list to vacuum for a manual
1888  * operation, and needs to be done additionally here as VACUUM could
1889  * happen across multiple transactions where relation ownership could have
1890  * changed in-between. Make sure to only generate logs for VACUUM in this
1891  * case.
1892  */
1894  rel->rd_rel,
1895  params->options & VACOPT_VACUUM))
1896  {
1897  relation_close(rel, lmode);
1900  return false;
1901  }
1902 
1903  /*
1904  * Check that it's of a vacuumable relkind.
1905  */
1906  if (rel->rd_rel->relkind != RELKIND_RELATION &&
1907  rel->rd_rel->relkind != RELKIND_MATVIEW &&
1908  rel->rd_rel->relkind != RELKIND_TOASTVALUE &&
1909  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1910  {
1911  ereport(WARNING,
1912  (errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
1913  RelationGetRelationName(rel))));
1914  relation_close(rel, lmode);
1917  return false;
1918  }
1919 
1920  /*
1921  * Silently ignore tables that are temp tables of other backends ---
1922  * trying to vacuum these will lead to great unhappiness, since their
1923  * contents are probably not up-to-date on disk. (We don't throw a
1924  * warning here; it would just lead to chatter during a database-wide
1925  * VACUUM.)
1926  */
1927  if (RELATION_IS_OTHER_TEMP(rel))
1928  {
1929  relation_close(rel, lmode);
1932  return false;
1933  }
1934 
1935  /*
1936  * Silently ignore partitioned tables as there is no work to be done. The
1937  * useful work is on their child partitions, which have been queued up for
1938  * us separately.
1939  */
1940  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1941  {
1942  relation_close(rel, lmode);
1945  /* It's OK to proceed with ANALYZE on this table */
1946  return true;
1947  }
1948 
1949  /*
1950  * Get a session-level lock too. This will protect our access to the
1951  * relation across multiple transactions, so that we can vacuum the
1952  * relation's TOAST table (if any) secure in the knowledge that no one is
1953  * deleting the parent relation.
1954  *
1955  * NOTE: this cannot block, even if someone else is waiting for access,
1956  * because the lock manager knows that both lock requests are from the
1957  * same process.
1958  */
1959  lockrelid = rel->rd_lockInfo.lockRelId;
1960  LockRelationIdForSession(&lockrelid, lmode);
1961 
1962  /*
1963  * Set index_cleanup option based on index_cleanup reloption if it wasn't
1964  * specified in VACUUM command, or when running in an autovacuum worker
1965  */
1966  if (params->index_cleanup == VACOPTVALUE_UNSPECIFIED)
1967  {
1968  StdRdOptIndexCleanup vacuum_index_cleanup;
1969 
1970  if (rel->rd_options == NULL)
1971  vacuum_index_cleanup = STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO;
1972  else
1973  vacuum_index_cleanup =
1974  ((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup;
1975 
1976  if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO)
1977  params->index_cleanup = VACOPTVALUE_AUTO;
1978  else if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON)
1980  else
1981  {
1982  Assert(vacuum_index_cleanup ==
1985  }
1986  }
1987 
1988  /*
1989  * Set truncate option based on truncate reloption if it wasn't specified
1990  * in VACUUM command, or when running in an autovacuum worker
1991  */
1992  if (params->truncate == VACOPTVALUE_UNSPECIFIED)
1993  {
1994  if (rel->rd_options == NULL ||
1995  ((StdRdOptions *) rel->rd_options)->vacuum_truncate)
1996  params->truncate = VACOPTVALUE_ENABLED;
1997  else
1998  params->truncate = VACOPTVALUE_DISABLED;
1999  }
2000 
2001  /*
2002  * Remember the relation's TOAST relation for later, if the caller asked
2003  * us to process it. In VACUUM FULL, though, the toast table is
2004  * automatically rebuilt by cluster_rel so we shouldn't recurse to it.
2005  */
2006  if ((params->options & VACOPT_PROCESS_TOAST) != 0 &&
2007  (params->options & VACOPT_FULL) == 0)
2008  toast_relid = rel->rd_rel->reltoastrelid;
2009  else
2010  toast_relid = InvalidOid;
2011 
2012  /*
2013  * Switch to the table owner's userid, so that any index functions are run
2014  * as that user. Also lock down security-restricted operations and
2015  * arrange to make GUC variable changes local to this command. (This is
2016  * unnecessary, but harmless, for lazy VACUUM.)
2017  */
2018  GetUserIdAndSecContext(&save_userid, &save_sec_context);
2019  SetUserIdAndSecContext(rel->rd_rel->relowner,
2020  save_sec_context | SECURITY_RESTRICTED_OPERATION);
2021  save_nestlevel = NewGUCNestLevel();
2022 
2023  /*
2024  * Do the actual work --- either FULL or "lazy" vacuum
2025  */
2026  if (params->options & VACOPT_FULL)
2027  {
2028  ClusterParams cluster_params = {0};
2029 
2030  /* close relation before vacuuming, but hold lock until commit */
2031  relation_close(rel, NoLock);
2032  rel = NULL;
2033 
2034  if ((params->options & VACOPT_VERBOSE) != 0)
2035  cluster_params.options |= CLUOPT_VERBOSE;
2036 
2037  /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
2038  cluster_rel(relid, InvalidOid, &cluster_params);
2039  }
2040  else
2041  table_relation_vacuum(rel, params, vac_strategy);
2042 
2043  /* Roll back any GUC changes executed by index functions */
2044  AtEOXact_GUC(false, save_nestlevel);
2045 
2046  /* Restore userid and security context */
2047  SetUserIdAndSecContext(save_userid, save_sec_context);
2048 
2049  /* all done with this class, but hold lock until commit */
2050  if (rel)
2051  relation_close(rel, NoLock);
2052 
2053  /*
2054  * Complete the transaction and free all temporary memory used.
2055  */
2058 
2059  /*
2060  * If the relation has a secondary toast rel, vacuum that too while we
2061  * still hold the session lock on the main table. Note however that
2062  * "analyze" will not get done on the toast table. This is good, because
2063  * the toaster always uses hardcoded index access and statistics are
2064  * totally unimportant for toast relations.
2065  */
2066  if (toast_relid != InvalidOid)
2067  vacuum_rel(toast_relid, NULL, params);
2068 
2069  /*
2070  * Now release the session-level lock on the main table.
2071  */
2072  UnlockRelationIdForSession(&lockrelid, lmode);
2073 
2074  /* Report that we really did it. */
2075  return true;
2076 }
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
Definition: cluster.c:277
#define CLUOPT_VERBOSE
Definition: cluster.h:24
int NewGUCNestLevel(void)
Definition: guc.c:6259
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:6273
void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:370
void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:383
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:312
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:600
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:607
#define PROC_IN_VACUUM
Definition: proc.h:55
#define PROC_VACUUM_FOR_WRAPAROUND
Definition: proc.h:57
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:632
StdRdOptIndexCleanup
Definition: rel.h:313
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO
Definition: rel.h:314
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF
Definition: rel.h:315
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON
Definition: rel.h:316
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
PGPROC * MyProc
Definition: proc.c:68
PROC_HDR * ProcGlobal
Definition: proc.c:80
bits32 options
Definition: cluster.h:29
LockRelId lockRelId
Definition: rel.h:45
Definition: rel.h:38
uint8 statusFlags
Definition: proc.h:192
int pgxactoff
Definition: proc.h:151
uint8 * statusFlags
Definition: proc.h:336
LockInfoData rd_lockInfo
Definition: rel.h:112
bytea * rd_options
Definition: rel.h:171
static void table_relation_vacuum(Relation rel, struct VacuumParams *params, BufferAccessStrategy bstrategy)
Definition: tableam.h:1677
Relation vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
Definition: vacuum.c:640

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_TOAST, VACOPT_VACUUM, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_DISABLED, VACOPTVALUE_ENABLED, VACOPTVALUE_UNSPECIFIED, vacuum_is_relation_owner(), vacuum_open_relation(), and WARNING.

Referenced by vacuum().

◆ vacuum_set_xid_limits()

void vacuum_set_xid_limits ( Relation  rel,
int  freeze_min_age,
int  freeze_table_age,
int  multixact_freeze_min_age,
int  multixact_freeze_table_age,
TransactionId oldestXmin,
TransactionId freezeLimit,
TransactionId xidFullScanLimit,
MultiXactId multiXactCutoff,
MultiXactId mxactFullScanLimit 
)

Definition at line 964 of file vacuum.c.

974 {
975  int freezemin;
976  int mxid_freezemin;
977  int effective_multixact_freeze_max_age;
978  TransactionId limit;
979  TransactionId safeLimit;
980  MultiXactId oldestMxact;
981  MultiXactId mxactLimit;
982  MultiXactId safeMxactLimit;
983 
984  /*
985  * We can always ignore processes running lazy vacuum. This is because we
986  * use these values only for deciding which tuples we must keep in the
987  * tables. Since lazy vacuum doesn't write its XID anywhere (usually no
988  * XID assigned), it's safe to ignore it. In theory it could be
989  * problematic to ignore lazy vacuums in a full vacuum, but keep in mind
990  * that only one vacuum process can be working on a particular table at
991  * any time, and that each vacuum is always an independent transaction.
992  */
993  *oldestXmin = GetOldestNonRemovableTransactionId(rel);
994 
996  {
997  TransactionId limit_xmin;
998  TimestampTz limit_ts;
999 
1000  if (TransactionIdLimitedForOldSnapshots(*oldestXmin, rel,
1001  &limit_xmin, &limit_ts))
1002  {
1003  /*
1004  * TODO: We should only set the threshold if we are pruning on the
1005  * basis of the increased limits. Not as crucial here as it is
1006  * for opportunistic pruning (which often happens at a much higher
1007  * frequency), but would still be a significant improvement.
1008  */
1009  SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin);
1010  *oldestXmin = limit_xmin;
1011  }
1012  }
1013 
1014  Assert(TransactionIdIsNormal(*oldestXmin));
1015 
1016  /*
1017  * Determine the minimum freeze age to use: as specified by the caller, or
1018  * vacuum_freeze_min_age, but in any case not more than half
1019  * autovacuum_freeze_max_age, so that autovacuums to prevent XID
1020  * wraparound won't occur too frequently.
1021  */
1022  freezemin = freeze_min_age;
1023  if (freezemin < 0)
1024  freezemin = vacuum_freeze_min_age;
1025  freezemin = Min(freezemin, autovacuum_freeze_max_age / 2);
1026  Assert(freezemin >= 0);
1027 
1028  /*
1029  * Compute the cutoff XID, being careful not to generate a "permanent" XID
1030  */
1031  limit = *oldestXmin - freezemin;
1032  if (!TransactionIdIsNormal(limit))
1033  limit = FirstNormalTransactionId;
1034 
1035  /*
1036  * If oldestXmin is very far back (in practice, more than
1037  * autovacuum_freeze_max_age / 2 XIDs old), complain and force a minimum
1038  * freeze age of zero.
1039  */
1041  if (!TransactionIdIsNormal(safeLimit))
1042  safeLimit = FirstNormalTransactionId;
1043 
1044  if (TransactionIdPrecedes(limit, safeLimit))
1045  {
1046  ereport(WARNING,
1047  (errmsg("oldest xmin is far in the past"),
1048  errhint("Close open transactions soon to avoid wraparound problems.\n"
1049  "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1050  limit = *oldestXmin;
1051  }
1052 
1053  *freezeLimit = limit;
1054 
1055  /*
1056  * Compute the multixact age for which freezing is urgent. This is
1057  * normally autovacuum_multixact_freeze_max_age, but may be less if we are
1058  * short of multixact member space.
1059  */
1060  effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
1061 
1062  /*
1063  * Determine the minimum multixact freeze age to use: as specified by
1064  * caller, or vacuum_multixact_freeze_min_age, but in any case not more
1065  * than half effective_multixact_freeze_max_age, so that autovacuums to
1066  * prevent MultiXact wraparound won't occur too frequently.
1067  */
1068  mxid_freezemin = multixact_freeze_min_age;
1069  if (mxid_freezemin < 0)
1070  mxid_freezemin = vacuum_multixact_freeze_min_age;
1071  mxid_freezemin = Min(mxid_freezemin,
1072  effective_multixact_freeze_max_age / 2);
1073  Assert(mxid_freezemin >= 0);
1074 
1075  /* compute the cutoff multi, being careful to generate a valid value */
1076  oldestMxact = GetOldestMultiXactId();
1077  mxactLimit = oldestMxact - mxid_freezemin;
1078  if (mxactLimit < FirstMultiXactId)
1079  mxactLimit = FirstMultiXactId;
1080 
1081  safeMxactLimit =
1082  ReadNextMultiXactId() - effective_multixact_freeze_max_age;
1083  if (safeMxactLimit < FirstMultiXactId)
1084  safeMxactLimit = FirstMultiXactId;
1085 
1086  if (MultiXactIdPrecedes(mxactLimit, safeMxactLimit))
1087  {
1088  ereport(WARNING,
1089  (errmsg("oldest multixact is far in the past"),
1090  errhint("Close open transactions with multixacts soon to avoid wraparound problems.")));
1091  /* Use the safe limit, unless an older mxact is still running */
1092  if (MultiXactIdPrecedes(oldestMxact, safeMxactLimit))
1093  mxactLimit = oldestMxact;
1094  else
1095  mxactLimit = safeMxactLimit;
1096  }
1097 
1098  *multiXactCutoff = mxactLimit;
1099 
1100  if (xidFullScanLimit != NULL)
1101  {
1102  int freezetable;
1103 
1104  Assert(mxactFullScanLimit != NULL);
1105 
1106  /*
1107  * Determine the table freeze age to use: as specified by the caller,
1108  * or vacuum_freeze_table_age, but in any case not more than
1109  * autovacuum_freeze_max_age * 0.95, so that if you have e.g nightly
1110  * VACUUM schedule, the nightly VACUUM gets a chance to freeze tuples
1111  * before anti-wraparound autovacuum is launched.
1112  */
1113  freezetable = freeze_table_age;
1114  if (freezetable < 0)
1115  freezetable = vacuum_freeze_table_age;
1116  freezetable = Min(freezetable, autovacuum_freeze_max_age * 0.95);
1117  Assert(freezetable >= 0);
1118 
1119  /*
1120  * Compute XID limit causing a full-table vacuum, being careful not to
1121  * generate a "permanent" XID.
1122  */
1123  limit = ReadNextTransactionId() - freezetable;
1124  if (!TransactionIdIsNormal(limit))
1125  limit = FirstNormalTransactionId;
1126 
1127  *xidFullScanLimit = limit;
1128 
1129  /*
1130  * Similar to the above, determine the table freeze age to use for
1131  * multixacts: as specified by the caller, or
1132  * vacuum_multixact_freeze_table_age, but in any case not more than
1133  * autovacuum_multixact_freeze_table_age * 0.95, so that if you have
1134  * e.g. nightly VACUUM schedule, the nightly VACUUM gets a chance to
1135  * freeze multixacts before anti-wraparound autovacuum is launched.
1136  */
1137  freezetable = multixact_freeze_table_age;
1138  if (freezetable < 0)
1139  freezetable = vacuum_multixact_freeze_table_age;
1140  freezetable = Min(freezetable,
1141  effective_multixact_freeze_max_age * 0.95);
1142  Assert(freezetable >= 0);
1143 
1144  /*
1145  * Compute MultiXact limit causing a full-table vacuum, being careful
1146  * to generate a valid MultiXact value.
1147  */
1148  mxactLimit = ReadNextMultiXactId() - freezetable;
1149  if (mxactLimit < FirstMultiXactId)
1150  mxactLimit = FirstMultiXactId;
1151 
1152  *mxactFullScanLimit = mxactLimit;
1153  }
1154  else
1155  {
1156  Assert(mxactFullScanLimit == NULL);
1157  }
1158 }
int autovacuum_freeze_max_age
Definition: autovacuum.c:124
#define Min(x, y)
Definition: c.h:986
int64 TimestampTz
Definition: timestamp.h:39
int errhint(const char *fmt,...)
Definition: elog.c:1151
int MultiXactMemberFreezeThreshold(void)
Definition: multixact.c:2828
#define FirstMultiXactId
Definition: multixact.h:25
bool TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, Relation relation, TransactionId *limit_xid, TimestampTz *limit_ts)
Definition: snapmgr.c:1766
void SetOldSnapshotThresholdTimestamp(TimestampTz ts, TransactionId xlimit)
Definition: snapmgr.c:1687
static bool OldSnapshotThresholdActive(void)
Definition: snapmgr.h:101
#define FirstNormalTransactionId
Definition: transam.h:34
int vacuum_freeze_min_age
Definition: vacuum.c:66
int vacuum_multixact_freeze_table_age
Definition: vacuum.c:69
int vacuum_freeze_table_age
Definition: vacuum.c:67
int vacuum_multixact_freeze_min_age
Definition: vacuum.c:68

References Assert(), autovacuum_freeze_max_age, ereport, errhint(), errmsg(), FirstMultiXactId, FirstNormalTransactionId, GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), Min, MultiXactIdPrecedes(), MultiXactMemberFreezeThreshold(), OldSnapshotThresholdActive(), ReadNextMultiXactId(), ReadNextTransactionId(), SetOldSnapshotThresholdTimestamp(), TransactionIdIsNormal, TransactionIdLimitedForOldSnapshots(), TransactionIdPrecedes(), 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_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( TransactionId  relfrozenxid,
MultiXactId  relminmxid 
)

Definition at line 1170 of file vacuum.c.

1171 {
1172  TransactionId xid_skip_limit;
1173  MultiXactId multi_skip_limit;
1174  int skip_index_vacuum;
1175 
1176  Assert(TransactionIdIsNormal(relfrozenxid));
1177  Assert(MultiXactIdIsValid(relminmxid));
1178 
1179  /*
1180  * Determine the index skipping age to use. In any case no less than
1181  * autovacuum_freeze_max_age * 1.05.
1182  */
1183  skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
1184 
1185  xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
1186  if (!TransactionIdIsNormal(xid_skip_limit))
1187  xid_skip_limit = FirstNormalTransactionId;
1188 
1189  if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
1190  {
1191  /* The table's relfrozenxid is too old */
1192  return true;
1193  }
1194 
1195  /*
1196  * Similar to above, determine the index skipping age to use for
1197  * multixact. In any case no less than autovacuum_multixact_freeze_max_age *
1198  * 1.05.
1199  */
1200  skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
1202 
1203  multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
1204  if (multi_skip_limit < FirstMultiXactId)
1205  multi_skip_limit = FirstMultiXactId;
1206 
1207  if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
1208  {
1209  /* The table's relminmxid is too old */
1210  return true;
1211  }
1212 
1213  return false;
1214 }
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:125
#define Max(x, y)
Definition: c.h:980
int vacuum_multixact_failsafe_age
Definition: vacuum.c:71
int vacuum_failsafe_age
Definition: vacuum.c:70

References Assert(), autovacuum_freeze_max_age, autovacuum_multixact_freeze_max_age, FirstMultiXactId, FirstNormalTransactionId, Max, MultiXactIdIsValid, MultiXactIdPrecedes(), ReadNextMultiXactId(), ReadNextTransactionId(), 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 75 of file vacuum.c.

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

◆ vac_strategy

BufferAccessStrategy vac_strategy
static

Definition at line 76 of file vacuum.c.

Referenced by vacuum(), and vacuum_rel().

◆ vacuum_failsafe_age

int vacuum_failsafe_age

Definition at line 70 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

int vacuum_freeze_min_age

Definition at line 66 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ vacuum_freeze_table_age

int vacuum_freeze_table_age

Definition at line 67 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ vacuum_multixact_failsafe_age

int vacuum_multixact_failsafe_age

Definition at line 71 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_multixact_freeze_min_age

int vacuum_multixact_freeze_min_age

Definition at line 68 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ vacuum_multixact_freeze_table_age

int vacuum_multixact_freeze_table_age

Definition at line 69 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

int VacuumCostBalanceLocal = 0

◆ VacuumSharedCostBalance

pg_atomic_uint32* VacuumSharedCostBalance = NULL