Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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/pg_database.h"
#include "catalog/pg_inherits.h"
#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/progress.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 "postmaster/interrupt.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/guc_hooks.h"
#include "utils/injection_point.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for vacuum.c:

Go to the source code of this file.

Macros

#define PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS   (NS_PER_S)
 

Functions

static Listexpand_vacuum_rel (VacuumRelation *vrel, MemoryContext vac_context, int options)
 
static Listget_all_vacuum_rels (MemoryContext vac_context, 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, BufferAccessStrategy bstrategy)
 
static double compute_parallel_delay (void)
 
static VacOptValue get_vacoptval_from_boolean (DefElem *def)
 
static bool vac_tid_reaped (ItemPointer itemptr, void *state)
 
bool check_vacuum_buffer_usage_limit (int *newval, void **extra, GucSource source)
 
void ExecVacuum (ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 
void vacuum (List *relations, const VacuumParams params, BufferAccessStrategy bstrategy, MemoryContext vac_context, bool isTopLevel)
 
bool vacuum_is_permitted_for_relation (Oid relid, Form_pg_class reltuple, bits32 options)
 
Relation vacuum_open_relation (Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
 
bool vacuum_get_cutoffs (Relation rel, const VacuumParams params, struct VacuumCutoffs *cutoffs)
 
bool vacuum_xid_failsafe_check (const struct VacuumCutoffs *cutoffs)
 
double vac_estimate_reltuples (Relation relation, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples)
 
void vac_update_relstats (Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, BlockNumber num_all_frozen_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti, bool *frozenxid_updated, bool *minmulti_updated, bool in_outer_xact)
 
void vac_update_datfrozenxid (void)
 
void vac_open_indexes (Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
 
void vac_close_indexes (int nindexes, Relation *Irel, LOCKMODE lockmode)
 
void vacuum_delay_point (bool is_analyze)
 
IndexBulkDeleteResultvac_bulkdel_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat, TidStore *dead_items, VacDeadItemsInfo *dead_items_info)
 
IndexBulkDeleteResultvac_cleanup_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat)
 

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
 
double vacuum_max_eager_freeze_failure_rate
 
bool track_cost_delay_timing
 
bool vacuum_truncate
 
double vacuum_cost_delay = 0
 
int vacuum_cost_limit = 200
 
int64 parallel_vacuum_worker_delay_ns = 0
 
bool VacuumFailsafeActive = false
 
pg_atomic_uint32VacuumSharedCostBalance = NULL
 
pg_atomic_uint32VacuumActiveNWorkers = NULL
 
int VacuumCostBalanceLocal = 0
 

Macro Definition Documentation

◆ PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS

#define PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS   (NS_PER_S)

Definition at line 69 of file vacuum.c.

Function Documentation

◆ check_vacuum_buffer_usage_limit()

bool check_vacuum_buffer_usage_limit ( int *  newval,
void **  extra,
GucSource  source 
)

Definition at line 138 of file vacuum.c.

140{
141 /* Value upper and lower hard limits are inclusive */
142 if (*newval == 0 || (*newval >= MIN_BAS_VAC_RING_SIZE_KB &&
144 return true;
145
146 /* Value does not fall within any allowable range */
147 GUC_check_errdetail("\"%s\" must be 0 or between %d kB and %d kB.",
148 "vacuum_buffer_usage_limit",
150
151 return false;
152}
#define newval
#define GUC_check_errdetail
Definition: guc.h:505
#define MIN_BAS_VAC_RING_SIZE_KB
Definition: miscadmin.h:277
#define MAX_BAS_VAC_RING_SIZE_KB
Definition: miscadmin.h:278

References GUC_check_errdetail, MAX_BAS_VAC_RING_SIZE_KB, MIN_BAS_VAC_RING_SIZE_KB, and newval.

◆ compute_parallel_delay()

static double compute_parallel_delay ( void  )
static

Definition at line 2575 of file vacuum.c.

2576{
2577 double msec = 0;
2578 uint32 shared_balance;
2579 int nworkers;
2580
2581 /* Parallel vacuum must be active */
2583
2585
2586 /* At least count itself */
2587 Assert(nworkers >= 1);
2588
2589 /* Update the shared cost balance value atomically */
2591
2592 /* Compute the total local balance for the current worker */
2594
2595 if ((shared_balance >= vacuum_cost_limit) &&
2596 (VacuumCostBalanceLocal > 0.5 * ((double) vacuum_cost_limit / nworkers)))
2597 {
2598 /* Compute sleep time based on the local cost balance */
2602 }
2603
2604 /*
2605 * Reset the local balance as we accumulated it into the shared value.
2606 */
2608
2609 return msec;
2610}
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:437
static uint32 pg_atomic_add_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:422
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:237
uint32_t uint32
Definition: c.h:538
int VacuumCostBalance
Definition: globals.c:157
Assert(PointerIsAligned(start, uint64))
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:116
double vacuum_cost_delay
Definition: vacuum.c:90
int VacuumCostBalanceLocal
Definition: vacuum.c:117
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:115
int vacuum_cost_limit
Definition: vacuum.c:91

References Assert(), pg_atomic_add_fetch_u32(), pg_atomic_read_u32(), pg_atomic_sub_fetch_u32(), vacuum_cost_delay, vacuum_cost_limit, VacuumActiveNWorkers, VacuumCostBalance, VacuumCostBalanceLocal, and VacuumSharedCostBalance.

Referenced by vacuum_delay_point().

◆ ExecVacuum()

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

Definition at line 161 of file vacuum.c.

162{
163 VacuumParams params;
164 BufferAccessStrategy bstrategy = NULL;
165 bool verbose = false;
166 bool skip_locked = false;
167 bool analyze = false;
168 bool freeze = false;
169 bool full = false;
170 bool disable_page_skipping = false;
171 bool process_main = true;
172 bool process_toast = true;
173 int ring_size;
174 bool skip_database_stats = false;
175 bool only_database_stats = false;
176 MemoryContext vac_context;
177 ListCell *lc;
178
179 /* index_cleanup and truncate values unspecified for now */
182
183 /* By default parallel vacuum is enabled */
184 params.nworkers = 0;
185
186 /* Will be set later if we recurse to a TOAST table. */
187 params.toast_parent = InvalidOid;
188
189 /*
190 * Set this to an invalid value so it is clear whether or not a
191 * BUFFER_USAGE_LIMIT was specified when making the access strategy.
192 */
193 ring_size = -1;
194
195 /* Parse options list */
196 foreach(lc, vacstmt->options)
197 {
198 DefElem *opt = (DefElem *) lfirst(lc);
199
200 /* Parse common options for VACUUM and ANALYZE */
201 if (strcmp(opt->defname, "verbose") == 0)
202 verbose = defGetBoolean(opt);
203 else if (strcmp(opt->defname, "skip_locked") == 0)
204 skip_locked = defGetBoolean(opt);
205 else if (strcmp(opt->defname, "buffer_usage_limit") == 0)
206 {
207 const char *hintmsg;
208 int result;
209 char *vac_buffer_size;
210
211 vac_buffer_size = defGetString(opt);
212
213 /*
214 * Check that the specified value is valid and the size falls
215 * within the hard upper and lower limits if it is not 0.
216 */
217 if (!parse_int(vac_buffer_size, &result, GUC_UNIT_KB, &hintmsg) ||
218 (result != 0 &&
219 (result < MIN_BAS_VAC_RING_SIZE_KB || result > MAX_BAS_VAC_RING_SIZE_KB)))
220 {
222 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
223 errmsg("BUFFER_USAGE_LIMIT option must be 0 or between %d kB and %d kB",
225 hintmsg ? errhint("%s", _(hintmsg)) : 0));
226 }
227
228 ring_size = result;
229 }
230 else if (!vacstmt->is_vacuumcmd)
232 (errcode(ERRCODE_SYNTAX_ERROR),
233 errmsg("unrecognized ANALYZE option \"%s\"", opt->defname),
234 parser_errposition(pstate, opt->location)));
235
236 /* Parse options available on VACUUM */
237 else if (strcmp(opt->defname, "analyze") == 0)
238 analyze = defGetBoolean(opt);
239 else if (strcmp(opt->defname, "freeze") == 0)
240 freeze = defGetBoolean(opt);
241 else if (strcmp(opt->defname, "full") == 0)
242 full = defGetBoolean(opt);
243 else if (strcmp(opt->defname, "disable_page_skipping") == 0)
244 disable_page_skipping = defGetBoolean(opt);
245 else if (strcmp(opt->defname, "index_cleanup") == 0)
246 {
247 /* Interpret no string as the default, which is 'auto' */
248 if (!opt->arg)
250 else
251 {
252 char *sval = defGetString(opt);
253
254 /* Try matching on 'auto' string, or fall back on boolean */
255 if (pg_strcasecmp(sval, "auto") == 0)
257 else
259 }
260 }
261 else if (strcmp(opt->defname, "process_main") == 0)
262 process_main = defGetBoolean(opt);
263 else if (strcmp(opt->defname, "process_toast") == 0)
264 process_toast = defGetBoolean(opt);
265 else if (strcmp(opt->defname, "truncate") == 0)
267 else if (strcmp(opt->defname, "parallel") == 0)
268 {
269 if (opt->arg == NULL)
270 {
272 (errcode(ERRCODE_SYNTAX_ERROR),
273 errmsg("parallel option requires a value between 0 and %d",
275 parser_errposition(pstate, opt->location)));
276 }
277 else
278 {
279 int nworkers;
280
281 nworkers = defGetInt32(opt);
282 if (nworkers < 0 || nworkers > MAX_PARALLEL_WORKER_LIMIT)
284 (errcode(ERRCODE_SYNTAX_ERROR),
285 errmsg("parallel workers for vacuum must be between 0 and %d",
287 parser_errposition(pstate, opt->location)));
288
289 /*
290 * Disable parallel vacuum, if user has specified parallel
291 * degree as zero.
292 */
293 if (nworkers == 0)
294 params.nworkers = -1;
295 else
296 params.nworkers = nworkers;
297 }
298 }
299 else if (strcmp(opt->defname, "skip_database_stats") == 0)
300 skip_database_stats = defGetBoolean(opt);
301 else if (strcmp(opt->defname, "only_database_stats") == 0)
302 only_database_stats = defGetBoolean(opt);
303 else
305 (errcode(ERRCODE_SYNTAX_ERROR),
306 errmsg("unrecognized VACUUM option \"%s\"", opt->defname),
307 parser_errposition(pstate, opt->location)));
308 }
309
310 /* Set vacuum options */
311 params.options =
313 (verbose ? VACOPT_VERBOSE : 0) |
314 (skip_locked ? VACOPT_SKIP_LOCKED : 0) |
315 (analyze ? VACOPT_ANALYZE : 0) |
316 (freeze ? VACOPT_FREEZE : 0) |
317 (full ? VACOPT_FULL : 0) |
318 (disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0) |
319 (process_main ? VACOPT_PROCESS_MAIN : 0) |
320 (process_toast ? VACOPT_PROCESS_TOAST : 0) |
321 (skip_database_stats ? VACOPT_SKIP_DATABASE_STATS : 0) |
322 (only_database_stats ? VACOPT_ONLY_DATABASE_STATS : 0);
323
324 /* sanity checks on options */
326 Assert((params.options & VACOPT_VACUUM) ||
327 !(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
328
329 if ((params.options & VACOPT_FULL) && params.nworkers > 0)
331 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
332 errmsg("VACUUM FULL cannot be performed in parallel")));
333
334 /*
335 * BUFFER_USAGE_LIMIT does nothing for VACUUM (FULL) so just raise an
336 * ERROR for that case. VACUUM (FULL, ANALYZE) does make use of it, so
337 * we'll permit that.
338 */
339 if (ring_size != -1 && (params.options & VACOPT_FULL) &&
340 !(params.options & VACOPT_ANALYZE))
342 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
343 errmsg("BUFFER_USAGE_LIMIT cannot be specified for VACUUM FULL")));
344
345 /*
346 * Make sure VACOPT_ANALYZE is specified if any column lists are present.
347 */
348 if (!(params.options & VACOPT_ANALYZE))
349 {
350 foreach(lc, vacstmt->rels)
351 {
353
354 if (vrel->va_cols != NIL)
356 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
357 errmsg("ANALYZE option must be specified when a column list is provided")));
358 }
359 }
360
361
362 /*
363 * Sanity check DISABLE_PAGE_SKIPPING option.
364 */
365 if ((params.options & VACOPT_FULL) != 0 &&
368 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
369 errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
370
371 /* sanity check for PROCESS_TOAST */
372 if ((params.options & VACOPT_FULL) != 0 &&
373 (params.options & VACOPT_PROCESS_TOAST) == 0)
375 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
376 errmsg("PROCESS_TOAST required with VACUUM FULL")));
377
378 /* sanity check for ONLY_DATABASE_STATS */
380 {
381 Assert(params.options & VACOPT_VACUUM);
382 if (vacstmt->rels != NIL)
384 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
385 errmsg("ONLY_DATABASE_STATS cannot be specified with a list of tables")));
386 /* don't require people to turn off PROCESS_TOAST/MAIN explicitly */
387 if (params.options & ~(VACOPT_VACUUM |
393 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
394 errmsg("ONLY_DATABASE_STATS cannot be specified with other VACUUM options")));
395 }
396
397 /*
398 * All freeze ages are zero if the FREEZE option is given; otherwise pass
399 * them as -1 which means to use the default values.
400 */
401 if (params.options & VACOPT_FREEZE)
402 {
403 params.freeze_min_age = 0;
404 params.freeze_table_age = 0;
405 params.multixact_freeze_min_age = 0;
407 }
408 else
409 {
410 params.freeze_min_age = -1;
411 params.freeze_table_age = -1;
412 params.multixact_freeze_min_age = -1;
413 params.multixact_freeze_table_age = -1;
414 }
415
416 /* user-invoked vacuum is never "for wraparound" */
417 params.is_wraparound = false;
418
419 /* user-invoked vacuum uses VACOPT_VERBOSE instead of log_min_duration */
420 params.log_min_duration = -1;
421
422 /*
423 * Later, in vacuum_rel(), we check if a reloption override was specified.
424 */
426
427 /*
428 * Create special memory context for cross-transaction storage.
429 *
430 * Since it is a child of PortalContext, it will go away eventually even
431 * if we suffer an error; there's no need for special abort cleanup logic.
432 */
434 "Vacuum",
436
437 /*
438 * Make a buffer strategy object in the cross-transaction memory context.
439 * We needn't bother making this for VACUUM (FULL) or VACUUM
440 * (ONLY_DATABASE_STATS) as they'll not make use of it. VACUUM (FULL,
441 * ANALYZE) is possible, so we'd better ensure that we make a strategy
442 * when we see ANALYZE.
443 */
444 if ((params.options & (VACOPT_ONLY_DATABASE_STATS |
445 VACOPT_FULL)) == 0 ||
446 (params.options & VACOPT_ANALYZE) != 0)
447 {
448
449 MemoryContext old_context = MemoryContextSwitchTo(vac_context);
450
451 Assert(ring_size >= -1);
452
453 /*
454 * If BUFFER_USAGE_LIMIT was specified by the VACUUM or ANALYZE
455 * command, it overrides the value of VacuumBufferUsageLimit. Either
456 * value may be 0, in which case GetAccessStrategyWithSize() will
457 * return NULL, effectively allowing full use of shared buffers.
458 */
459 if (ring_size == -1)
460 ring_size = VacuumBufferUsageLimit;
461
462 bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, ring_size);
463
464 MemoryContextSwitchTo(old_context);
465 }
466
467 /* Now go through the common routine */
468 vacuum(vacstmt->rels, params, bstrategy, vac_context, isTopLevel);
469
470 /* Finally, clean up the vacuum memory context */
471 MemoryContextDelete(vac_context);
472}
#define MAX_PARALLEL_WORKER_LIMIT
@ BAS_VACUUM
Definition: bufmgr.h:40
int32 defGetInt32(DefElem *def)
Definition: define.c:149
char * defGetString(DefElem *def)
Definition: define.c:35
bool defGetBoolean(DefElem *def)
Definition: define.c:94
int errhint(const char *fmt,...)
Definition: elog.c:1321
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define _(x)
Definition: elog.c:91
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
BufferAccessStrategy GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
Definition: freelist.c:509
int VacuumBufferUsageLimit
Definition: globals.c:149
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:2876
#define GUC_UNIT_KB
Definition: guc.h:232
int verbose
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
MemoryContext PortalContext
Definition: mcxt.c:175
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define NIL
Definition: pg_list.h:68
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define InvalidOid
Definition: postgres_ext.h:37
static long analyze(struct nfa *nfa)
Definition: regc_nfa.c:3051
char * defname
Definition: parsenodes.h:842
ParseLoc location
Definition: parsenodes.h:846
Node * arg
Definition: parsenodes.h:843
int nworkers
Definition: vacuum.h:246
int freeze_table_age
Definition: vacuum.h:221
VacOptValue truncate
Definition: vacuum.h:231
bits32 options
Definition: vacuum.h:219
int freeze_min_age
Definition: vacuum.h:220
bool is_wraparound
Definition: vacuum.h:226
int multixact_freeze_min_age
Definition: vacuum.h:222
int multixact_freeze_table_age
Definition: vacuum.h:224
int log_min_duration
Definition: vacuum.h:227
Oid toast_parent
Definition: vacuum.h:232
VacOptValue index_cleanup
Definition: vacuum.h:230
double max_eager_freeze_failure_rate
Definition: vacuum.h:239
List * options
Definition: parsenodes.h:3975
bool is_vacuumcmd
Definition: parsenodes.h:3977
List * rels
Definition: parsenodes.h:3976
void vacuum(List *relations, const VacuumParams params, BufferAccessStrategy bstrategy, MemoryContext vac_context, bool isTopLevel)
Definition: vacuum.c:497
double vacuum_max_eager_freeze_failure_rate
Definition: vacuum.c:80
static VacOptValue get_vacoptval_from_boolean(DefElem *def)
Definition: vacuum.c:2619
#define VACOPT_FREEZE
Definition: vacuum.h:183
#define VACOPT_SKIP_LOCKED
Definition: vacuum.h:185
#define VACOPT_VACUUM
Definition: vacuum.h:180
#define VACOPT_VERBOSE
Definition: vacuum.h:182
#define VACOPT_FULL
Definition: vacuum.h:184
#define VACOPT_SKIP_DATABASE_STATS
Definition: vacuum.h:189
@ VACOPTVALUE_AUTO
Definition: vacuum.h:203
@ VACOPTVALUE_UNSPECIFIED
Definition: vacuum.h:202
#define VACOPT_PROCESS_TOAST
Definition: vacuum.h:187
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition: vacuum.h:188
#define VACOPT_ONLY_DATABASE_STATS
Definition: vacuum.h:190
#define VACOPT_PROCESS_MAIN
Definition: vacuum.h:186
#define VACOPT_ANALYZE
Definition: vacuum.h:181

References _, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, analyze(), DefElem::arg, Assert(), BAS_VACUUM, defGetBoolean(), defGetInt32(), defGetString(), DefElem::defname, ereport, errcode(), errhint(), errmsg(), ERROR, VacuumParams::freeze_min_age, VacuumParams::freeze_table_age, get_vacoptval_from_boolean(), GetAccessStrategyWithSize(), GUC_UNIT_KB, VacuumParams::index_cleanup, InvalidOid, VacuumStmt::is_vacuumcmd, VacuumParams::is_wraparound, lfirst, lfirst_node, DefElem::location, VacuumParams::log_min_duration, MAX_BAS_VAC_RING_SIZE_KB, VacuumParams::max_eager_freeze_failure_rate, MAX_PARALLEL_WORKER_LIMIT, MemoryContextDelete(), MemoryContextSwitchTo(), MIN_BAS_VAC_RING_SIZE_KB, VacuumParams::multixact_freeze_min_age, VacuumParams::multixact_freeze_table_age, NIL, VacuumParams::nworkers, VacuumParams::options, VacuumStmt::options, parse_int(), parser_errposition(), pg_strcasecmp(), PortalContext, VacuumStmt::rels, VacuumParams::toast_parent, VacuumParams::truncate, VacuumRelation::va_cols, VACOPT_ANALYZE, VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_FREEZE, VACOPT_FULL, VACOPT_ONLY_DATABASE_STATS, VACOPT_PROCESS_MAIN, VACOPT_PROCESS_TOAST, VACOPT_SKIP_DATABASE_STATS, VACOPT_SKIP_LOCKED, VACOPT_VACUUM, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_UNSPECIFIED, vacuum(), vacuum_max_eager_freeze_failure_rate, VacuumBufferUsageLimit, and verbose.

Referenced by standard_ProcessUtility().

◆ expand_vacuum_rel()

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

Definition at line 886 of file vacuum.c.

888{
889 List *vacrels = NIL;
890 MemoryContext oldcontext;
891
892 /* If caller supplied OID, there's nothing we need do here. */
893 if (OidIsValid(vrel->oid))
894 {
895 oldcontext = MemoryContextSwitchTo(vac_context);
896 vacrels = lappend(vacrels, vrel);
897 MemoryContextSwitchTo(oldcontext);
898 }
899 else
900 {
901 /*
902 * Process a specific relation, and possibly partitions or child
903 * tables thereof.
904 */
905 Oid relid;
906 HeapTuple tuple;
907 Form_pg_class classForm;
908 bool include_children;
909 bool is_partitioned_table;
910 int rvr_opts;
911
912 /*
913 * Since autovacuum workers supply OIDs when calling vacuum(), no
914 * autovacuum worker should reach this code.
915 */
917
918 /*
919 * We transiently take AccessShareLock to protect the syscache lookup
920 * below, as well as find_all_inheritors's expectation that the caller
921 * holds some lock on the starting relation.
922 */
923 rvr_opts = (options & VACOPT_SKIP_LOCKED) ? RVR_SKIP_LOCKED : 0;
926 rvr_opts,
927 NULL, NULL);
928
929 /*
930 * If the lock is unavailable, emit the same log statement that
931 * vacuum_rel() and analyze_rel() would.
932 */
933 if (!OidIsValid(relid))
934 {
937 (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
938 errmsg("skipping vacuum of \"%s\" --- lock not available",
939 vrel->relation->relname)));
940 else
942 (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
943 errmsg("skipping analyze of \"%s\" --- lock not available",
944 vrel->relation->relname)));
945 return vacrels;
946 }
947
948 /*
949 * To check whether the relation is a partitioned table and its
950 * ownership, fetch its syscache entry.
951 */
952 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
953 if (!HeapTupleIsValid(tuple))
954 elog(ERROR, "cache lookup failed for relation %u", relid);
955 classForm = (Form_pg_class) GETSTRUCT(tuple);
956
957 /*
958 * Make a returnable VacuumRelation for this rel if the user has the
959 * required privileges.
960 */
961 if (vacuum_is_permitted_for_relation(relid, classForm, options))
962 {
963 oldcontext = MemoryContextSwitchTo(vac_context);
964 vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
965 relid,
966 vrel->va_cols));
967 MemoryContextSwitchTo(oldcontext);
968 }
969
970 /*
971 * Vacuuming a partitioned table with ONLY will not do anything since
972 * the partitioned table itself is empty. Issue a warning if the user
973 * requests this.
974 */
975 include_children = vrel->relation->inh;
976 is_partitioned_table = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
977 if ((options & VACOPT_VACUUM) && is_partitioned_table && !include_children)
979 (errmsg("VACUUM ONLY of partitioned table \"%s\" has no effect",
980 vrel->relation->relname)));
981
982 ReleaseSysCache(tuple);
983
984 /*
985 * Unless the user has specified ONLY, make relation list entries for
986 * its partitions or inheritance child tables. Note that the list
987 * returned by find_all_inheritors() includes the passed-in OID, so we
988 * have to skip that. There's no point in taking locks on the
989 * individual partitions or child tables yet, and doing so would just
990 * add unnecessary deadlock risk. For this last reason, we do not yet
991 * check the ownership of the partitions/tables, which get added to
992 * the list to process. Ownership will be checked later on anyway.
993 */
994 if (include_children)
995 {
996 List *part_oids = find_all_inheritors(relid, NoLock, NULL);
997 ListCell *part_lc;
998
999 foreach(part_lc, part_oids)
1000 {
1001 Oid part_oid = lfirst_oid(part_lc);
1002
1003 if (part_oid == relid)
1004 continue; /* ignore original table */
1005
1006 /*
1007 * We omit a RangeVar since it wouldn't be appropriate to
1008 * complain about failure to open one of these relations
1009 * later.
1010 */
1011 oldcontext = MemoryContextSwitchTo(vac_context);
1012 vacrels = lappend(vacrels, makeVacuumRelation(NULL,
1013 part_oid,
1014 vrel->va_cols));
1015 MemoryContextSwitchTo(oldcontext);
1016 }
1017 }
1018
1019 /*
1020 * Release lock again. This means that by the time we actually try to
1021 * process the table, it might be gone or renamed. In the former case
1022 * we'll silently ignore it; in the latter case we'll process it
1023 * anyway, but we must beware that the RangeVar doesn't necessarily
1024 * identify it anymore. This isn't ideal, perhaps, but there's little
1025 * practical alternative, since we're typically going to commit this
1026 * transaction and begin a new one between now and then. Moreover,
1027 * holding locks on multiple relations would create significant risk
1028 * of deadlock.
1029 */
1031 }
1032
1033 return vacrels;
1034}
#define OidIsValid(objectId)
Definition: c.h:774
#define WARNING
Definition: elog.h:36
#define elog(elevel,...)
Definition: elog.h:226
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
List * lappend(List *list, void *datum)
Definition: list.c:339
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
VacuumRelation * makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
Definition: makefuncs.c:907
#define AmAutoVacuumWorkerProcess()
Definition: miscadmin.h:382
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:440
@ RVR_SKIP_LOCKED
Definition: namespace.h:92
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
#define lfirst_oid(lc)
Definition: pg_list.h:174
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
unsigned int Oid
Definition: postgres_ext.h:32
Definition: pg_list.h:54
char * relname
Definition: primnodes.h:83
bool inh
Definition: primnodes.h:86
RangeVar * relation
Definition: parsenodes.h:3990
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:264
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:220
bool vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple, bits32 options)
Definition: vacuum.c:722

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

Referenced by vacuum().

◆ get_all_vacuum_rels()

static List * get_all_vacuum_rels ( MemoryContext  vac_context,
int  options 
)
static

Definition at line 1041 of file vacuum.c.

1042{
1043 List *vacrels = NIL;
1044 Relation pgclass;
1045 TableScanDesc scan;
1046 HeapTuple tuple;
1047
1048 pgclass = table_open(RelationRelationId, AccessShareLock);
1049
1050 scan = table_beginscan_catalog(pgclass, 0, NULL);
1051
1052 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1053 {
1054 Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
1055 MemoryContext oldcontext;
1056 Oid relid = classForm->oid;
1057
1058 /*
1059 * We include partitioned tables here; depending on which operation is
1060 * to be performed, caller will decide whether to process or ignore
1061 * them.
1062 */
1063 if (classForm->relkind != RELKIND_RELATION &&
1064 classForm->relkind != RELKIND_MATVIEW &&
1065 classForm->relkind != RELKIND_PARTITIONED_TABLE)
1066 continue;
1067
1068 /* check permissions of relation */
1069 if (!vacuum_is_permitted_for_relation(relid, classForm, options))
1070 continue;
1071
1072 /*
1073 * Build VacuumRelation(s) specifying the table OIDs to be processed.
1074 * We omit a RangeVar since it wouldn't be appropriate to complain
1075 * about failure to open one of these relations later.
1076 */
1077 oldcontext = MemoryContextSwitchTo(vac_context);
1078 vacrels = lappend(vacrels, makeVacuumRelation(NULL,
1079 relid,
1080 NIL));
1081 MemoryContextSwitchTo(oldcontext);
1082 }
1083
1084 table_endscan(scan);
1085 table_close(pgclass, AccessShareLock);
1086
1087 return vacrels;
1088}
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1346
@ ForwardScanDirection
Definition: sdir.h:28
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, ScanKeyData *key)
Definition: tableam.c:113
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:985

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

Referenced by vacuum().

◆ get_vacoptval_from_boolean()

static VacOptValue get_vacoptval_from_boolean ( DefElem def)
static

Definition at line 2619 of file vacuum.c.

2620{
2622}
@ 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,
TidStore dead_items,
VacDeadItemsInfo dead_items_info 
)

Definition at line 2630 of file vacuum.c.

2632{
2633 /* Do bulk deletion */
2634 istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2635 dead_items);
2636
2637 ereport(ivinfo->message_level,
2638 (errmsg("scanned index \"%s\" to remove %" PRId64 " row versions",
2640 dead_items_info->num_items)));
2641
2642 return istat;
2643}
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:805
#define RelationGetRelationName(relation)
Definition: rel.h:548
Relation index
Definition: genam.h:73
int message_level
Definition: genam.h:78
int64 num_items
Definition: vacuum.h:295
static bool vac_tid_reaped(ItemPointer itemptr, void *state)
Definition: vacuum.c:2677

References ereport, errmsg(), IndexVacuumInfo::index, index_bulk_delete(), IndexVacuumInfo::message_level, VacDeadItemsInfo::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 2651 of file vacuum.c.

2652{
2653 istat = index_vacuum_cleanup(ivinfo, istat);
2654
2655 if (istat)
2656 ereport(ivinfo->message_level,
2657 (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2659 istat->num_index_tuples,
2660 istat->num_pages),
2661 errdetail("%.0f index row versions were removed.\n"
2662 "%u index pages were newly deleted.\n"
2663 "%u index pages are currently deleted, of which %u are currently reusable.",
2664 istat->tuples_removed,
2665 istat->pages_newly_deleted,
2666 istat->pages_deleted, istat->pages_free)));
2667
2668 return istat;
2669}
int errdetail(const char *fmt,...)
Definition: elog.c:1207
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:826
BlockNumber pages_deleted
Definition: genam.h:109
BlockNumber pages_newly_deleted
Definition: genam.h:108
BlockNumber pages_free
Definition: genam.h:110
BlockNumber num_pages
Definition: genam.h:104
double tuples_removed
Definition: genam.h:107
double num_index_tuples
Definition: genam.h:106

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

2403{
2404 if (Irel == NULL)
2405 return;
2406
2407 while (nindexes--)
2408 {
2409 Relation ind = Irel[nindexes];
2410
2411 index_close(ind, lockmode);
2412 }
2413 pfree(Irel);
2414}
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
void pfree(void *pointer)
Definition: mcxt.c:1594

References index_close(), and pfree().

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

◆ vac_estimate_reltuples()

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

Definition at line 1333 of file vacuum.c.

1337{
1338 BlockNumber old_rel_pages = relation->rd_rel->relpages;
1339 double old_rel_tuples = relation->rd_rel->reltuples;
1340 double old_density;
1341 double unscanned_pages;
1342 double total_tuples;
1343
1344 /* If we did scan the whole table, just use the count as-is */
1345 if (scanned_pages >= total_pages)
1346 return scanned_tuples;
1347
1348 /*
1349 * When successive VACUUM commands scan the same few pages again and
1350 * again, without anything from the table really changing, there is a risk
1351 * that our beliefs about tuple density will gradually become distorted.
1352 * This might be caused by vacuumlazy.c implementation details, such as
1353 * its tendency to always scan the last heap page. Handle that here.
1354 *
1355 * If the relation is _exactly_ the same size according to the existing
1356 * pg_class entry, and only a few of its pages (less than 2%) were
1357 * scanned, keep the existing value of reltuples. Also keep the existing
1358 * value when only a subset of rel's pages <= a single page were scanned.
1359 *
1360 * (Note: we might be returning -1 here.)
1361 */
1362 if (old_rel_pages == total_pages &&
1363 scanned_pages < (double) total_pages * 0.02)
1364 return old_rel_tuples;
1365 if (scanned_pages <= 1)
1366 return old_rel_tuples;
1367
1368 /*
1369 * If old density is unknown, we can't do much except scale up
1370 * scanned_tuples to match total_pages.
1371 */
1372 if (old_rel_tuples < 0 || old_rel_pages == 0)
1373 return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1374
1375 /*
1376 * Okay, we've covered the corner cases. The normal calculation is to
1377 * convert the old measurement to a density (tuples per page), then
1378 * estimate the number of tuples in the unscanned pages using that figure,
1379 * and finally add on the number of tuples in the scanned pages.
1380 */
1381 old_density = old_rel_tuples / old_rel_pages;
1382 unscanned_pages = (double) total_pages - (double) scanned_pages;
1383 total_tuples = old_density * unscanned_pages + scanned_tuples;
1384 return floor(total_tuples + 0.5);
1385}
uint32 BlockNumber
Definition: block.h:31
Form_pg_class rd_rel
Definition: rel.h:111

References RelationData::rd_rel.

Referenced by lazy_scan_heap(), and statapprox_heap().

◆ vac_open_indexes()

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

Definition at line 2359 of file vacuum.c.

2361{
2362 List *indexoidlist;
2363 ListCell *indexoidscan;
2364 int i;
2365
2366 Assert(lockmode != NoLock);
2367
2368 indexoidlist = RelationGetIndexList(relation);
2369
2370 /* allocate enough memory for all indexes */
2371 i = list_length(indexoidlist);
2372
2373 if (i > 0)
2374 *Irel = (Relation *) palloc(i * sizeof(Relation));
2375 else
2376 *Irel = NULL;
2377
2378 /* collect just the ready indexes */
2379 i = 0;
2380 foreach(indexoidscan, indexoidlist)
2381 {
2382 Oid indexoid = lfirst_oid(indexoidscan);
2383 Relation indrel;
2384
2385 indrel = index_open(indexoid, lockmode);
2386 if (indrel->rd_index->indisready)
2387 (*Irel)[i++] = indrel;
2388 else
2389 index_close(indrel, lockmode);
2390 }
2391
2392 *nindexes = i;
2393
2394 list_free(indexoidlist);
2395}
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
int i
Definition: isn.c:77
void list_free(List *list)
Definition: list.c:1546
void * palloc(Size size)
Definition: mcxt.c:1365
static int list_length(const List *l)
Definition: pg_list.h:152
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4836
Form_pg_index rd_index
Definition: rel.h:192

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

2678{
2679 TidStore *dead_items = (TidStore *) state;
2680
2681 return TidStoreIsMember(dead_items, itemptr);
2682}
Definition: regguts.h:323
bool TidStoreIsMember(TidStore *ts, ItemPointer tid)
Definition: tidstore.c:421

References TidStoreIsMember().

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

1834{
1836 Relation relation;
1837 TableScanDesc scan;
1838 HeapTuple tuple;
1839 Oid oldestxid_datoid;
1840 Oid minmulti_datoid;
1841 bool bogus = false;
1842 bool frozenAlreadyWrapped = false;
1843
1844 /* Restrict task to one backend per cluster; see SimpleLruTruncate(). */
1845 LWLockAcquire(WrapLimitsVacuumLock, LW_EXCLUSIVE);
1846
1847 /* init oldest datoids to sync with my frozenXID/minMulti values */
1848 oldestxid_datoid = MyDatabaseId;
1849 minmulti_datoid = MyDatabaseId;
1850
1851 /*
1852 * Scan pg_database to compute the minimum datfrozenxid/datminmxid
1853 *
1854 * Since vac_update_datfrozenxid updates datfrozenxid/datminmxid in-place,
1855 * the values could change while we look at them. Fetch each one just
1856 * once to ensure sane behavior of the comparison logic. (Here, as in
1857 * many other places, we assume that fetching or updating an XID in shared
1858 * storage is atomic.)
1859 *
1860 * Note: we need not worry about a race condition with new entries being
1861 * inserted by CREATE DATABASE. Any such entry will have a copy of some
1862 * existing DB's datfrozenxid, and that source DB cannot be ours because
1863 * of the interlock against copying a DB containing an active backend.
1864 * Hence the new entry will not reduce the minimum. Also, if two VACUUMs
1865 * concurrently modify the datfrozenxid's of different databases, the
1866 * worst possible outcome is that pg_xact is not truncated as aggressively
1867 * as it could be.
1868 */
1869 relation = table_open(DatabaseRelationId, AccessShareLock);
1870
1871 scan = table_beginscan_catalog(relation, 0, NULL);
1872
1873 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1874 {
1875 volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
1876 TransactionId datfrozenxid = dbform->datfrozenxid;
1877 TransactionId datminmxid = dbform->datminmxid;
1878
1881
1882 /*
1883 * If database is in the process of getting dropped, or has been
1884 * interrupted while doing so, no connections to it are possible
1885 * anymore. Therefore we don't need to take it into account here.
1886 * Which is good, because it can't be processed by autovacuum either.
1887 */
1889 {
1890 elog(DEBUG2,
1891 "skipping invalid database \"%s\" while computing relfrozenxid",
1892 NameStr(dbform->datname));
1893 continue;
1894 }
1895
1896 /*
1897 * If things are working properly, no database should have a
1898 * datfrozenxid or datminmxid that is "in the future". However, such
1899 * cases have been known to arise due to bugs in pg_upgrade. If we
1900 * see any entries that are "in the future", chicken out and don't do
1901 * anything. This ensures we won't truncate clog before those
1902 * databases have been scanned and cleaned up. (We will issue the
1903 * "already wrapped" warning if appropriate, though.)
1904 */
1905 if (TransactionIdPrecedes(lastSaneFrozenXid, datfrozenxid) ||
1906 MultiXactIdPrecedes(lastSaneMinMulti, datminmxid))
1907 bogus = true;
1908
1909 if (TransactionIdPrecedes(nextXID, datfrozenxid))
1910 frozenAlreadyWrapped = true;
1911 else if (TransactionIdPrecedes(datfrozenxid, frozenXID))
1912 {
1913 frozenXID = datfrozenxid;
1914 oldestxid_datoid = dbform->oid;
1915 }
1916
1917 if (MultiXactIdPrecedes(datminmxid, minMulti))
1918 {
1919 minMulti = datminmxid;
1920 minmulti_datoid = dbform->oid;
1921 }
1922 }
1923
1924 table_endscan(scan);
1925
1926 table_close(relation, AccessShareLock);
1927
1928 /*
1929 * Do not truncate CLOG if we seem to have suffered wraparound already;
1930 * the computed minimum XID might be bogus. This case should now be
1931 * impossible due to the defenses in GetNewTransactionId, but we keep the
1932 * test anyway.
1933 */
1934 if (frozenAlreadyWrapped)
1935 {
1937 (errmsg("some databases have not been vacuumed in over 2 billion transactions"),
1938 errdetail("You might have already suffered transaction-wraparound data loss.")));
1939 LWLockRelease(WrapLimitsVacuumLock);
1940 return;
1941 }
1942
1943 /* chicken out if data is bogus in any other way */
1944 if (bogus)
1945 {
1946 LWLockRelease(WrapLimitsVacuumLock);
1947 return;
1948 }
1949
1950 /*
1951 * Advance the oldest value for commit timestamps before truncating, so
1952 * that if a user requests a timestamp for a transaction we're truncating
1953 * away right after this point, they get NULL instead of an ugly "file not
1954 * found" error from slru.c. This doesn't matter for xact/multixact
1955 * because they are not subject to arbitrary lookups from users.
1956 */
1957 AdvanceOldestCommitTsXid(frozenXID);
1958
1959 /*
1960 * Truncate CLOG, multixact and CommitTs to the oldest computed value.
1961 */
1962 TruncateCLOG(frozenXID, oldestxid_datoid);
1963 TruncateCommitTs(frozenXID);
1964 TruncateMultiXact(minMulti, minmulti_datoid);
1965
1966 /*
1967 * Update the wrap limit for GetNewTransactionId and creation of new
1968 * MultiXactIds. Note: these functions will also signal the postmaster
1969 * for an(other) autovac cycle if needed. XXX should we avoid possibly
1970 * signaling twice?
1971 */
1972 SetTransactionIdLimit(frozenXID, oldestxid_datoid);
1973 SetMultiXactIdLimit(minMulti, minmulti_datoid, false);
1974
1975 LWLockRelease(WrapLimitsVacuumLock);
1976}
#define NameStr(name)
Definition: c.h:751
uint32 TransactionId
Definition: c.h:657
void TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
Definition: clog.c:966
void AdvanceOldestCommitTsXid(TransactionId oldestXact)
Definition: commit_ts.c:914
void TruncateCommitTs(TransactionId oldestXact)
Definition: commit_ts.c:861
bool database_is_invalid_form(Form_pg_database datform)
Definition: dbcommands.c:3214
#define DEBUG2
Definition: elog.h:29
Oid MyDatabaseId
Definition: globals.c:94
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1174
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1894
@ LW_EXCLUSIVE
Definition: lwlock.h:112
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3265
void SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid, bool is_startup)
Definition: multixact.c:2292
void TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
Definition: multixact.c:3050
#define MultiXactIdIsValid(multi)
Definition: multixact.h:29
TransactionId datfrozenxid
Definition: pg_database.h:62
TransactionId datminmxid
Definition: pg_database.h:65
FormData_pg_database * Form_pg_database
Definition: pg_database.h:96
FormData_pg_database
Definition: pg_database.h:89
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
static TransactionId ReadNextTransactionId(void)
Definition: transam.h:315
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
void SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
Definition: varsup.c:372

References AccessShareLock, AdvanceOldestCommitTsXid(), Assert(), database_is_invalid_form(), datfrozenxid, datminmxid, DEBUG2, elog, ereport, errdetail(), errmsg(), FormData_pg_database, ForwardScanDirection, GETSTRUCT(), heap_getnext(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MultiXactIdPrecedes(), MyDatabaseId, NameStr, 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 1611 of file vacuum.c.

1612{
1613 HeapTuple tuple;
1614 Form_pg_database dbform;
1615 Relation relation;
1616 SysScanDesc scan;
1617 HeapTuple classTup;
1618 TransactionId newFrozenXid;
1619 MultiXactId newMinMulti;
1620 TransactionId lastSaneFrozenXid;
1621 MultiXactId lastSaneMinMulti;
1622 bool bogus = false;
1623 bool dirty = false;
1624 ScanKeyData key[1];
1625 void *inplace_state;
1626
1627 /*
1628 * Restrict this task to one backend per database. This avoids race
1629 * conditions that would move datfrozenxid or datminmxid backward. It
1630 * avoids calling vac_truncate_clog() with a datfrozenxid preceding a
1631 * datfrozenxid passed to an earlier vac_truncate_clog() call.
1632 */
1634
1635 /*
1636 * Initialize the "min" calculation with
1637 * GetOldestNonRemovableTransactionId(), which is a reasonable
1638 * approximation to the minimum relfrozenxid for not-yet-committed
1639 * pg_class entries for new tables; see AddNewRelationTuple(). So we
1640 * cannot produce a wrong minimum by starting with this.
1641 */
1642 newFrozenXid = GetOldestNonRemovableTransactionId(NULL);
1643
1644 /*
1645 * Similarly, initialize the MultiXact "min" with the value that would be
1646 * used on pg_class for new tables. See AddNewRelationTuple().
1647 */
1648 newMinMulti = GetOldestMultiXactId();
1649
1650 /*
1651 * Identify the latest relfrozenxid and relminmxid values that we could
1652 * validly see during the scan. These are conservative values, but it's
1653 * not really worth trying to be more exact.
1654 */
1655 lastSaneFrozenXid = ReadNextTransactionId();
1656 lastSaneMinMulti = ReadNextMultiXactId();
1657
1658 /*
1659 * We must seqscan pg_class to find the minimum Xid, because there is no
1660 * index that can help us here.
1661 *
1662 * See vac_truncate_clog() for the race condition to prevent.
1663 */
1664 relation = table_open(RelationRelationId, AccessShareLock);
1665
1666 scan = systable_beginscan(relation, InvalidOid, false,
1667 NULL, 0, NULL);
1668
1669 while ((classTup = systable_getnext(scan)) != NULL)
1670 {
1671 volatile FormData_pg_class *classForm = (Form_pg_class) GETSTRUCT(classTup);
1672 TransactionId relfrozenxid = classForm->relfrozenxid;
1673 TransactionId relminmxid = classForm->relminmxid;
1674
1675 /*
1676 * Only consider relations able to hold unfrozen XIDs (anything else
1677 * should have InvalidTransactionId in relfrozenxid anyway).
1678 */
1679 if (classForm->relkind != RELKIND_RELATION &&
1680 classForm->relkind != RELKIND_MATVIEW &&
1681 classForm->relkind != RELKIND_TOASTVALUE)
1682 {
1683 Assert(!TransactionIdIsValid(relfrozenxid));
1684 Assert(!MultiXactIdIsValid(relminmxid));
1685 continue;
1686 }
1687
1688 /*
1689 * Some table AMs might not need per-relation xid / multixid horizons.
1690 * It therefore seems reasonable to allow relfrozenxid and relminmxid
1691 * to not be set (i.e. set to their respective Invalid*Id)
1692 * independently. Thus validate and compute horizon for each only if
1693 * set.
1694 *
1695 * If things are working properly, no relation should have a
1696 * relfrozenxid or relminmxid that is "in the future". However, such
1697 * cases have been known to arise due to bugs in pg_upgrade. If we
1698 * see any entries that are "in the future", chicken out and don't do
1699 * anything. This ensures we won't truncate clog & multixact SLRUs
1700 * before those relations have been scanned and cleaned up.
1701 */
1702
1703 if (TransactionIdIsValid(relfrozenxid))
1704 {
1705 Assert(TransactionIdIsNormal(relfrozenxid));
1706
1707 /* check for values in the future */
1708 if (TransactionIdPrecedes(lastSaneFrozenXid, relfrozenxid))
1709 {
1710 bogus = true;
1711 break;
1712 }
1713
1714 /* determine new horizon */
1715 if (TransactionIdPrecedes(relfrozenxid, newFrozenXid))
1716 newFrozenXid = relfrozenxid;
1717 }
1718
1719 if (MultiXactIdIsValid(relminmxid))
1720 {
1721 /* check for values in the future */
1722 if (MultiXactIdPrecedes(lastSaneMinMulti, relminmxid))
1723 {
1724 bogus = true;
1725 break;
1726 }
1727
1728 /* determine new horizon */
1729 if (MultiXactIdPrecedes(relminmxid, newMinMulti))
1730 newMinMulti = relminmxid;
1731 }
1732 }
1733
1734 /* we're done with pg_class */
1735 systable_endscan(scan);
1736 table_close(relation, AccessShareLock);
1737
1738 /* chicken out if bogus data found */
1739 if (bogus)
1740 return;
1741
1742 Assert(TransactionIdIsNormal(newFrozenXid));
1743 Assert(MultiXactIdIsValid(newMinMulti));
1744
1745 /* Now fetch the pg_database tuple we need to update. */
1746 relation = table_open(DatabaseRelationId, RowExclusiveLock);
1747
1748 /*
1749 * Fetch a copy of the tuple to scribble on. We could check the syscache
1750 * tuple first. If that concluded !dirty, we'd avoid waiting on
1751 * concurrent heap_update() and would avoid exclusive-locking the buffer.
1752 * For now, don't optimize that.
1753 */
1754 ScanKeyInit(&key[0],
1755 Anum_pg_database_oid,
1756 BTEqualStrategyNumber, F_OIDEQ,
1758
1759 systable_inplace_update_begin(relation, DatabaseOidIndexId, true,
1760 NULL, 1, key, &tuple, &inplace_state);
1761
1762 if (!HeapTupleIsValid(tuple))
1763 elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
1764
1765 dbform = (Form_pg_database) GETSTRUCT(tuple);
1766
1767 /*
1768 * As in vac_update_relstats(), we ordinarily don't want to let
1769 * datfrozenxid go backward; but if it's "in the future" then it must be
1770 * corrupt and it seems best to overwrite it.
1771 */
1772 if (dbform->datfrozenxid != newFrozenXid &&
1773 (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid) ||
1774 TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid)))
1775 {
1776 dbform->datfrozenxid = newFrozenXid;
1777 dirty = true;
1778 }
1779 else
1780 newFrozenXid = dbform->datfrozenxid;
1781
1782 /* Ditto for datminmxid */
1783 if (dbform->datminmxid != newMinMulti &&
1784 (MultiXactIdPrecedes(dbform->datminmxid, newMinMulti) ||
1785 MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid)))
1786 {
1787 dbform->datminmxid = newMinMulti;
1788 dirty = true;
1789 }
1790 else
1791 newMinMulti = dbform->datminmxid;
1792
1793 if (dirty)
1794 systable_inplace_update_finish(inplace_state, tuple);
1795 else
1796 systable_inplace_update_cancel(inplace_state);
1797
1798 heap_freetuple(tuple);
1799 table_close(relation, RowExclusiveLock);
1800
1801 /*
1802 * If we were able to advance datfrozenxid or datminmxid, see if we can
1803 * truncate pg_xact and/or pg_multixact. Also do it if the shared
1804 * XID-wrap-limit info is stale, since this action will update that too.
1805 */
1806 if (dirty || ForceTransactionIdLimitUpdate())
1807 vac_truncate_clog(newFrozenXid, newMinMulti,
1808 lastSaneFrozenXid, lastSaneMinMulti);
1809}
TransactionId MultiXactId
Definition: c.h:667
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
void systable_inplace_update_cancel(void *state)
Definition: genam.c:902
void systable_inplace_update_begin(Relation relation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, const ScanKeyData *key, HeapTuple *oldtupcopy, void **state)
Definition: genam.c:807
void systable_inplace_update_finish(void *state, HeapTuple tuple)
Definition: genam.c:883
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
void LockDatabaseFrozenIds(LOCKMODE lockmode)
Definition: lmgr.c:491
#define ExclusiveLock
Definition: lockdefs.h:42
#define RowExclusiveLock
Definition: lockdefs.h:38
MultiXactId GetOldestMultiXactId(void)
Definition: multixact.c:2594
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:762
FormData_pg_class
Definition: pg_class.h:145
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:1953
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:1830
bool ForceTransactionIdLimitUpdate(void)
Definition: varsup.c:517

References AccessShareLock, Assert(), BTEqualStrategyNumber, elog, ERROR, ExclusiveLock, ForceTransactionIdLimitUpdate(), FormData_pg_class, GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, sort-test::key, LockDatabaseFrozenIds(), MultiXactIdIsValid, MultiXactIdPrecedes(), MyDatabaseId, ObjectIdGetDatum(), ReadNextMultiXactId(), ReadNextTransactionId(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), systable_inplace_update_begin(), systable_inplace_update_cancel(), systable_inplace_update_finish(), 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,
BlockNumber  num_all_frozen_pages,
bool  hasindex,
TransactionId  frozenxid,
MultiXactId  minmulti,
bool *  frozenxid_updated,
bool *  minmulti_updated,
bool  in_outer_xact 
)

Definition at line 1429 of file vacuum.c.

1437{
1438 Oid relid = RelationGetRelid(relation);
1439 Relation rd;
1440 ScanKeyData key[1];
1441 HeapTuple ctup;
1442 void *inplace_state;
1443 Form_pg_class pgcform;
1444 bool dirty,
1445 futurexid,
1446 futuremxid;
1447 TransactionId oldfrozenxid;
1448 MultiXactId oldminmulti;
1449
1450 rd = table_open(RelationRelationId, RowExclusiveLock);
1451
1452 /* Fetch a copy of the tuple to scribble on */
1453 ScanKeyInit(&key[0],
1454 Anum_pg_class_oid,
1455 BTEqualStrategyNumber, F_OIDEQ,
1456 ObjectIdGetDatum(relid));
1457 systable_inplace_update_begin(rd, ClassOidIndexId, true,
1458 NULL, 1, key, &ctup, &inplace_state);
1459 if (!HeapTupleIsValid(ctup))
1460 elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
1461 relid);
1462 pgcform = (Form_pg_class) GETSTRUCT(ctup);
1463
1464 /* Apply statistical updates, if any, to copied tuple */
1465
1466 dirty = false;
1467 if (pgcform->relpages != (int32) num_pages)
1468 {
1469 pgcform->relpages = (int32) num_pages;
1470 dirty = true;
1471 }
1472 if (pgcform->reltuples != (float4) num_tuples)
1473 {
1474 pgcform->reltuples = (float4) num_tuples;
1475 dirty = true;
1476 }
1477 if (pgcform->relallvisible != (int32) num_all_visible_pages)
1478 {
1479 pgcform->relallvisible = (int32) num_all_visible_pages;
1480 dirty = true;
1481 }
1482 if (pgcform->relallfrozen != (int32) num_all_frozen_pages)
1483 {
1484 pgcform->relallfrozen = (int32) num_all_frozen_pages;
1485 dirty = true;
1486 }
1487
1488 /* Apply DDL updates, but not inside an outer transaction (see above) */
1489
1490 if (!in_outer_xact)
1491 {
1492 /*
1493 * If we didn't find any indexes, reset relhasindex.
1494 */
1495 if (pgcform->relhasindex && !hasindex)
1496 {
1497 pgcform->relhasindex = false;
1498 dirty = true;
1499 }
1500
1501 /* We also clear relhasrules and relhastriggers if needed */
1502 if (pgcform->relhasrules && relation->rd_rules == NULL)
1503 {
1504 pgcform->relhasrules = false;
1505 dirty = true;
1506 }
1507 if (pgcform->relhastriggers && relation->trigdesc == NULL)
1508 {
1509 pgcform->relhastriggers = false;
1510 dirty = true;
1511 }
1512 }
1513
1514 /*
1515 * Update relfrozenxid, unless caller passed InvalidTransactionId
1516 * indicating it has no new data.
1517 *
1518 * Ordinarily, we don't let relfrozenxid go backwards. However, if the
1519 * stored relfrozenxid is "in the future" then it seems best to assume
1520 * it's corrupt, and overwrite with the oldest remaining XID in the table.
1521 * This should match vac_update_datfrozenxid() concerning what we consider
1522 * to be "in the future".
1523 */
1524 oldfrozenxid = pgcform->relfrozenxid;
1525 futurexid = false;
1526 if (frozenxid_updated)
1527 *frozenxid_updated = false;
1528 if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
1529 {
1530 bool update = false;
1531
1532 if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
1533 update = true;
1534 else if (TransactionIdPrecedes(ReadNextTransactionId(), oldfrozenxid))
1535 futurexid = update = true;
1536
1537 if (update)
1538 {
1539 pgcform->relfrozenxid = frozenxid;
1540 dirty = true;
1541 if (frozenxid_updated)
1542 *frozenxid_updated = true;
1543 }
1544 }
1545
1546 /* Similarly for relminmxid */
1547 oldminmulti = pgcform->relminmxid;
1548 futuremxid = false;
1549 if (minmulti_updated)
1550 *minmulti_updated = false;
1551 if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
1552 {
1553 bool update = false;
1554
1555 if (MultiXactIdPrecedes(oldminmulti, minmulti))
1556 update = true;
1557 else if (MultiXactIdPrecedes(ReadNextMultiXactId(), oldminmulti))
1558 futuremxid = update = true;
1559
1560 if (update)
1561 {
1562 pgcform->relminmxid = minmulti;
1563 dirty = true;
1564 if (minmulti_updated)
1565 *minmulti_updated = true;
1566 }
1567 }
1568
1569 /* If anything changed, write out the tuple. */
1570 if (dirty)
1571 systable_inplace_update_finish(inplace_state, ctup);
1572 else
1573 systable_inplace_update_cancel(inplace_state);
1574
1576
1577 if (futurexid)
1580 errmsg_internal("overwrote invalid relfrozenxid value %u with new value %u for table \"%s\"",
1581 oldfrozenxid, frozenxid,
1582 RelationGetRelationName(relation))));
1583 if (futuremxid)
1586 errmsg_internal("overwrote invalid relminmxid value %u with new value %u for table \"%s\"",
1587 oldminmulti, minmulti,
1588 RelationGetRelationName(relation))));
1589}
int32_t int32
Definition: c.h:534
float float4
Definition: c.h:634
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1161
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:42
#define RelationGetRelid(relation)
Definition: rel.h:514
TriggerDesc * trigdesc
Definition: rel.h:117
RuleLock * rd_rules
Definition: rel.h:115

References BTEqualStrategyNumber, elog, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, GETSTRUCT(), HeapTupleIsValid, sort-test::key, MultiXactIdIsValid, MultiXactIdPrecedes(), ObjectIdGetDatum(), RelationData::rd_rules, ReadNextMultiXactId(), ReadNextTransactionId(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_inplace_update_begin(), systable_inplace_update_cancel(), systable_inplace_update_finish(), table_close(), table_open(), TransactionIdIsNormal, TransactionIdPrecedes(), RelationData::trigdesc, and WARNING.

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

◆ vacuum()

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

Definition at line 497 of file vacuum.c.

499{
500 static bool in_vacuum = false;
501
502 const char *stmttype;
503 volatile bool in_outer_xact,
504 use_own_xacts;
505
506 stmttype = (params.options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
507
508 /*
509 * We cannot run VACUUM inside a user transaction block; if we were inside
510 * a transaction, then our commit- and start-transaction-command calls
511 * would not have the intended effect! There are numerous other subtle
512 * dependencies on this, too.
513 *
514 * ANALYZE (without VACUUM) can run either way.
515 */
516 if (params.options & VACOPT_VACUUM)
517 {
518 PreventInTransactionBlock(isTopLevel, stmttype);
519 in_outer_xact = false;
520 }
521 else
522 in_outer_xact = IsInTransactionBlock(isTopLevel);
523
524 /*
525 * Check for and disallow recursive calls. This could happen when VACUUM
526 * FULL or ANALYZE calls a hostile index expression that itself calls
527 * ANALYZE.
528 */
529 if (in_vacuum)
531 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
532 errmsg("%s cannot be executed from VACUUM or ANALYZE",
533 stmttype)));
534
535 /*
536 * Build list of relation(s) to process, putting any new data in
537 * vac_context for safekeeping.
538 */
540 {
541 /* We don't process any tables in this case */
542 Assert(relations == NIL);
543 }
544 else if (relations != NIL)
545 {
546 List *newrels = NIL;
547 ListCell *lc;
548
549 foreach(lc, relations)
550 {
552 List *sublist;
553 MemoryContext old_context;
554
555 sublist = expand_vacuum_rel(vrel, vac_context, params.options);
556 old_context = MemoryContextSwitchTo(vac_context);
557 newrels = list_concat(newrels, sublist);
558 MemoryContextSwitchTo(old_context);
559 }
560 relations = newrels;
561 }
562 else
563 relations = get_all_vacuum_rels(vac_context, params.options);
564
565 /*
566 * Decide whether we need to start/commit our own transactions.
567 *
568 * For VACUUM (with or without ANALYZE): always do so, so that we can
569 * release locks as soon as possible. (We could possibly use the outer
570 * transaction for a one-table VACUUM, but handling TOAST tables would be
571 * problematic.)
572 *
573 * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
574 * start/commit our own transactions. Also, there's no need to do so if
575 * only processing one relation. For multiple relations when not within a
576 * transaction block, and also in an autovacuum worker, use own
577 * transactions so we can release locks sooner.
578 */
579 if (params.options & VACOPT_VACUUM)
580 use_own_xacts = true;
581 else
582 {
583 Assert(params.options & VACOPT_ANALYZE);
585 use_own_xacts = true;
586 else if (in_outer_xact)
587 use_own_xacts = false;
588 else if (list_length(relations) > 1)
589 use_own_xacts = true;
590 else
591 use_own_xacts = false;
592 }
593
594 /*
595 * vacuum_rel expects to be entered with no transaction active; it will
596 * start and commit its own transaction. But we are called by an SQL
597 * command, and so we are executing inside a transaction already. We
598 * commit the transaction started in PostgresMain() here, and start
599 * another one before exiting to match the commit waiting for us back in
600 * PostgresMain().
601 */
602 if (use_own_xacts)
603 {
604 Assert(!in_outer_xact);
605
606 /* ActiveSnapshot is not set by autovacuum */
607 if (ActiveSnapshotSet())
609
610 /* matches the StartTransaction in PostgresMain() */
612 }
613
614 /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
615 PG_TRY();
616 {
617 ListCell *cur;
618
619 in_vacuum = true;
620 VacuumFailsafeActive = false;
626
627 /*
628 * Loop to process each selected relation.
629 */
630 foreach(cur, relations)
631 {
633
634 if (params.options & VACOPT_VACUUM)
635 {
636 if (!vacuum_rel(vrel->oid, vrel->relation, params, bstrategy))
637 continue;
638 }
639
640 if (params.options & VACOPT_ANALYZE)
641 {
642 /*
643 * If using separate xacts, start one for analyze. Otherwise,
644 * we can use the outer transaction.
645 */
646 if (use_own_xacts)
647 {
649 /* functions in indexes may want a snapshot set */
651 }
652
653 analyze_rel(vrel->oid, vrel->relation, params,
654 vrel->va_cols, in_outer_xact, bstrategy);
655
656 if (use_own_xacts)
657 {
659 /* standard_ProcessUtility() does CCI if !use_own_xacts */
662 }
663 else
664 {
665 /*
666 * If we're not using separate xacts, better separate the
667 * ANALYZE actions with CCIs. This avoids trouble if user
668 * says "ANALYZE t, t".
669 */
671 }
672 }
673
674 /*
675 * Ensure VacuumFailsafeActive has been reset before vacuuming the
676 * next relation.
677 */
678 VacuumFailsafeActive = false;
679 }
680 }
681 PG_FINALLY();
682 {
683 in_vacuum = false;
684 VacuumCostActive = false;
685 VacuumFailsafeActive = false;
687 }
688 PG_END_TRY();
689
690 /*
691 * Finish up processing.
692 */
693 if (use_own_xacts)
694 {
695 /* here, we are not in a transaction */
696
697 /*
698 * This matches the CommitTransaction waiting for us in
699 * PostgresMain().
700 */
702 }
703
704 if ((params.options & VACOPT_VACUUM) &&
706 {
707 /*
708 * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
709 */
711 }
712
713}
void VacuumUpdateCosts(void)
Definition: autovacuum.c:1667
void analyze_rel(Oid relid, RangeVar *relation, const VacuumParams params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
Definition: analyze.c:108
struct cursor * cur
Definition: ecpg.c:29
#define PG_TRY(...)
Definition: elog.h:372
#define PG_END_TRY(...)
Definition: elog.h:397
#define PG_FINALLY(...)
Definition: elog.h:389
bool VacuumCostActive
Definition: globals.c:158
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:680
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:810
void PopActiveSnapshot(void)
Definition: snapmgr.c:773
static List * expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, int options)
Definition: vacuum.c:886
static List * get_all_vacuum_rels(MemoryContext vac_context, int options)
Definition: vacuum.c:1041
void vac_update_datfrozenxid(void)
Definition: vacuum.c:1611
bool VacuumFailsafeActive
Definition: vacuum.c:109
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, BufferAccessStrategy bstrategy)
Definition: vacuum.c:1999
bool IsInTransactionBlock(bool isTopLevel)
Definition: xact.c:3781
void CommandCounterIncrement(void)
Definition: xact.c:1100
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3660
void StartTransactionCommand(void)
Definition: xact.c:3071
void CommitTransactionCommand(void)
Definition: xact.c:3169

References ActiveSnapshotSet(), AmAutoVacuumWorkerProcess, analyze_rel(), Assert(), CommandCounterIncrement(), CommitTransactionCommand(), cur, ereport, errcode(), errmsg(), ERROR, expand_vacuum_rel(), get_all_vacuum_rels(), GetTransactionSnapshot(), IsInTransactionBlock(), lfirst_node, list_concat(), list_length(), MemoryContextSwitchTo(), NIL, VacuumRelation::oid, VacuumParams::options, PG_END_TRY, PG_FINALLY, PG_TRY, PopActiveSnapshot(), PreventInTransactionBlock(), PushActiveSnapshot(), VacuumRelation::relation, StartTransactionCommand(), VacuumRelation::va_cols, vac_update_datfrozenxid(), VACOPT_ANALYZE, VACOPT_ONLY_DATABASE_STATS, VACOPT_SKIP_DATABASE_STATS, VACOPT_VACUUM, vacuum_rel(), VacuumActiveNWorkers, VacuumCostActive, VacuumCostBalance, VacuumCostBalanceLocal, VacuumFailsafeActive, VacuumSharedCostBalance, and VacuumUpdateCosts().

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 ( bool  is_analyze)

Definition at line 2423 of file vacuum.c.

2424{
2425 double msec = 0;
2426
2427 /* Always check for interrupts */
2429
2430 if (InterruptPending ||
2432 return;
2433
2434 /*
2435 * Autovacuum workers should reload the configuration file if requested.
2436 * This allows changes to [autovacuum_]vacuum_cost_limit and
2437 * [autovacuum_]vacuum_cost_delay to take effect while a table is being
2438 * vacuumed or analyzed.
2439 */
2441 {
2442 ConfigReloadPending = false;
2445 }
2446
2447 /*
2448 * If we disabled cost-based delays after reloading the config file,
2449 * return.
2450 */
2451 if (!VacuumCostActive)
2452 return;
2453
2454 /*
2455 * For parallel vacuum, the delay is computed based on the shared cost
2456 * balance. See compute_parallel_delay.
2457 */
2458 if (VacuumSharedCostBalance != NULL)
2459 msec = compute_parallel_delay();
2462
2463 /* Nap if appropriate */
2464 if (msec > 0)
2465 {
2466 instr_time delay_start;
2467
2468 if (msec > vacuum_cost_delay * 4)
2469 msec = vacuum_cost_delay * 4;
2470
2472 INSTR_TIME_SET_CURRENT(delay_start);
2473
2474 pgstat_report_wait_start(WAIT_EVENT_VACUUM_DELAY);
2475 pg_usleep(msec * 1000);
2477
2479 {
2480 instr_time delay_end;
2481 instr_time delay;
2482
2483 INSTR_TIME_SET_CURRENT(delay_end);
2484 INSTR_TIME_SET_ZERO(delay);
2485 INSTR_TIME_ACCUM_DIFF(delay, delay_end, delay_start);
2486
2487 /*
2488 * For parallel workers, we only report the delay time every once
2489 * in a while to avoid overloading the leader with messages and
2490 * interrupts.
2491 */
2492 if (IsParallelWorker())
2493 {
2494 static instr_time last_report_time;
2495 instr_time time_since_last_report;
2496
2497 Assert(!is_analyze);
2498
2499 /* Accumulate the delay time */
2501
2502 /* Calculate interval since last report */
2503 INSTR_TIME_SET_ZERO(time_since_last_report);
2504 INSTR_TIME_ACCUM_DIFF(time_since_last_report, delay_end, last_report_time);
2505
2506 /* If we haven't reported in a while, do so now */
2507 if (INSTR_TIME_GET_NANOSEC(time_since_last_report) >=
2509 {
2512
2513 /* Reset variables */
2514 last_report_time = delay_end;
2516 }
2517 }
2518 else if (is_analyze)
2520 INSTR_TIME_GET_NANOSEC(delay));
2521 else
2523 INSTR_TIME_GET_NANOSEC(delay));
2524 }
2525
2526 /*
2527 * We don't want to ignore postmaster death during very long vacuums
2528 * with vacuum_cost_delay configured. We can't use the usual
2529 * WaitLatch() approach here because we want microsecond-based sleep
2530 * durations above.
2531 */
2533 exit(1);
2534
2536
2537 /*
2538 * Balance and update limit values for autovacuum workers. We must do
2539 * this periodically, as the number of workers across which we are
2540 * balancing the limit may have changed.
2541 *
2542 * TODO: There may be better criteria for determining when to do this
2543 * besides "check after napping".
2544 */
2546
2547 /* Might have gotten an interrupt while sleeping */
2549 }
2550}
void AutoVacuumUpdateCostLimit(void)
Definition: autovacuum.c:1736
void pgstat_progress_parallel_incr_param(int index, int64 incr)
void pgstat_progress_incr_param(int index, int64 incr)
volatile sig_atomic_t InterruptPending
Definition: globals.c:32
bool IsUnderPostmaster
Definition: globals.c:120
void ProcessConfigFile(GucContext context)
Definition: guc-file.l:120
@ PGC_SIGHUP
Definition: guc.h:75
#define IsParallelWorker()
Definition: parallel.h:60
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_GET_NANOSEC(t)
Definition: instr_time.h:125
#define INSTR_TIME_SET_ZERO(t)
Definition: instr_time.h:172
#define INSTR_TIME_ACCUM_DIFF(x, y, z)
Definition: instr_time.h:184
volatile sig_atomic_t ConfigReloadPending
Definition: interrupt.c:27
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
#define PostmasterIsAlive()
Definition: pmsignal.h:107
#define PROGRESS_VACUUM_DELAY_TIME
Definition: progress.h:31
#define PROGRESS_ANALYZE_DELAY_TIME
Definition: progress.h:50
void pg_usleep(long microsec)
Definition: signal.c:53
bool track_cost_delay_timing
Definition: vacuum.c:81
static double compute_parallel_delay(void)
Definition: vacuum.c:2575
#define PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS
Definition: vacuum.c:69
int64 parallel_vacuum_worker_delay_ns
Definition: vacuum.c:94
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:69
static void pgstat_report_wait_end(void)
Definition: wait_event.h:85

References AmAutoVacuumWorkerProcess, Assert(), AutoVacuumUpdateCostLimit(), CHECK_FOR_INTERRUPTS, compute_parallel_delay(), ConfigReloadPending, INSTR_TIME_ACCUM_DIFF, INSTR_TIME_GET_NANOSEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SET_ZERO, InterruptPending, IsParallelWorker, IsUnderPostmaster, PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS, parallel_vacuum_worker_delay_ns, pg_usleep(), PGC_SIGHUP, pgstat_progress_incr_param(), pgstat_progress_parallel_incr_param(), pgstat_report_wait_end(), pgstat_report_wait_start(), PostmasterIsAlive, ProcessConfigFile(), PROGRESS_ANALYZE_DELAY_TIME, PROGRESS_VACUUM_DELAY_TIME, track_cost_delay_timing, vacuum_cost_delay, vacuum_cost_limit, VacuumCostActive, VacuumCostBalance, VacuumSharedCostBalance, and VacuumUpdateCosts().

Referenced by acquire_sample_rows(), blbulkdelete(), blvacuumcleanup(), btvacuumpage(), btvacuumscan(), 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(), gistvacuumscan(), hashbucketcleanup(), lazy_scan_heap(), lazy_vacuum_heap_rel(), spgprocesspending(), and spgvacuumscan().

◆ vacuum_get_cutoffs()

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

Definition at line 1103 of file vacuum.c.

1105{
1106 int freeze_min_age,
1107 multixact_freeze_min_age,
1108 freeze_table_age,
1109 multixact_freeze_table_age,
1110 effective_multixact_freeze_max_age;
1111 TransactionId nextXID,
1112 safeOldestXmin,
1113 aggressiveXIDCutoff;
1114 MultiXactId nextMXID,
1115 safeOldestMxact,
1116 aggressiveMXIDCutoff;
1117
1118 /* Use mutable copies of freeze age parameters */
1119 freeze_min_age = params.freeze_min_age;
1120 multixact_freeze_min_age = params.multixact_freeze_min_age;
1121 freeze_table_age = params.freeze_table_age;
1122 multixact_freeze_table_age = params.multixact_freeze_table_age;
1123
1124 /* Set pg_class fields in cutoffs */
1125 cutoffs->relfrozenxid = rel->rd_rel->relfrozenxid;
1126 cutoffs->relminmxid = rel->rd_rel->relminmxid;
1127
1128 /*
1129 * Acquire OldestXmin.
1130 *
1131 * We can always ignore processes running lazy vacuum. This is because we
1132 * use these values only for deciding which tuples we must keep in the
1133 * tables. Since lazy vacuum doesn't write its XID anywhere (usually no
1134 * XID assigned), it's safe to ignore it. In theory it could be
1135 * problematic to ignore lazy vacuums in a full vacuum, but keep in mind
1136 * that only one vacuum process can be working on a particular table at
1137 * any time, and that each vacuum is always an independent transaction.
1138 */
1140
1142
1143 /* Acquire OldestMxact */
1144 cutoffs->OldestMxact = GetOldestMultiXactId();
1146
1147 /* Acquire next XID/next MXID values used to apply age-based settings */
1148 nextXID = ReadNextTransactionId();
1149 nextMXID = ReadNextMultiXactId();
1150
1151 /*
1152 * Also compute the multixact age for which freezing is urgent. This is
1153 * normally autovacuum_multixact_freeze_max_age, but may be less if we are
1154 * short of multixact member space.
1155 */
1156 effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
1157
1158 /*
1159 * Almost ready to set freeze output parameters; check if OldestXmin or
1160 * OldestMxact are held back to an unsafe degree before we start on that
1161 */
1162 safeOldestXmin = nextXID - autovacuum_freeze_max_age;
1163 if (!TransactionIdIsNormal(safeOldestXmin))
1164 safeOldestXmin = FirstNormalTransactionId;
1165 safeOldestMxact = nextMXID - effective_multixact_freeze_max_age;
1166 if (safeOldestMxact < FirstMultiXactId)
1167 safeOldestMxact = FirstMultiXactId;
1168 if (TransactionIdPrecedes(cutoffs->OldestXmin, safeOldestXmin))
1170 (errmsg("cutoff for removing and freezing tuples is far in the past"),
1171 errhint("Close open transactions soon to avoid wraparound problems.\n"
1172 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1173 if (MultiXactIdPrecedes(cutoffs->OldestMxact, safeOldestMxact))
1175 (errmsg("cutoff for freezing multixacts is far in the past"),
1176 errhint("Close open transactions soon to avoid wraparound problems.\n"
1177 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1178
1179 /*
1180 * Determine the minimum freeze age to use: as specified by the caller, or
1181 * vacuum_freeze_min_age, but in any case not more than half
1182 * autovacuum_freeze_max_age, so that autovacuums to prevent XID
1183 * wraparound won't occur too frequently.
1184 */
1185 if (freeze_min_age < 0)
1186 freeze_min_age = vacuum_freeze_min_age;
1187 freeze_min_age = Min(freeze_min_age, autovacuum_freeze_max_age / 2);
1188 Assert(freeze_min_age >= 0);
1189
1190 /* Compute FreezeLimit, being careful to generate a normal XID */
1191 cutoffs->FreezeLimit = nextXID - freeze_min_age;
1192 if (!TransactionIdIsNormal(cutoffs->FreezeLimit))
1194 /* FreezeLimit must always be <= OldestXmin */
1195 if (TransactionIdPrecedes(cutoffs->OldestXmin, cutoffs->FreezeLimit))
1196 cutoffs->FreezeLimit = cutoffs->OldestXmin;
1197
1198 /*
1199 * Determine the minimum multixact freeze age to use: as specified by
1200 * caller, or vacuum_multixact_freeze_min_age, but in any case not more
1201 * than half effective_multixact_freeze_max_age, so that autovacuums to
1202 * prevent MultiXact wraparound won't occur too frequently.
1203 */
1204 if (multixact_freeze_min_age < 0)
1205 multixact_freeze_min_age = vacuum_multixact_freeze_min_age;
1206 multixact_freeze_min_age = Min(multixact_freeze_min_age,
1207 effective_multixact_freeze_max_age / 2);
1208 Assert(multixact_freeze_min_age >= 0);
1209
1210 /* Compute MultiXactCutoff, being careful to generate a valid value */
1211 cutoffs->MultiXactCutoff = nextMXID - multixact_freeze_min_age;
1212 if (cutoffs->MultiXactCutoff < FirstMultiXactId)
1214 /* MultiXactCutoff must always be <= OldestMxact */
1215 if (MultiXactIdPrecedes(cutoffs->OldestMxact, cutoffs->MultiXactCutoff))
1216 cutoffs->MultiXactCutoff = cutoffs->OldestMxact;
1217
1218 /*
1219 * Finally, figure out if caller needs to do an aggressive VACUUM or not.
1220 *
1221 * Determine the table freeze age to use: as specified by the caller, or
1222 * the value of the vacuum_freeze_table_age GUC, but in any case not more
1223 * than autovacuum_freeze_max_age * 0.95, so that if you have e.g nightly
1224 * VACUUM schedule, the nightly VACUUM gets a chance to freeze XIDs before
1225 * anti-wraparound autovacuum is launched.
1226 */
1227 if (freeze_table_age < 0)
1228 freeze_table_age = vacuum_freeze_table_age;
1229 freeze_table_age = Min(freeze_table_age, autovacuum_freeze_max_age * 0.95);
1230 Assert(freeze_table_age >= 0);
1231 aggressiveXIDCutoff = nextXID - freeze_table_age;
1232 if (!TransactionIdIsNormal(aggressiveXIDCutoff))
1233 aggressiveXIDCutoff = FirstNormalTransactionId;
1235 aggressiveXIDCutoff))
1236 return true;
1237
1238 /*
1239 * Similar to the above, determine the table freeze age to use for
1240 * multixacts: as specified by the caller, or the value of the
1241 * vacuum_multixact_freeze_table_age GUC, but in any case not more than
1242 * effective_multixact_freeze_max_age * 0.95, so that if you have e.g.
1243 * nightly VACUUM schedule, the nightly VACUUM gets a chance to freeze
1244 * multixacts before anti-wraparound autovacuum is launched.
1245 */
1246 if (multixact_freeze_table_age < 0)
1247 multixact_freeze_table_age = vacuum_multixact_freeze_table_age;
1248 multixact_freeze_table_age =
1249 Min(multixact_freeze_table_age,
1250 effective_multixact_freeze_max_age * 0.95);
1251 Assert(multixact_freeze_table_age >= 0);
1252 aggressiveMXIDCutoff = nextMXID - multixact_freeze_table_age;
1253 if (aggressiveMXIDCutoff < FirstMultiXactId)
1254 aggressiveMXIDCutoff = FirstMultiXactId;
1256 aggressiveMXIDCutoff))
1257 return true;
1258
1259 /* Non-aggressive VACUUM */
1260 return false;
1261}
int autovacuum_freeze_max_age
Definition: autovacuum.c:129
#define Min(x, y)
Definition: c.h:1003
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3279
int MultiXactMemberFreezeThreshold(void)
Definition: multixact.c:2924
#define FirstMultiXactId
Definition: multixact.h:26
TransactionId FreezeLimit
Definition: vacuum.h:284
TransactionId OldestXmin
Definition: vacuum.h:274
TransactionId relfrozenxid
Definition: vacuum.h:258
MultiXactId relminmxid
Definition: vacuum.h:259
MultiXactId MultiXactCutoff
Definition: vacuum.h:285
MultiXactId OldestMxact
Definition: vacuum.h:275
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:299
#define FirstNormalTransactionId
Definition: transam.h:34
int vacuum_freeze_min_age
Definition: vacuum.c:74
int vacuum_multixact_freeze_table_age
Definition: vacuum.c:77
int vacuum_freeze_table_age
Definition: vacuum.c:75
int vacuum_multixact_freeze_min_age
Definition: vacuum.c:76

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

Referenced by copy_table_data(), and heap_vacuum_rel().

◆ vacuum_is_permitted_for_relation()

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

Definition at line 722 of file vacuum.c.

724{
725 char *relname;
726
728
729 /*----------
730 * A role has privileges to vacuum or analyze the relation if any of the
731 * following are true:
732 * - the role owns the current database and the relation is not shared
733 * - the role has the MAINTAIN privilege on the relation
734 *----------
735 */
736 if ((object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) &&
737 !reltuple->relisshared) ||
739 return true;
740
741 relname = NameStr(reltuple->relname);
742
743 if ((options & VACOPT_VACUUM) != 0)
744 {
746 (errmsg("permission denied to vacuum \"%s\", skipping it",
747 relname)));
748
749 /*
750 * For VACUUM ANALYZE, both logs could show up, but just generate
751 * information for VACUUM as that would be the first one to be
752 * processed.
753 */
754 return false;
755 }
756
757 if ((options & VACOPT_ANALYZE) != 0)
759 (errmsg("permission denied to analyze \"%s\", skipping it",
760 relname)));
761
762 return false;
763}
@ ACLCHECK_OK
Definition: acl.h:183
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4088
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4037
Oid GetUserId(void)
Definition: miscinit.c:469
#define ACL_MAINTAIN
Definition: parsenodes.h:90
NameData relname
Definition: pg_class.h:38

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

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

◆ vacuum_open_relation()

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

Definition at line 774 of file vacuum.c.

776{
777 Relation rel;
778 bool rel_lock = true;
779 int elevel;
780
782
783 /*
784 * Open the relation and get the appropriate lock on it.
785 *
786 * There's a race condition here: the relation may have gone away since
787 * the last time we saw it. If so, we don't need to vacuum or analyze it.
788 *
789 * If we've been asked not to wait for the relation lock, acquire it first
790 * in non-blocking mode, before calling try_relation_open().
791 */
793 rel = try_relation_open(relid, lmode);
794 else if (ConditionalLockRelationOid(relid, lmode))
795 rel = try_relation_open(relid, NoLock);
796 else
797 {
798 rel = NULL;
799 rel_lock = false;
800 }
801
802 /* if relation is opened, leave */
803 if (rel)
804 return rel;
805
806 /*
807 * Relation could not be opened, hence generate if possible a log
808 * informing on the situation.
809 *
810 * If the RangeVar is not defined, we do not have enough information to
811 * provide a meaningful log statement. Chances are that the caller has
812 * intentionally not provided this information so that this logging is
813 * skipped, anyway.
814 */
815 if (relation == NULL)
816 return NULL;
817
818 /*
819 * Determine the log level.
820 *
821 * For manual VACUUM or ANALYZE, we emit a WARNING to match the log
822 * statements in the permission checks; otherwise, only log if the caller
823 * so requested.
824 */
826 elevel = WARNING;
827 else if (verbose)
828 elevel = LOG;
829 else
830 return NULL;
831
832 if ((options & VACOPT_VACUUM) != 0)
833 {
834 if (!rel_lock)
835 ereport(elevel,
836 (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
837 errmsg("skipping vacuum of \"%s\" --- lock not available",
838 relation->relname)));
839 else
840 ereport(elevel,
842 errmsg("skipping vacuum of \"%s\" --- relation no longer exists",
843 relation->relname)));
844
845 /*
846 * For VACUUM ANALYZE, both logs could show up, but just generate
847 * information for VACUUM as that would be the first one to be
848 * processed.
849 */
850 return NULL;
851 }
852
853 if ((options & VACOPT_ANALYZE) != 0)
854 {
855 if (!rel_lock)
856 ereport(elevel,
857 (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
858 errmsg("skipping analyze of \"%s\" --- lock not available",
859 relation->relname)));
860 else
861 ereport(elevel,
863 errmsg("skipping analyze of \"%s\" --- relation no longer exists",
864 relation->relname)));
865 }
866
867 return NULL;
868}
#define LOG
Definition: elog.h:31
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:151
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:79
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88

References AmAutoVacuumWorkerProcess, Assert(), ConditionalLockRelationOid(), ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), 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,
BufferAccessStrategy  bstrategy 
)
static

Definition at line 1999 of file vacuum.c.

2001{
2002 LOCKMODE lmode;
2003 Relation rel;
2004 LockRelId lockrelid;
2005 Oid priv_relid;
2006 Oid toast_relid;
2007 Oid save_userid;
2008 int save_sec_context;
2009 int save_nestlevel;
2010 VacuumParams toast_vacuum_params;
2011
2012 /*
2013 * This function scribbles on the parameters, so make a copy early to
2014 * avoid affecting the TOAST table (if we do end up recursing to it).
2015 */
2016 memcpy(&toast_vacuum_params, &params, sizeof(VacuumParams));
2017
2018 /* Begin a transaction for vacuuming this relation */
2020
2021 if (!(params.options & VACOPT_FULL))
2022 {
2023 /*
2024 * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
2025 * other concurrent VACUUMs know that they can ignore this one while
2026 * determining their OldestXmin. (The reason we don't set it during a
2027 * full VACUUM is exactly that we may have to run user-defined
2028 * functions for functional indexes, and we want to make sure that if
2029 * they use the snapshot set above, any tuples it requires can't get
2030 * removed from other tables. An index function that depends on the
2031 * contents of other tables is arguably broken, but we won't break it
2032 * here by violating transaction semantics.)
2033 *
2034 * We also set the VACUUM_FOR_WRAPAROUND flag, which is passed down by
2035 * autovacuum; it's used to avoid canceling a vacuum that was invoked
2036 * in an emergency.
2037 *
2038 * Note: these flags remain set until CommitTransaction or
2039 * AbortTransaction. We don't want to clear them until we reset
2040 * MyProc->xid/xmin, otherwise GetOldestNonRemovableTransactionId()
2041 * might appear to go backwards, which is probably Not Good. (We also
2042 * set PROC_IN_VACUUM *before* taking our own snapshot, so that our
2043 * xmin doesn't become visible ahead of setting the flag.)
2044 */
2045 LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
2047 if (params.is_wraparound)
2050 LWLockRelease(ProcArrayLock);
2051 }
2052
2053 /*
2054 * Need to acquire a snapshot to prevent pg_subtrans from being truncated,
2055 * cutoff xids in local memory wrapping around, and to have updated xmin
2056 * horizons.
2057 */
2059
2060 /*
2061 * Check for user-requested abort. Note we want this to be inside a
2062 * transaction, so xact.c doesn't issue useless WARNING.
2063 */
2065
2066 /*
2067 * Determine the type of lock we want --- hard exclusive lock for a FULL
2068 * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
2069 * way, we can be sure that no other backend is vacuuming the same table.
2070 */
2071 lmode = (params.options & VACOPT_FULL) ?
2073
2074 /* open the relation and get the appropriate lock on it */
2075 rel = vacuum_open_relation(relid, relation, params.options,
2076 params.log_min_duration >= 0, lmode);
2077
2078 /* leave if relation could not be opened or locked */
2079 if (!rel)
2080 {
2083 return false;
2084 }
2085
2086 /*
2087 * When recursing to a TOAST table, check privileges on the parent. NB:
2088 * This is only safe to do because we hold a session lock on the main
2089 * relation that prevents concurrent deletion.
2090 */
2091 if (OidIsValid(params.toast_parent))
2092 priv_relid = params.toast_parent;
2093 else
2094 priv_relid = RelationGetRelid(rel);
2095
2096 /*
2097 * Check if relation needs to be skipped based on privileges. This check
2098 * happens also when building the relation list to vacuum for a manual
2099 * operation, and needs to be done additionally here as VACUUM could
2100 * happen across multiple transactions where privileges could have changed
2101 * in-between. Make sure to only generate logs for VACUUM in this case.
2102 */
2103 if (!vacuum_is_permitted_for_relation(priv_relid,
2104 rel->rd_rel,
2105 params.options & ~VACOPT_ANALYZE))
2106 {
2107 relation_close(rel, lmode);
2110 return false;
2111 }
2112
2113 /*
2114 * Check that it's of a vacuumable relkind.
2115 */
2116 if (rel->rd_rel->relkind != RELKIND_RELATION &&
2117 rel->rd_rel->relkind != RELKIND_MATVIEW &&
2118 rel->rd_rel->relkind != RELKIND_TOASTVALUE &&
2119 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2120 {
2122 (errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
2124 relation_close(rel, lmode);
2127 return false;
2128 }
2129
2130 /*
2131 * Silently ignore tables that are temp tables of other backends ---
2132 * trying to vacuum these will lead to great unhappiness, since their
2133 * contents are probably not up-to-date on disk. (We don't throw a
2134 * warning here; it would just lead to chatter during a database-wide
2135 * VACUUM.)
2136 */
2137 if (RELATION_IS_OTHER_TEMP(rel))
2138 {
2139 relation_close(rel, lmode);
2142 return false;
2143 }
2144
2145 /*
2146 * Silently ignore partitioned tables as there is no work to be done. The
2147 * useful work is on their child partitions, which have been queued up for
2148 * us separately.
2149 */
2150 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2151 {
2152 relation_close(rel, lmode);
2155 /* It's OK to proceed with ANALYZE on this table */
2156 return true;
2157 }
2158
2159 /*
2160 * Get a session-level lock too. This will protect our access to the
2161 * relation across multiple transactions, so that we can vacuum the
2162 * relation's TOAST table (if any) secure in the knowledge that no one is
2163 * deleting the parent relation.
2164 *
2165 * NOTE: this cannot block, even if someone else is waiting for access,
2166 * because the lock manager knows that both lock requests are from the
2167 * same process.
2168 */
2169 lockrelid = rel->rd_lockInfo.lockRelId;
2170 LockRelationIdForSession(&lockrelid, lmode);
2171
2172 /*
2173 * Set index_cleanup option based on index_cleanup reloption if it wasn't
2174 * specified in VACUUM command, or when running in an autovacuum worker
2175 */
2177 {
2178 StdRdOptIndexCleanup vacuum_index_cleanup;
2179
2180 if (rel->rd_options == NULL)
2181 vacuum_index_cleanup = STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO;
2182 else
2183 vacuum_index_cleanup =
2184 ((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup;
2185
2186 if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO)
2188 else if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON)
2190 else
2191 {
2192 Assert(vacuum_index_cleanup ==
2195 }
2196 }
2197
2198#ifdef USE_INJECTION_POINTS
2199 if (params.index_cleanup == VACOPTVALUE_AUTO)
2200 INJECTION_POINT("vacuum-index-cleanup-auto", NULL);
2201 else if (params.index_cleanup == VACOPTVALUE_DISABLED)
2202 INJECTION_POINT("vacuum-index-cleanup-disabled", NULL);
2203 else if (params.index_cleanup == VACOPTVALUE_ENABLED)
2204 INJECTION_POINT("vacuum-index-cleanup-enabled", NULL);
2205#endif
2206
2207 /*
2208 * Check if the vacuum_max_eager_freeze_failure_rate table storage
2209 * parameter was specified. This overrides the GUC value.
2210 */
2211 if (rel->rd_options != NULL &&
2212 ((StdRdOptions *) rel->rd_options)->vacuum_max_eager_freeze_failure_rate >= 0)
2214 ((StdRdOptions *) rel->rd_options)->vacuum_max_eager_freeze_failure_rate;
2215
2216 /*
2217 * Set truncate option based on truncate reloption or GUC if it wasn't
2218 * specified in VACUUM command, or when running in an autovacuum worker
2219 */
2220 if (params.truncate == VACOPTVALUE_UNSPECIFIED)
2221 {
2223
2224 if (opts && opts->vacuum_truncate_set)
2225 {
2226 if (opts->vacuum_truncate)
2228 else
2230 }
2231 else if (vacuum_truncate)
2233 else
2235 }
2236
2237#ifdef USE_INJECTION_POINTS
2238 if (params.truncate == VACOPTVALUE_AUTO)
2239 INJECTION_POINT("vacuum-truncate-auto", NULL);
2240 else if (params.truncate == VACOPTVALUE_DISABLED)
2241 INJECTION_POINT("vacuum-truncate-disabled", NULL);
2242 else if (params.truncate == VACOPTVALUE_ENABLED)
2243 INJECTION_POINT("vacuum-truncate-enabled", NULL);
2244#endif
2245
2246 /*
2247 * Remember the relation's TOAST relation for later, if the caller asked
2248 * us to process it. In VACUUM FULL, though, the toast table is
2249 * automatically rebuilt by cluster_rel so we shouldn't recurse to it,
2250 * unless PROCESS_MAIN is disabled.
2251 */
2252 if ((params.options & VACOPT_PROCESS_TOAST) != 0 &&
2253 ((params.options & VACOPT_FULL) == 0 ||
2254 (params.options & VACOPT_PROCESS_MAIN) == 0))
2255 toast_relid = rel->rd_rel->reltoastrelid;
2256 else
2257 toast_relid = InvalidOid;
2258
2259 /*
2260 * Switch to the table owner's userid, so that any index functions are run
2261 * as that user. Also lock down security-restricted operations and
2262 * arrange to make GUC variable changes local to this command. (This is
2263 * unnecessary, but harmless, for lazy VACUUM.)
2264 */
2265 GetUserIdAndSecContext(&save_userid, &save_sec_context);
2266 SetUserIdAndSecContext(rel->rd_rel->relowner,
2267 save_sec_context | SECURITY_RESTRICTED_OPERATION);
2268 save_nestlevel = NewGUCNestLevel();
2270
2271 /*
2272 * If PROCESS_MAIN is set (the default), it's time to vacuum the main
2273 * relation. Otherwise, we can skip this part. If processing the TOAST
2274 * table is required (e.g., PROCESS_TOAST is set), we force PROCESS_MAIN
2275 * to be set when we recurse to the TOAST table.
2276 */
2277 if (params.options & VACOPT_PROCESS_MAIN)
2278 {
2279 /*
2280 * Do the actual work --- either FULL or "lazy" vacuum
2281 */
2282 if (params.options & VACOPT_FULL)
2283 {
2284 ClusterParams cluster_params = {0};
2285
2286 if ((params.options & VACOPT_VERBOSE) != 0)
2287 cluster_params.options |= CLUOPT_VERBOSE;
2288
2289 /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
2290 cluster_rel(rel, InvalidOid, &cluster_params);
2291 /* cluster_rel closes the relation, but keeps lock */
2292
2293 rel = NULL;
2294 }
2295 else
2296 table_relation_vacuum(rel, params, bstrategy);
2297 }
2298
2299 /* Roll back any GUC changes executed by index functions */
2300 AtEOXact_GUC(false, save_nestlevel);
2301
2302 /* Restore userid and security context */
2303 SetUserIdAndSecContext(save_userid, save_sec_context);
2304
2305 /* all done with this class, but hold lock until commit */
2306 if (rel)
2307 relation_close(rel, NoLock);
2308
2309 /*
2310 * Complete the transaction and free all temporary memory used.
2311 */
2314
2315 /*
2316 * If the relation has a secondary toast rel, vacuum that too while we
2317 * still hold the session lock on the main table. Note however that
2318 * "analyze" will not get done on the toast table. This is good, because
2319 * the toaster always uses hardcoded index access and statistics are
2320 * totally unimportant for toast relations.
2321 */
2322 if (toast_relid != InvalidOid)
2323 {
2324 /*
2325 * Force VACOPT_PROCESS_MAIN so vacuum_rel() processes it. Likewise,
2326 * set toast_parent so that the privilege checks are done on the main
2327 * relation. NB: This is only safe to do because we hold a session
2328 * lock on the main relation that prevents concurrent deletion.
2329 */
2330 toast_vacuum_params.options |= VACOPT_PROCESS_MAIN;
2331 toast_vacuum_params.toast_parent = relid;
2332
2333 vacuum_rel(toast_relid, NULL, toast_vacuum_params, bstrategy);
2334 }
2335
2336 /*
2337 * Now release the session-level lock on the main table.
2338 */
2339 UnlockRelationIdForSession(&lockrelid, lmode);
2340
2341 /* Report that we really did it. */
2342 return true;
2343}
void cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params)
Definition: cluster.c:311
#define CLUOPT_VERBOSE
Definition: cluster.h:23
int NewGUCNestLevel(void)
Definition: guc.c:2240
void RestrictSearchPath(void)
Definition: guc.c:2251
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2267
#define INJECTION_POINT(name, arg)
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:391
void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:404
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:318
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:612
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:619
static AmcheckOptions opts
Definition: pg_amcheck.c:112
#define PROC_IN_VACUUM
Definition: proc.h:58
#define PROC_VACUUM_FOR_WRAPAROUND
Definition: proc.h:60
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:667
StdRdOptIndexCleanup
Definition: rel.h:334
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO
Definition: rel.h:335
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF
Definition: rel.h:336
@ STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON
Definition: rel.h:337
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
PGPROC * MyProc
Definition: proc.c:66
PROC_HDR * ProcGlobal
Definition: proc.c:78
bits32 options
Definition: cluster.h:30
LockRelId lockRelId
Definition: rel.h:46
Definition: rel.h:39
uint8 statusFlags
Definition: proc.h:259
int pgxactoff
Definition: proc.h:201
uint8 * statusFlags
Definition: proc.h:403
LockInfoData rd_lockInfo
Definition: rel.h:114
bytea * rd_options
Definition: rel.h:175
static void table_relation_vacuum(Relation rel, const VacuumParams params, BufferAccessStrategy bstrategy)
Definition: tableam.h:1676
Relation vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
Definition: vacuum.c:774
bool vacuum_truncate
Definition: vacuum.c:82

References AccessExclusiveLock, Assert(), AtEOXact_GUC(), CHECK_FOR_INTERRUPTS, CLUOPT_VERBOSE, cluster_rel(), CommitTransactionCommand(), ereport, errmsg(), GetTransactionSnapshot(), GetUserIdAndSecContext(), if(), VacuumParams::index_cleanup, INJECTION_POINT, InvalidOid, VacuumParams::is_wraparound, LockRelationIdForSession(), LockInfoData::lockRelId, VacuumParams::log_min_duration, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), VacuumParams::max_eager_freeze_failure_rate, MyProc, NewGUCNestLevel(), NoLock, OidIsValid, ClusterParams::options, VacuumParams::options, opts, 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, RestrictSearchPath(), 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::toast_parent, VacuumParams::truncate, UnlockRelationIdForSession(), VACOPT_ANALYZE, VACOPT_FULL, VACOPT_PROCESS_MAIN, VACOPT_PROCESS_TOAST, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_DISABLED, VACOPTVALUE_ENABLED, VACOPTVALUE_UNSPECIFIED, vacuum_is_permitted_for_relation(), vacuum_open_relation(), vacuum_rel(), vacuum_truncate, and WARNING.

Referenced by vacuum(), and vacuum_rel().

◆ vacuum_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( const struct VacuumCutoffs cutoffs)

Definition at line 1271 of file vacuum.c.

1272{
1273 TransactionId relfrozenxid = cutoffs->relfrozenxid;
1274 MultiXactId relminmxid = cutoffs->relminmxid;
1275 TransactionId xid_skip_limit;
1276 MultiXactId multi_skip_limit;
1277 int skip_index_vacuum;
1278
1279 Assert(TransactionIdIsNormal(relfrozenxid));
1280 Assert(MultiXactIdIsValid(relminmxid));
1281
1282 /*
1283 * Determine the index skipping age to use. In any case no less than
1284 * autovacuum_freeze_max_age * 1.05.
1285 */
1286 skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
1287
1288 xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
1289 if (!TransactionIdIsNormal(xid_skip_limit))
1290 xid_skip_limit = FirstNormalTransactionId;
1291
1292 if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
1293 {
1294 /* The table's relfrozenxid is too old */
1295 return true;
1296 }
1297
1298 /*
1299 * Similar to above, determine the index skipping age to use for
1300 * multixact. In any case no less than autovacuum_multixact_freeze_max_age *
1301 * 1.05.
1302 */
1303 skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
1305
1306 multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
1307 if (multi_skip_limit < FirstMultiXactId)
1308 multi_skip_limit = FirstMultiXactId;
1309
1310 if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
1311 {
1312 /* The table's relminmxid is too old */
1313 return true;
1314 }
1315
1316 return false;
1317}
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:130
#define Max(x, y)
Definition: c.h:997
int vacuum_multixact_failsafe_age
Definition: vacuum.c:79
int vacuum_failsafe_age
Definition: vacuum.c:78

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

Referenced by lazy_check_wraparound_failsafe().

Variable Documentation

◆ parallel_vacuum_worker_delay_ns

int64 parallel_vacuum_worker_delay_ns = 0

Definition at line 94 of file vacuum.c.

Referenced by parallel_vacuum_main(), and vacuum_delay_point().

◆ track_cost_delay_timing

bool track_cost_delay_timing

Definition at line 81 of file vacuum.c.

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

◆ vacuum_cost_delay

double vacuum_cost_delay = 0

◆ vacuum_cost_limit

int vacuum_cost_limit = 200

◆ vacuum_failsafe_age

int vacuum_failsafe_age

Definition at line 78 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

int vacuum_freeze_min_age

Definition at line 74 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_freeze_table_age

int vacuum_freeze_table_age

Definition at line 75 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_max_eager_freeze_failure_rate

double vacuum_max_eager_freeze_failure_rate

Definition at line 80 of file vacuum.c.

Referenced by default_reloptions(), ExecVacuum(), and table_recheck_autovac().

◆ vacuum_multixact_failsafe_age

int vacuum_multixact_failsafe_age

Definition at line 79 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_multixact_freeze_min_age

int vacuum_multixact_freeze_min_age

Definition at line 76 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_freeze_table_age

int vacuum_multixact_freeze_table_age

Definition at line 77 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_truncate

bool vacuum_truncate

Definition at line 82 of file vacuum.c.

Referenced by default_reloptions(), and vacuum_rel().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

int VacuumCostBalanceLocal = 0

◆ VacuumFailsafeActive

◆ VacuumSharedCostBalance

pg_atomic_uint32* VacuumSharedCostBalance = NULL