PostgreSQL Source Code  git master
vacuum.h File Reference
#include "access/htup.h"
#include "access/genam.h"
#include "access/parallel.h"
#include "catalog/pg_class.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "parser/parse_node.h"
#include "storage/buf.h"
#include "storage/lock.h"
#include "utils/relcache.h"
Include dependency graph for vacuum.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  VacAttrStats
 
struct  VacuumParams
 
struct  VacuumCutoffs
 
struct  VacDeadItems
 

Macros

#define VACUUM_OPTION_NO_PARALLEL   0
 
#define VACUUM_OPTION_PARALLEL_BULKDEL   (1 << 0)
 
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP   (1 << 1)
 
#define VACUUM_OPTION_PARALLEL_CLEANUP   (1 << 2)
 
#define VACUUM_OPTION_MAX_VALID_VALUE   ((1 << 3) - 1)
 
#define VACOPT_VACUUM   0x01 /* do VACUUM */
 
#define VACOPT_ANALYZE   0x02 /* do ANALYZE */
 
#define VACOPT_VERBOSE   0x04 /* output INFO instrumentation messages */
 
#define VACOPT_FREEZE   0x08 /* FREEZE option */
 
#define VACOPT_FULL   0x10 /* FULL (non-concurrent) vacuum */
 
#define VACOPT_SKIP_LOCKED   0x20 /* skip if cannot get lock */
 
#define VACOPT_PROCESS_MAIN   0x40 /* process main relation */
 
#define VACOPT_PROCESS_TOAST   0x80 /* process the TOAST table, if any */
 
#define VACOPT_DISABLE_PAGE_SKIPPING   0x100 /* don't skip any pages */
 
#define VACOPT_SKIP_DATABASE_STATS   0x200 /* skip vac_update_datfrozenxid() */
 
#define VACOPT_ONLY_DATABASE_STATS   0x400 /* only vac_update_datfrozenxid() */
 
#define MAXDEADITEMS(avail_mem)    (((avail_mem) - offsetof(VacDeadItems, items)) / sizeof(ItemPointerData))
 

Typedefs

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

Enumerations

enum  VacOptValue { VACOPTVALUE_UNSPECIFIED = 0 , VACOPTVALUE_AUTO , VACOPTVALUE_DISABLED , VACOPTVALUE_ENABLED }
 

Functions

void ExecVacuum (ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 
void vacuum (List *relations, VacuumParams *params, BufferAccessStrategy bstrategy, bool isTopLevel)
 
void vac_open_indexes (Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
 
void vac_close_indexes (int nindexes, Relation *Irel, LOCKMODE lockmode)
 
double vac_estimate_reltuples (Relation relation, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples)
 
void vac_update_relstats (Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti, bool *frozenxid_updated, bool *minmulti_updated, bool in_outer_xact)
 
bool vacuum_get_cutoffs (Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
 
bool vacuum_xid_failsafe_check (const struct VacuumCutoffs *cutoffs)
 
void vac_update_datfrozenxid (void)
 
void vacuum_delay_point (void)
 
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)
 
IndexBulkDeleteResultvac_bulkdel_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat, VacDeadItems *dead_items)
 
IndexBulkDeleteResultvac_cleanup_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat)
 
Size vac_max_items_to_alloc_size (int max_items)
 
ParallelVacuumStateparallel_vacuum_init (Relation rel, Relation *indrels, int nindexes, int nrequested_workers, int max_items, int elevel, BufferAccessStrategy bstrategy)
 
void parallel_vacuum_end (ParallelVacuumState *pvs, IndexBulkDeleteResult **istats)
 
VacDeadItemsparallel_vacuum_get_dead_items (ParallelVacuumState *pvs)
 
void parallel_vacuum_bulkdel_all_indexes (ParallelVacuumState *pvs, long num_table_tuples, int num_index_scans)
 
void parallel_vacuum_cleanup_all_indexes (ParallelVacuumState *pvs, long num_table_tuples, int num_index_scans, bool estimated_count)
 
void parallel_vacuum_main (dsm_segment *seg, shm_toc *toc)
 
void analyze_rel (Oid relid, RangeVar *relation, VacuumParams *params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
 
bool std_typanalyze (VacAttrStats *stats)
 
double anl_random_fract (void)
 
double anl_init_selection_state (int n)
 
double anl_get_next_S (double t, int n, double *stateptr)
 

Variables

PGDLLIMPORT int default_statistics_target
 
PGDLLIMPORT int vacuum_freeze_min_age
 
PGDLLIMPORT int vacuum_freeze_table_age
 
PGDLLIMPORT int vacuum_multixact_freeze_min_age
 
PGDLLIMPORT int vacuum_multixact_freeze_table_age
 
PGDLLIMPORT int vacuum_failsafe_age
 
PGDLLIMPORT int vacuum_multixact_failsafe_age
 
PGDLLIMPORT pg_atomic_uint32VacuumSharedCostBalance
 
PGDLLIMPORT pg_atomic_uint32VacuumActiveNWorkers
 
PGDLLIMPORT int VacuumCostBalanceLocal
 

Macro Definition Documentation

◆ MAXDEADITEMS

#define MAXDEADITEMS (   avail_mem)     (((avail_mem) - offsetof(VacDeadItems, items)) / sizeof(ItemPointerData))

Definition at line 292 of file vacuum.h.

◆ VACOPT_ANALYZE

#define VACOPT_ANALYZE   0x02 /* do ANALYZE */

Definition at line 184 of file vacuum.h.

◆ VACOPT_DISABLE_PAGE_SKIPPING

#define VACOPT_DISABLE_PAGE_SKIPPING   0x100 /* don't skip any pages */

Definition at line 191 of file vacuum.h.

◆ VACOPT_FREEZE

#define VACOPT_FREEZE   0x08 /* FREEZE option */

Definition at line 186 of file vacuum.h.

◆ VACOPT_FULL

#define VACOPT_FULL   0x10 /* FULL (non-concurrent) vacuum */

Definition at line 187 of file vacuum.h.

◆ VACOPT_ONLY_DATABASE_STATS

#define VACOPT_ONLY_DATABASE_STATS   0x400 /* only vac_update_datfrozenxid() */

Definition at line 193 of file vacuum.h.

◆ VACOPT_PROCESS_MAIN

#define VACOPT_PROCESS_MAIN   0x40 /* process main relation */

Definition at line 189 of file vacuum.h.

◆ VACOPT_PROCESS_TOAST

#define VACOPT_PROCESS_TOAST   0x80 /* process the TOAST table, if any */

Definition at line 190 of file vacuum.h.

◆ VACOPT_SKIP_DATABASE_STATS

#define VACOPT_SKIP_DATABASE_STATS   0x200 /* skip vac_update_datfrozenxid() */

Definition at line 192 of file vacuum.h.

◆ VACOPT_SKIP_LOCKED

#define VACOPT_SKIP_LOCKED   0x20 /* skip if cannot get lock */

Definition at line 188 of file vacuum.h.

◆ VACOPT_VACUUM

#define VACOPT_VACUUM   0x01 /* do VACUUM */

Definition at line 183 of file vacuum.h.

◆ VACOPT_VERBOSE

#define VACOPT_VERBOSE   0x04 /* output INFO instrumentation messages */

Definition at line 185 of file vacuum.h.

◆ VACUUM_OPTION_MAX_VALID_VALUE

#define VACUUM_OPTION_MAX_VALID_VALUE   ((1 << 3) - 1)

Definition at line 65 of file vacuum.h.

◆ VACUUM_OPTION_NO_PARALLEL

#define VACUUM_OPTION_NO_PARALLEL   0

Definition at line 41 of file vacuum.h.

◆ VACUUM_OPTION_PARALLEL_BULKDEL

#define VACUUM_OPTION_PARALLEL_BULKDEL   (1 << 0)

Definition at line 47 of file vacuum.h.

◆ VACUUM_OPTION_PARALLEL_CLEANUP

#define VACUUM_OPTION_PARALLEL_CLEANUP   (1 << 2)

Definition at line 62 of file vacuum.h.

◆ VACUUM_OPTION_PARALLEL_COND_CLEANUP

#define VACUUM_OPTION_PARALLEL_COND_CLEANUP   (1 << 1)

Definition at line 54 of file vacuum.h.

Typedef Documentation

◆ AnalyzeAttrComputeStatsFunc

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

Definition at line 110 of file vacuum.h.

◆ AnalyzeAttrFetchFunc

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

Definition at line 107 of file vacuum.h.

◆ ParallelVacuumState

Definition at line 1 of file vacuum.h.

◆ VacAttrStats

typedef struct VacAttrStats VacAttrStats

◆ VacAttrStatsP

typedef struct VacAttrStats* VacAttrStatsP

Definition at line 105 of file vacuum.h.

◆ VacDeadItems

typedef struct VacDeadItems VacDeadItems

◆ VacOptValue

typedef enum VacOptValue VacOptValue

◆ VacuumParams

typedef struct VacuumParams VacuumParams

Enumeration Type Documentation

◆ VacOptValue

Enumerator
VACOPTVALUE_UNSPECIFIED 
VACOPTVALUE_AUTO 
VACOPTVALUE_DISABLED 
VACOPTVALUE_ENABLED 

Definition at line 203 of file vacuum.h.

204 {
209 } VacOptValue;
VacOptValue
Definition: vacuum.h:204
@ VACOPTVALUE_AUTO
Definition: vacuum.h:206
@ VACOPTVALUE_ENABLED
Definition: vacuum.h:208
@ VACOPTVALUE_UNSPECIFIED
Definition: vacuum.h:205
@ VACOPTVALUE_DISABLED
Definition: vacuum.h:207

Function Documentation

◆ analyze_rel()

void analyze_rel ( Oid  relid,
RangeVar relation,
VacuumParams params,
List va_cols,
bool  in_outer_xact,
BufferAccessStrategy  bstrategy 
)

Definition at line 121 of file analyze.c.

124 {
125  Relation onerel;
126  int elevel;
127  AcquireSampleRowsFunc acquirefunc = NULL;
128  BlockNumber relpages = 0;
129 
130  /* Select logging level */
131  if (params->options & VACOPT_VERBOSE)
132  elevel = INFO;
133  else
134  elevel = DEBUG2;
135 
136  /* Set up static variables */
137  vac_strategy = bstrategy;
138 
139  /*
140  * Check for user-requested abort.
141  */
143 
144  /*
145  * Open the relation, getting ShareUpdateExclusiveLock to ensure that two
146  * ANALYZEs don't run on it concurrently. (This also locks out a
147  * concurrent VACUUM, which doesn't matter much at the moment but might
148  * matter if we ever try to accumulate stats on dead tuples.) If the rel
149  * has been dropped since we last saw it, we don't need to process it.
150  *
151  * Make sure to generate only logs for ANALYZE in this case.
152  */
153  onerel = vacuum_open_relation(relid, relation, params->options & ~(VACOPT_VACUUM),
154  params->log_min_duration >= 0,
156 
157  /* leave if relation could not be opened or locked */
158  if (!onerel)
159  return;
160 
161  /*
162  * Check if relation needs to be skipped based on privileges. This check
163  * happens also when building the relation list to analyze for a manual
164  * operation, and needs to be done additionally here as ANALYZE could
165  * happen across multiple transactions where privileges could have changed
166  * in-between. Make sure to generate only logs for ANALYZE in this case.
167  */
169  onerel->rd_rel,
170  params->options & VACOPT_ANALYZE))
171  {
173  return;
174  }
175 
176  /*
177  * Silently ignore tables that are temp tables of other backends ---
178  * trying to analyze these is rather pointless, since their contents are
179  * probably not up-to-date on disk. (We don't throw a warning here; it
180  * would just lead to chatter during a database-wide ANALYZE.)
181  */
182  if (RELATION_IS_OTHER_TEMP(onerel))
183  {
185  return;
186  }
187 
188  /*
189  * We can ANALYZE any table except pg_statistic. See update_attstats
190  */
191  if (RelationGetRelid(onerel) == StatisticRelationId)
192  {
194  return;
195  }
196 
197  /*
198  * Check that it's of an analyzable relkind, and set up appropriately.
199  */
200  if (onerel->rd_rel->relkind == RELKIND_RELATION ||
201  onerel->rd_rel->relkind == RELKIND_MATVIEW)
202  {
203  /* Regular table, so we'll use the regular row acquisition function */
204  acquirefunc = acquire_sample_rows;
205  /* Also get regular table's size */
206  relpages = RelationGetNumberOfBlocks(onerel);
207  }
208  else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
209  {
210  /*
211  * For a foreign table, call the FDW's hook function to see whether it
212  * supports analysis.
213  */
214  FdwRoutine *fdwroutine;
215  bool ok = false;
216 
217  fdwroutine = GetFdwRoutineForRelation(onerel, false);
218 
219  if (fdwroutine->AnalyzeForeignTable != NULL)
220  ok = fdwroutine->AnalyzeForeignTable(onerel,
221  &acquirefunc,
222  &relpages);
223 
224  if (!ok)
225  {
227  (errmsg("skipping \"%s\" --- cannot analyze this foreign table",
228  RelationGetRelationName(onerel))));
230  return;
231  }
232  }
233  else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
234  {
235  /*
236  * For partitioned tables, we want to do the recursive ANALYZE below.
237  */
238  }
239  else
240  {
241  /* No need for a WARNING if we already complained during VACUUM */
242  if (!(params->options & VACOPT_VACUUM))
244  (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
245  RelationGetRelationName(onerel))));
247  return;
248  }
249 
250  /*
251  * OK, let's do it. First, initialize progress reporting.
252  */
254  RelationGetRelid(onerel));
255 
256  /*
257  * Do the normal non-recursive ANALYZE. We can skip this for partitioned
258  * tables, which don't contain any rows.
259  */
260  if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
261  do_analyze_rel(onerel, params, va_cols, acquirefunc,
262  relpages, false, in_outer_xact, elevel);
263 
264  /*
265  * If there are child tables, do recursive ANALYZE.
266  */
267  if (onerel->rd_rel->relhassubclass)
268  do_analyze_rel(onerel, params, va_cols, acquirefunc, relpages,
269  true, in_outer_xact, elevel);
270 
271  /*
272  * Close source relation now, but keep lock so that no one deletes it
273  * before we commit. (If someone did, they'd fail to clean up the entries
274  * we made in pg_statistic. Also, releasing the lock before commit would
275  * expose us to concurrent-update failures in update_attstats.)
276  */
277  relation_close(onerel, NoLock);
278 
280 }
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_ANALYZE
uint32 BlockNumber
Definition: block.h:31
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:161
static BufferAccessStrategy vac_strategy
Definition: analyze.c:87
static int acquire_sample_rows(Relation onerel, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
Definition: analyze.c:1130
static void do_analyze_rel(Relation onerel, VacuumParams *params, List *va_cols, AcquireSampleRowsFunc acquirefunc, BlockNumber relpages, bool inh, bool in_outer_xact, int elevel)
Definition: analyze.c:290
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define WARNING
Definition: elog.h:36
#define DEBUG2
Definition: elog.h:29
#define INFO
Definition: elog.h:34
#define ereport(elevel,...)
Definition: elog.h:149
int(* AcquireSampleRowsFunc)(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
Definition: fdwapi.h:151
FdwRoutine * GetFdwRoutineForRelation(Relation relation, bool makecopy)
Definition: foreign.c:429
#define NoLock
Definition: lockdefs.h:34
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
#define RelationGetRelid(relation)
Definition: rel.h:501
#define RelationGetRelationName(relation)
Definition: rel.h:535
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:656
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
AnalyzeForeignTable_function AnalyzeForeignTable
Definition: fdwapi.h:257
Form_pg_class rd_rel
Definition: rel.h:110
bits32 options
Definition: vacuum.h:219
int log_min_duration
Definition: vacuum.h:227
Relation vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
Definition: vacuum.c:651
bool vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple, bits32 options)
Definition: vacuum.c:595
#define VACOPT_VACUUM
Definition: vacuum.h:183
#define VACOPT_VERBOSE
Definition: vacuum.h:185
#define VACOPT_ANALYZE
Definition: vacuum.h:184

References acquire_sample_rows(), FdwRoutine::AnalyzeForeignTable, CHECK_FOR_INTERRUPTS, DEBUG2, do_analyze_rel(), ereport, errmsg(), GetFdwRoutineForRelation(), INFO, VacuumParams::log_min_duration, NoLock, VacuumParams::options, pgstat_progress_end_command(), pgstat_progress_start_command(), PROGRESS_COMMAND_ANALYZE, RelationData::rd_rel, relation_close(), RELATION_IS_OTHER_TEMP, RelationGetNumberOfBlocks, RelationGetRelationName, RelationGetRelid, ShareUpdateExclusiveLock, vac_strategy, VACOPT_ANALYZE, VACOPT_VACUUM, VACOPT_VERBOSE, vacuum_is_permitted_for_relation(), vacuum_open_relation(), and WARNING.

Referenced by vacuum().

◆ anl_get_next_S()

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

Definition at line 296 of file sampling.c.

297 {
298  double result;
299 
300  oldrs.W = *stateptr;
301  result = reservoir_get_next_S(&oldrs, t, n);
302  *stateptr = oldrs.W;
303  return result;
304 }
static ReservoirStateData oldrs
Definition: sampling.c:262
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:147

◆ anl_init_selection_state()

double anl_init_selection_state ( int  n)

Definition at line 281 of file sampling.c.

282 {
283  /* initialize if first time through */
285  {
287  &oldrs.randstate);
288  oldrs_initialized = true;
289  }
290 
291  /* Initial value of W (for use when Algorithm Z is first applied) */
292  return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
293 }
#define unlikely(x)
Definition: c.h:295
uint32 pg_prng_uint32(pg_prng_state *state)
Definition: pg_prng.c:191
pg_prng_state pg_global_prng_state
Definition: pg_prng.c:34
static bool oldrs_initialized
Definition: sampling.c:263
double sampler_random_fract(pg_prng_state *randstate)
Definition: sampling.c:241
void sampler_random_init_state(uint32 seed, pg_prng_state *randstate)
Definition: sampling.c:234
pg_prng_state randstate
Definition: sampling.h:49

◆ anl_random_fract()

double anl_random_fract ( void  )

Definition at line 266 of file sampling.c.

267 {
268  /* initialize if first time through */
270  {
272  &oldrs.randstate);
273  oldrs_initialized = true;
274  }
275 
276  /* and compute a random fraction */
278 }

◆ ExecVacuum()

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

Definition at line 110 of file vacuum.c.

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

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

Referenced by standard_ProcessUtility().

◆ parallel_vacuum_bulkdel_all_indexes()

void parallel_vacuum_bulkdel_all_indexes ( ParallelVacuumState pvs,
long  num_table_tuples,
int  num_index_scans 
)

Definition at line 455 of file vacuumparallel.c.

457 {
459 
460  /*
461  * We can only provide an approximate value of num_heap_tuples, at least
462  * for now.
463  */
464  pvs->shared->reltuples = num_table_tuples;
465  pvs->shared->estimated_count = true;
466 
467  parallel_vacuum_process_all_indexes(pvs, num_index_scans, true);
468 }
#define IsParallelWorker()
Definition: parallel.h:61
double reltuples
bool estimated_count
static void parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scans, bool vacuum)

References Assert(), PVShared::estimated_count, IsParallelWorker, parallel_vacuum_process_all_indexes(), PVShared::reltuples, and ParallelVacuumState::shared.

Referenced by lazy_vacuum_all_indexes().

◆ parallel_vacuum_cleanup_all_indexes()

void parallel_vacuum_cleanup_all_indexes ( ParallelVacuumState pvs,
long  num_table_tuples,
int  num_index_scans,
bool  estimated_count 
)

Definition at line 474 of file vacuumparallel.c.

476 {
478 
479  /*
480  * We can provide a better estimate of total number of surviving tuples
481  * (we assume indexes are more interested in that than in the number of
482  * nominally live tuples).
483  */
484  pvs->shared->reltuples = num_table_tuples;
485  pvs->shared->estimated_count = estimated_count;
486 
487  parallel_vacuum_process_all_indexes(pvs, num_index_scans, false);
488 }

References Assert(), PVShared::estimated_count, IsParallelWorker, parallel_vacuum_process_all_indexes(), PVShared::reltuples, and ParallelVacuumState::shared.

Referenced by lazy_cleanup_all_indexes().

◆ parallel_vacuum_end()

void parallel_vacuum_end ( ParallelVacuumState pvs,
IndexBulkDeleteResult **  istats 
)

Definition at line 419 of file vacuumparallel.c.

420 {
422 
423  /* Copy the updated statistics */
424  for (int i = 0; i < pvs->nindexes; i++)
425  {
426  PVIndStats *indstats = &(pvs->indstats[i]);
427 
428  if (indstats->istat_updated)
429  {
430  istats[i] = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
431  memcpy(istats[i], &indstats->istat, sizeof(IndexBulkDeleteResult));
432  }
433  else
434  istats[i] = NULL;
435  }
436 
439 
441  pfree(pvs);
442 }
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:927
int i
Definition: isn.c:73
void pfree(void *pointer)
Definition: mcxt.c:1436
void * palloc0(Size size)
Definition: mcxt.c:1241
bool istat_updated
IndexBulkDeleteResult istat
ParallelContext * pcxt
PVIndStats * indstats
void ExitParallelMode(void)
Definition: xact.c:1049

References Assert(), DestroyParallelContext(), ExitParallelMode(), i, ParallelVacuumState::indstats, IsParallelWorker, PVIndStats::istat, PVIndStats::istat_updated, ParallelVacuumState::nindexes, palloc0(), ParallelVacuumState::pcxt, pfree(), and ParallelVacuumState::will_parallel_vacuum.

Referenced by dead_items_cleanup().

◆ parallel_vacuum_get_dead_items()

VacDeadItems* parallel_vacuum_get_dead_items ( ParallelVacuumState pvs)

Definition at line 446 of file vacuumparallel.c.

447 {
448  return pvs->dead_items;
449 }
VacDeadItems * dead_items

References ParallelVacuumState::dead_items.

Referenced by dead_items_alloc().

◆ parallel_vacuum_init()

ParallelVacuumState* parallel_vacuum_init ( Relation  rel,
Relation indrels,
int  nindexes,
int  nrequested_workers,
int  max_items,
int  elevel,
BufferAccessStrategy  bstrategy 
)

Definition at line 224 of file vacuumparallel.c.

227 {
228  ParallelVacuumState *pvs;
229  ParallelContext *pcxt;
230  PVShared *shared;
231  VacDeadItems *dead_items;
232  PVIndStats *indstats;
233  BufferUsage *buffer_usage;
234  WalUsage *wal_usage;
235  bool *will_parallel_vacuum;
236  Size est_indstats_len;
237  Size est_shared_len;
238  Size est_dead_items_len;
239  int nindexes_mwm = 0;
240  int parallel_workers = 0;
241  int querylen;
242 
243  /*
244  * A parallel vacuum must be requested and there must be indexes on the
245  * relation
246  */
247  Assert(nrequested_workers >= 0);
248  Assert(nindexes > 0);
249 
250  /*
251  * Compute the number of parallel vacuum workers to launch
252  */
253  will_parallel_vacuum = (bool *) palloc0(sizeof(bool) * nindexes);
254  parallel_workers = parallel_vacuum_compute_workers(indrels, nindexes,
255  nrequested_workers,
256  will_parallel_vacuum);
257  if (parallel_workers <= 0)
258  {
259  /* Can't perform vacuum in parallel -- return NULL */
260  pfree(will_parallel_vacuum);
261  return NULL;
262  }
263 
265  pvs->indrels = indrels;
266  pvs->nindexes = nindexes;
267  pvs->will_parallel_vacuum = will_parallel_vacuum;
268  pvs->bstrategy = bstrategy;
269 
271  pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
272  parallel_workers);
273  Assert(pcxt->nworkers > 0);
274  pvs->pcxt = pcxt;
275 
276  /* Estimate size for index vacuum stats -- PARALLEL_VACUUM_KEY_INDEX_STATS */
277  est_indstats_len = mul_size(sizeof(PVIndStats), nindexes);
278  shm_toc_estimate_chunk(&pcxt->estimator, est_indstats_len);
279  shm_toc_estimate_keys(&pcxt->estimator, 1);
280 
281  /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
282  est_shared_len = sizeof(PVShared);
283  shm_toc_estimate_chunk(&pcxt->estimator, est_shared_len);
284  shm_toc_estimate_keys(&pcxt->estimator, 1);
285 
286  /* Estimate size for dead_items -- PARALLEL_VACUUM_KEY_DEAD_ITEMS */
287  est_dead_items_len = vac_max_items_to_alloc_size(max_items);
288  shm_toc_estimate_chunk(&pcxt->estimator, est_dead_items_len);
289  shm_toc_estimate_keys(&pcxt->estimator, 1);
290 
291  /*
292  * Estimate space for BufferUsage and WalUsage --
293  * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
294  *
295  * If there are no extensions loaded that care, we could skip this. We
296  * have no way of knowing whether anyone's looking at pgBufferUsage or
297  * pgWalUsage, so do it unconditionally.
298  */
300  mul_size(sizeof(BufferUsage), pcxt->nworkers));
301  shm_toc_estimate_keys(&pcxt->estimator, 1);
303  mul_size(sizeof(WalUsage), pcxt->nworkers));
304  shm_toc_estimate_keys(&pcxt->estimator, 1);
305 
306  /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
307  if (debug_query_string)
308  {
309  querylen = strlen(debug_query_string);
310  shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
311  shm_toc_estimate_keys(&pcxt->estimator, 1);
312  }
313  else
314  querylen = 0; /* keep compiler quiet */
315 
316  InitializeParallelDSM(pcxt);
317 
318  /* Prepare index vacuum stats */
319  indstats = (PVIndStats *) shm_toc_allocate(pcxt->toc, est_indstats_len);
320  MemSet(indstats, 0, est_indstats_len);
321  for (int i = 0; i < nindexes; i++)
322  {
323  Relation indrel = indrels[i];
324  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
325 
326  /*
327  * Cleanup option should be either disabled, always performing in
328  * parallel or conditionally performing in parallel.
329  */
330  Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
331  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
332  Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
333 
334  if (!will_parallel_vacuum[i])
335  continue;
336 
337  if (indrel->rd_indam->amusemaintenanceworkmem)
338  nindexes_mwm++;
339 
340  /*
341  * Remember the number of indexes that support parallel operation for
342  * each phase.
343  */
344  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
346  if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
348  if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
350  }
352  pvs->indstats = indstats;
353 
354  /* Prepare shared information */
355  shared = (PVShared *) shm_toc_allocate(pcxt->toc, est_shared_len);
356  MemSet(shared, 0, est_shared_len);
357  shared->relid = RelationGetRelid(rel);
358  shared->elevel = elevel;
360  (nindexes_mwm > 0) ?
361  maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
363 
364  pg_atomic_init_u32(&(shared->cost_balance), 0);
365  pg_atomic_init_u32(&(shared->active_nworkers), 0);
366  pg_atomic_init_u32(&(shared->idx), 0);
367 
369  pvs->shared = shared;
370 
371  /* Prepare the dead_items space */
372  dead_items = (VacDeadItems *) shm_toc_allocate(pcxt->toc,
373  est_dead_items_len);
374  dead_items->max_items = max_items;
375  dead_items->num_items = 0;
376  MemSet(dead_items->items, 0, sizeof(ItemPointerData) * max_items);
378  pvs->dead_items = dead_items;
379 
380  /*
381  * Allocate space for each worker's BufferUsage and WalUsage; no need to
382  * initialize
383  */
384  buffer_usage = shm_toc_allocate(pcxt->toc,
385  mul_size(sizeof(BufferUsage), pcxt->nworkers));
387  pvs->buffer_usage = buffer_usage;
388  wal_usage = shm_toc_allocate(pcxt->toc,
389  mul_size(sizeof(WalUsage), pcxt->nworkers));
391  pvs->wal_usage = wal_usage;
392 
393  /* Store query string for workers */
394  if (debug_query_string)
395  {
396  char *sharedquery;
397 
398  sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
399  memcpy(sharedquery, debug_query_string, querylen + 1);
400  sharedquery[querylen] = '\0';
401  shm_toc_insert(pcxt->toc,
402  PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
403  }
404 
405  /* Success -- return parallel vacuum state */
406  return pvs;
407 }
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:218
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:203
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:165
#define Min(x, y)
Definition: c.h:988
unsigned char uint8
Definition: c.h:488
#define MemSet(start, val, len)
Definition: c.h:1004
size_t Size
Definition: c.h:589
int maintenance_work_mem
Definition: globals.c:127
const char * debug_query_string
Definition: postgres.c:81
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition: shm_toc.c:171
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition: shm_toc.c:88
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
#define shm_toc_estimate_keys(e, cnt)
Definition: shm_toc.h:53
Size mul_size(Size s1, Size s2)
Definition: shmem.c:519
bool amusemaintenanceworkmem
Definition: amapi.h:246
uint8 amparallelvacuumoptions
Definition: amapi.h:248
pg_atomic_uint32 cost_balance
int maintenance_work_mem_worker
pg_atomic_uint32 active_nworkers
pg_atomic_uint32 idx
shm_toc_estimator estimator
Definition: parallel.h:42
shm_toc * toc
Definition: parallel.h:45
BufferAccessStrategy bstrategy
BufferUsage * buffer_usage
struct IndexAmRoutine * rd_indam
Definition: rel.h:202
ItemPointerData items[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuum.h:289
int max_items
Definition: vacuum.h:285
int num_items
Definition: vacuum.h:286
Size vac_max_items_to_alloc_size(int max_items)
Definition: vacuum.c:2383
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:62
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:47
#define VACUUM_OPTION_MAX_VALID_VALUE
Definition: vacuum.h:65
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:54
static int parallel_vacuum_compute_workers(Relation *indrels, int nindexes, int nrequested, bool *will_parallel_vacuum)
#define PARALLEL_VACUUM_KEY_INDEX_STATS
#define PARALLEL_VACUUM_KEY_QUERY_TEXT
#define PARALLEL_VACUUM_KEY_BUFFER_USAGE
#define PARALLEL_VACUUM_KEY_SHARED
#define PARALLEL_VACUUM_KEY_WAL_USAGE
struct PVShared PVShared
#define PARALLEL_VACUUM_KEY_DEAD_ITEMS
void EnterParallelMode(void)
Definition: xact.c:1036

References PVShared::active_nworkers, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::amusemaintenanceworkmem, Assert(), ParallelVacuumState::bstrategy, ParallelVacuumState::buffer_usage, PVShared::cost_balance, CreateParallelContext(), ParallelVacuumState::dead_items, debug_query_string, PVShared::elevel, EnterParallelMode(), ParallelContext::estimator, i, PVShared::idx, ParallelVacuumState::indrels, ParallelVacuumState::indstats, InitializeParallelDSM(), VacDeadItems::items, maintenance_work_mem, PVShared::maintenance_work_mem_worker, VacDeadItems::max_items, MemSet, Min, mul_size(), ParallelVacuumState::nindexes, ParallelVacuumState::nindexes_parallel_bulkdel, ParallelVacuumState::nindexes_parallel_cleanup, ParallelVacuumState::nindexes_parallel_condcleanup, VacDeadItems::num_items, ParallelContext::nworkers, palloc0(), parallel_vacuum_compute_workers(), PARALLEL_VACUUM_KEY_BUFFER_USAGE, PARALLEL_VACUUM_KEY_DEAD_ITEMS, PARALLEL_VACUUM_KEY_INDEX_STATS, PARALLEL_VACUUM_KEY_QUERY_TEXT, PARALLEL_VACUUM_KEY_SHARED, PARALLEL_VACUUM_KEY_WAL_USAGE, ParallelVacuumState::pcxt, pfree(), pg_atomic_init_u32(), RelationData::rd_indam, RelationGetRelid, PVShared::relid, ParallelVacuumState::shared, shm_toc_allocate(), shm_toc_estimate_chunk, shm_toc_estimate_keys, shm_toc_insert(), ParallelContext::toc, vac_max_items_to_alloc_size(), VACUUM_OPTION_MAX_VALID_VALUE, VACUUM_OPTION_PARALLEL_BULKDEL, VACUUM_OPTION_PARALLEL_CLEANUP, VACUUM_OPTION_PARALLEL_COND_CLEANUP, ParallelVacuumState::wal_usage, and ParallelVacuumState::will_parallel_vacuum.

Referenced by dead_items_alloc().

◆ parallel_vacuum_main()

void parallel_vacuum_main ( dsm_segment seg,
shm_toc toc 
)

Definition at line 936 of file vacuumparallel.c.

937 {
939  Relation rel;
940  Relation *indrels;
941  PVIndStats *indstats;
942  PVShared *shared;
943  VacDeadItems *dead_items;
944  BufferUsage *buffer_usage;
945  WalUsage *wal_usage;
946  int nindexes;
947  char *sharedquery;
948  ErrorContextCallback errcallback;
949 
950  /*
951  * A parallel vacuum worker must have only PROC_IN_VACUUM flag since we
952  * don't support parallel vacuum for autovacuum as of now.
953  */
955 
956  elog(DEBUG1, "starting parallel vacuum worker");
957 
958  shared = (PVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false);
959 
960  /* Set debug_query_string for individual workers */
961  sharedquery = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_QUERY_TEXT, true);
962  debug_query_string = sharedquery;
964 
965  /*
966  * Open table. The lock mode is the same as the leader process. It's
967  * okay because the lock mode does not conflict among the parallel
968  * workers.
969  */
970  rel = table_open(shared->relid, ShareUpdateExclusiveLock);
971 
972  /*
973  * Open all indexes. indrels are sorted in order by OID, which should be
974  * matched to the leader's one.
975  */
976  vac_open_indexes(rel, RowExclusiveLock, &nindexes, &indrels);
977  Assert(nindexes > 0);
978 
979  if (shared->maintenance_work_mem_worker > 0)
981 
982  /* Set index statistics */
983  indstats = (PVIndStats *) shm_toc_lookup(toc,
985  false);
986 
987  /* Set dead_items space */
988  dead_items = (VacDeadItems *) shm_toc_lookup(toc,
990  false);
991 
992  /* Set cost-based vacuum delay */
994  VacuumCostBalance = 0;
995  VacuumPageHit = 0;
996  VacuumPageMiss = 0;
997  VacuumPageDirty = 0;
1001 
1002  /* Set parallel vacuum state */
1003  pvs.indrels = indrels;
1004  pvs.nindexes = nindexes;
1005  pvs.indstats = indstats;
1006  pvs.shared = shared;
1007  pvs.dead_items = dead_items;
1010 
1011  /* These fields will be filled during index vacuum or cleanup */
1012  pvs.indname = NULL;
1014 
1015  /* Each parallel VACUUM worker gets its own access strategy */
1017 
1018  /* Setup error traceback support for ereport() */
1020  errcallback.arg = &pvs;
1021  errcallback.previous = error_context_stack;
1022  error_context_stack = &errcallback;
1023 
1024  /* Prepare to track buffer usage during parallel execution */
1026 
1027  /* Process indexes to perform vacuum/cleanup */
1029 
1030  /* Report buffer/WAL usage during parallel execution */
1031  buffer_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, false);
1032  wal_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_WAL_USAGE, false);
1034  &wal_usage[ParallelWorkerNumber]);
1035 
1036  /* Pop the error context stack */
1037  error_context_stack = errcallback.previous;
1038 
1039  vac_close_indexes(nindexes, indrels, RowExclusiveLock);
1042 }
int ParallelWorkerNumber
Definition: parallel.c:113
void pgstat_report_activity(BackendState state, const char *cmd_str)
@ STATE_RUNNING
@ BAS_VACUUM
Definition: bufmgr.h:38
ErrorContextCallback * error_context_stack
Definition: elog.c:95
#define DEBUG1
Definition: elog.h:30
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:541
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:596
int64 VacuumPageHit
Definition: globals.c:148
int64 VacuumPageMiss
Definition: globals.c:149
bool VacuumCostActive
Definition: globals.c:153
int64 VacuumPageDirty
Definition: globals.c:150
int VacuumCostBalance
Definition: globals.c:152
double VacuumCostDelay
Definition: globals.c:146
void InstrEndParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition: instrument.c:208
void InstrStartParallelQuery(void)
Definition: instrument.c:200
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3331
char * pstrdup(const char *in)
Definition: mcxt.c:1624
#define PROC_IN_VACUUM
Definition: proc.h:57
#define RelationGetNamespace(relation)
Definition: rel.h:542
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition: shm_toc.c:232
PGPROC * MyProc
Definition: proc.c:66
struct ErrorContextCallback * previous
Definition: elog.h:295
void(* callback)(void *arg)
Definition: elog.h:296
uint8 statusFlags
Definition: proc.h:233
PVIndVacStatus status
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:86
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2147
int VacuumCostBalanceLocal
Definition: vacuum.c:87
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2190
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:85
static void parallel_vacuum_error_callback(void *arg)
static void parallel_vacuum_process_safe_indexes(ParallelVacuumState *pvs)
@ PARALLEL_INDVAC_STATUS_INITIAL

References PVShared::active_nworkers, ErrorContextCallback::arg, Assert(), BAS_VACUUM, ParallelVacuumState::bstrategy, ErrorContextCallback::callback, PVShared::cost_balance, ParallelVacuumState::dead_items, DEBUG1, debug_query_string, elog(), error_context_stack, FreeAccessStrategy(), get_namespace_name(), GetAccessStrategy(), ParallelVacuumState::indname, ParallelVacuumState::indrels, ParallelVacuumState::indstats, InstrEndParallelQuery(), InstrStartParallelQuery(), maintenance_work_mem, PVShared::maintenance_work_mem_worker, MyProc, ParallelVacuumState::nindexes, PARALLEL_INDVAC_STATUS_INITIAL, parallel_vacuum_error_callback(), PARALLEL_VACUUM_KEY_BUFFER_USAGE, PARALLEL_VACUUM_KEY_DEAD_ITEMS, PARALLEL_VACUUM_KEY_INDEX_STATS, PARALLEL_VACUUM_KEY_QUERY_TEXT, PARALLEL_VACUUM_KEY_SHARED, PARALLEL_VACUUM_KEY_WAL_USAGE, parallel_vacuum_process_safe_indexes(), ParallelWorkerNumber, pgstat_report_activity(), ErrorContextCallback::previous, PROC_IN_VACUUM, pstrdup(), RelationGetNamespace, RelationGetRelationName, PVShared::relid, ParallelVacuumState::relname, ParallelVacuumState::relnamespace, RowExclusiveLock, ParallelVacuumState::shared, ShareUpdateExclusiveLock, shm_toc_lookup(), STATE_RUNNING, ParallelVacuumState::status, PGPROC::statusFlags, table_close(), table_open(), vac_close_indexes(), vac_open_indexes(), VacuumActiveNWorkers, VacuumCostActive, VacuumCostBalance, VacuumCostBalanceLocal, VacuumCostDelay, VacuumPageDirty, VacuumPageHit, VacuumPageMiss, and VacuumSharedCostBalance.

◆ std_typanalyze()

bool std_typanalyze ( VacAttrStats stats)

Definition at line 1858 of file analyze.c.

1859 {
1860  Form_pg_attribute attr = stats->attr;
1861  Oid ltopr;
1862  Oid eqopr;
1863  StdAnalyzeData *mystats;
1864 
1865  /* If the attstattarget column is negative, use the default value */
1866  /* NB: it is okay to scribble on stats->attr since it's a copy */
1867  if (attr->attstattarget < 0)
1868  attr->attstattarget = default_statistics_target;
1869 
1870  /* Look for default "<" and "=" operators for column's type */
1872  false, false, false,
1873  &ltopr, &eqopr, NULL,
1874  NULL);
1875 
1876  /* Save the operator info for compute_stats routines */
1877  mystats = (StdAnalyzeData *) palloc(sizeof(StdAnalyzeData));
1878  mystats->eqopr = eqopr;
1879  mystats->eqfunc = OidIsValid(eqopr) ? get_opcode(eqopr) : InvalidOid;
1880  mystats->ltopr = ltopr;
1881  stats->extra_data = mystats;
1882 
1883  /*
1884  * Determine which standard statistics algorithm to use
1885  */
1886  if (OidIsValid(eqopr) && OidIsValid(ltopr))
1887  {
1888  /* Seems to be a scalar datatype */
1890  /*--------------------
1891  * The following choice of minrows is based on the paper
1892  * "Random sampling for histogram construction: how much is enough?"
1893  * by Surajit Chaudhuri, Rajeev Motwani and Vivek Narasayya, in
1894  * Proceedings of ACM SIGMOD International Conference on Management
1895  * of Data, 1998, Pages 436-447. Their Corollary 1 to Theorem 5
1896  * says that for table size n, histogram size k, maximum relative
1897  * error in bin size f, and error probability gamma, the minimum
1898  * random sample size is
1899  * r = 4 * k * ln(2*n/gamma) / f^2
1900  * Taking f = 0.5, gamma = 0.01, n = 10^6 rows, we obtain
1901  * r = 305.82 * k
1902  * Note that because of the log function, the dependence on n is
1903  * quite weak; even at n = 10^12, a 300*k sample gives <= 0.66
1904  * bin size error with probability 0.99. So there's no real need to
1905  * scale for n, which is a good thing because we don't necessarily
1906  * know it at this point.
1907  *--------------------
1908  */
1909  stats->minrows = 300 * attr->attstattarget;
1910  }
1911  else if (OidIsValid(eqopr))
1912  {
1913  /* We can still recognize distinct values */
1915  /* Might as well use the same minrows as above */
1916  stats->minrows = 300 * attr->attstattarget;
1917  }
1918  else
1919  {
1920  /* Can't do much but the trivial stuff */
1922  /* Might as well use the same minrows as above */
1923  stats->minrows = 300 * attr->attstattarget;
1924  }
1925 
1926  return true;
1927 }
#define OidIsValid(objectId)
Definition: c.h:759
static void compute_scalar_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
Definition: analyze.c:2371
int default_statistics_target
Definition: analyze.c:83
static void compute_distinct_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
Definition: analyze.c:2028
static void compute_trivial_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
Definition: analyze.c:1938
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1267
void * palloc(Size size)
Definition: mcxt.c:1210
void get_sort_group_operators(Oid argtype, bool needLT, bool needEQ, bool needGT, Oid *ltOpr, Oid *eqOpr, Oid *gtOpr, bool *isHashable)
Definition: parse_oper.c:192
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
Oid attrtypid
Definition: vacuum.h:129
int minrows
Definition: vacuum.h:140
Form_pg_attribute attr
Definition: vacuum.h:128
void * extra_data
Definition: vacuum.h:141
AnalyzeAttrComputeStatsFunc compute_stats
Definition: vacuum.h:139

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

Referenced by array_typanalyze(), examine_attribute(), and examine_expression().

◆ vac_bulkdel_one_index()

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

Definition at line 2337 of file vacuum.c.

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

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

Referenced by lazy_vacuum_one_index(), and parallel_vacuum_process_one_index().

◆ vac_cleanup_one_index()

IndexBulkDeleteResult* vac_cleanup_one_index ( IndexVacuumInfo ivinfo,
IndexBulkDeleteResult istat 
)

Definition at line 2358 of file vacuum.c.

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

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

Referenced by lazy_cleanup_one_index(), and parallel_vacuum_process_one_index().

◆ vac_close_indexes()

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

Definition at line 2190 of file vacuum.c.

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

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

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

References RelationData::rd_rel.

Referenced by lazy_scan_heap(), and statapprox_heap().

◆ vac_max_items_to_alloc_size()

Size vac_max_items_to_alloc_size ( int  max_items)

Definition at line 2383 of file vacuum.c.

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

References Assert(), MaxAllocSize, and MAXDEADITEMS.

Referenced by dead_items_alloc(), and parallel_vacuum_init().

◆ vac_open_indexes()

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

Definition at line 2147 of file vacuum.c.

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

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

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

◆ vac_update_datfrozenxid()

void vac_update_datfrozenxid ( void  )

Definition at line 1476 of file vacuum.c.

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

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

Referenced by do_autovacuum(), and vacuum().

◆ vac_update_relstats()

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

Definition at line 1309 of file vacuum.c.

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

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

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

◆ vacuum()

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

Definition at line 311 of file vacuum.c.

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

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

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

◆ vacuum_delay_point()

void vacuum_delay_point ( void  )

Definition at line 2211 of file vacuum.c.

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

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

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

◆ vacuum_get_cutoffs()

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

Definition at line 964 of file vacuum.c.

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

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

Referenced by copy_table_data(), and heap_vacuum_rel().

◆ vacuum_is_permitted_for_relation()

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

Definition at line 595 of file vacuum.c.

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

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

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

◆ vacuum_open_relation()

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

Definition at line 651 of file vacuum.c.

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

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

Referenced by analyze_rel(), and vacuum_rel().

◆ vacuum_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( const struct VacuumCutoffs cutoffs)

Definition at line 1151 of file vacuum.c.

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

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

Referenced by lazy_check_wraparound_failsafe().

Variable Documentation

◆ default_statistics_target

PGDLLIMPORT int default_statistics_target
extern

◆ vacuum_failsafe_age

PGDLLIMPORT int vacuum_failsafe_age
extern

Definition at line 72 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

PGDLLIMPORT int vacuum_freeze_min_age
extern

Definition at line 68 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_freeze_table_age

PGDLLIMPORT int vacuum_freeze_table_age
extern

Definition at line 69 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_failsafe_age

PGDLLIMPORT int vacuum_multixact_failsafe_age
extern

Definition at line 73 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_multixact_freeze_min_age

PGDLLIMPORT int vacuum_multixact_freeze_min_age
extern

Definition at line 70 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_freeze_table_age

PGDLLIMPORT int vacuum_multixact_freeze_table_age
extern

Definition at line 71 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

PGDLLIMPORT int VacuumCostBalanceLocal
extern

◆ VacuumSharedCostBalance