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  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_TOAST   0x40 /* process the TOAST table, if any */
 
#define VACOPT_DISABLE_PAGE_SKIPPING   0x80 /* don't skip any pages */
 
#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_set_xid_limits (Relation rel, const VacuumParams *params, TransactionId *OldestXmin, MultiXactId *OldestMxact, TransactionId *FreezeLimit, MultiXactId *MultiXactCutoff)
 
bool vacuum_xid_failsafe_check (TransactionId relfrozenxid, MultiXactId relminmxid)
 
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 250 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   0x80 /* don't skip any pages */

Definition at line 190 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_PROCESS_TOAST

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

Definition at line 189 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 200 of file vacuum.h.

201 {
206 } VacOptValue;
VacOptValue
Definition: vacuum.h:201
@ VACOPTVALUE_AUTO
Definition: vacuum.h:203
@ VACOPTVALUE_ENABLED
Definition: vacuum.h:205
@ VACOPTVALUE_UNSPECIFIED
Definition: vacuum.h:202
@ VACOPTVALUE_DISABLED
Definition: vacuum.h:204

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,
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:156
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:906
#define WARNING
Definition: elog.h:32
#define DEBUG2
Definition: elog.h:25
#define INFO
Definition: elog.h:30
#define ereport(elevel,...)
Definition: elog.h:145
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:216
int log_min_duration
Definition: vacuum.h:224
Relation vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
Definition: vacuum.c:614
bool vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple, bits32 options)
Definition: vacuum.c:556
#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:185
pg_prng_state pg_global_prng_state
Definition: pg_prng.c:28
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 107 of file vacuum.c.

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

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

Referenced by standard_ProcessUtility().

◆ 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:1306
void * palloc0(Size size)
Definition: mcxt.c:1230
bool istat_updated
IndexBulkDeleteResult istat
ParallelContext * pcxt
PVIndStats * indstats
void ExitParallelMode(void)
Definition: xact.c:1048

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:937
unsigned char uint8
Definition: c.h:440
#define MemSet(start, val, len)
Definition: c.h:953
size_t Size
Definition: c.h:541
int maintenance_work_mem
Definition: globals.c:127
const char * debug_query_string
Definition: postgres.c:82
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:247
int max_items
Definition: vacuum.h:243
int num_items
Definition: vacuum.h:244
Size vac_max_items_to_alloc_size(int max_items)
Definition: vacuum.c:2331
#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:1035

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:33
ErrorContextCallback * error_context_stack
Definition: elog.c:94
#define DEBUG1
Definition: elog.h:26
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:1483
#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:68
struct ErrorContextCallback * previous
Definition: elog.h:234
void(* callback)(void *arg)
Definition: elog.h:235
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:84
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2102
int VacuumCostBalanceLocal
Definition: vacuum.c:85
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2145
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:83
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:711
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:1199
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 2285 of file vacuum.c.

2287 {
2288  /* Do bulk deletion */
2289  istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2290  (void *) dead_items);
2291 
2292  ereport(ivinfo->message_level,
2293  (errmsg("scanned index \"%s\" to remove %d row versions",
2294  RelationGetRelationName(ivinfo->index),
2295  dead_items->num_items)));
2296 
2297  return istat;
2298 }
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:691
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:2346

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

2307 {
2308  istat = index_vacuum_cleanup(ivinfo, istat);
2309 
2310  if (istat)
2311  ereport(ivinfo->message_level,
2312  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2313  RelationGetRelationName(ivinfo->index),
2314  istat->num_index_tuples,
2315  istat->num_pages),
2316  errdetail("%.0f index row versions were removed.\n"
2317  "%u index pages were newly deleted.\n"
2318  "%u index pages are currently deleted, of which %u are currently reusable.",
2319  istat->tuples_removed,
2320  istat->pages_newly_deleted,
2321  istat->pages_deleted, istat->pages_free)));
2322 
2323  return istat;
2324 }
int errdetail(const char *fmt,...)
Definition: elog.c:1039
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:712
BlockNumber pages_deleted
Definition: genam.h:81
BlockNumber pages_newly_deleted
Definition: genam.h:80
BlockNumber pages_free
Definition: genam.h:82
BlockNumber num_pages
Definition: genam.h:76
double tuples_removed
Definition: genam.h:79
double num_index_tuples
Definition: genam.h:78

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

Referenced by lazy_cleanup_one_index(), and parallel_vacuum_process_one_index().

◆ vac_close_indexes()

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

Definition at line 2145 of file vacuum.c.

2146 {
2147  if (Irel == NULL)
2148  return;
2149 
2150  while (nindexes--)
2151  {
2152  Relation ind = Irel[nindexes];
2153 
2154  index_close(ind, lockmode);
2155  }
2156  pfree(Irel);
2157 }
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 1186 of file vacuum.c.

1190 {
1191  BlockNumber old_rel_pages = relation->rd_rel->relpages;
1192  double old_rel_tuples = relation->rd_rel->reltuples;
1193  double old_density;
1194  double unscanned_pages;
1195  double total_tuples;
1196 
1197  /* If we did scan the whole table, just use the count as-is */
1198  if (scanned_pages >= total_pages)
1199  return scanned_tuples;
1200 
1201  /*
1202  * When successive VACUUM commands scan the same few pages again and
1203  * again, without anything from the table really changing, there is a risk
1204  * that our beliefs about tuple density will gradually become distorted.
1205  * This might be caused by vacuumlazy.c implementation details, such as
1206  * its tendency to always scan the last heap page. Handle that here.
1207  *
1208  * If the relation is _exactly_ the same size according to the existing
1209  * pg_class entry, and only a few of its pages (less than 2%) were
1210  * scanned, keep the existing value of reltuples. Also keep the existing
1211  * value when only a subset of rel's pages <= a single page were scanned.
1212  *
1213  * (Note: we might be returning -1 here.)
1214  */
1215  if (old_rel_pages == total_pages &&
1216  scanned_pages < (double) total_pages * 0.02)
1217  return old_rel_tuples;
1218  if (scanned_pages <= 1)
1219  return old_rel_tuples;
1220 
1221  /*
1222  * If old density is unknown, we can't do much except scale up
1223  * scanned_tuples to match total_pages.
1224  */
1225  if (old_rel_tuples < 0 || old_rel_pages == 0)
1226  return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1227 
1228  /*
1229  * Okay, we've covered the corner cases. The normal calculation is to
1230  * convert the old measurement to a density (tuples per page), then
1231  * estimate the number of tuples in the unscanned pages using that figure,
1232  * and finally add on the number of tuples in the scanned pages.
1233  */
1234  old_density = old_rel_tuples / old_rel_pages;
1235  unscanned_pages = (double) total_pages - (double) scanned_pages;
1236  total_tuples = old_density * unscanned_pages + scanned_tuples;
1237  return floor(total_tuples + 0.5);
1238 }

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

2332 {
2333  Assert(max_items <= MAXDEADITEMS(MaxAllocSize));
2334 
2335  return offsetof(VacDeadItems, items) + sizeof(ItemPointerData) * max_items;
2336 }
struct ItemPointerData ItemPointerData
#define MaxAllocSize
Definition: memutils.h:40
#define MAXDEADITEMS(avail_mem)
Definition: vacuum.h:250

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

2104 {
2105  List *indexoidlist;
2106  ListCell *indexoidscan;
2107  int i;
2108 
2109  Assert(lockmode != NoLock);
2110 
2111  indexoidlist = RelationGetIndexList(relation);
2112 
2113  /* allocate enough memory for all indexes */
2114  i = list_length(indexoidlist);
2115 
2116  if (i > 0)
2117  *Irel = (Relation *) palloc(i * sizeof(Relation));
2118  else
2119  *Irel = NULL;
2120 
2121  /* collect just the ready indexes */
2122  i = 0;
2123  foreach(indexoidscan, indexoidlist)
2124  {
2125  Oid indexoid = lfirst_oid(indexoidscan);
2126  Relation indrel;
2127 
2128  indrel = index_open(indexoid, lockmode);
2129  if (indrel->rd_index->indisready)
2130  (*Irel)[i++] = indrel;
2131  else
2132  index_close(indrel, lockmode);
2133  }
2134 
2135  *nindexes = i;
2136 
2137  list_free(indexoidlist);
2138 }
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:150
#define lfirst_oid(lc)
Definition: pg_list.h:172
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4738
Definition: pg_list.h:52
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 1449 of file vacuum.c.

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

1289 {
1290  Oid relid = RelationGetRelid(relation);
1291  Relation rd;
1292  HeapTuple ctup;
1293  Form_pg_class pgcform;
1294  bool dirty,
1295  futurexid,
1296  futuremxid;
1297  TransactionId oldfrozenxid;
1298  MultiXactId oldminmulti;
1299 
1300  rd = table_open(RelationRelationId, RowExclusiveLock);
1301 
1302  /* Fetch a copy of the tuple to scribble on */
1304  if (!HeapTupleIsValid(ctup))
1305  elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
1306  relid);
1307  pgcform = (Form_pg_class) GETSTRUCT(ctup);
1308 
1309  /* Apply statistical updates, if any, to copied tuple */
1310 
1311  dirty = false;
1312  if (pgcform->relpages != (int32) num_pages)
1313  {
1314  pgcform->relpages = (int32) num_pages;
1315  dirty = true;
1316  }
1317  if (pgcform->reltuples != (float4) num_tuples)
1318  {
1319  pgcform->reltuples = (float4) num_tuples;
1320  dirty = true;
1321  }
1322  if (pgcform->relallvisible != (int32) num_all_visible_pages)
1323  {
1324  pgcform->relallvisible = (int32) num_all_visible_pages;
1325  dirty = true;
1326  }
1327 
1328  /* Apply DDL updates, but not inside an outer transaction (see above) */
1329 
1330  if (!in_outer_xact)
1331  {
1332  /*
1333  * If we didn't find any indexes, reset relhasindex.
1334  */
1335  if (pgcform->relhasindex && !hasindex)
1336  {
1337  pgcform->relhasindex = false;
1338  dirty = true;
1339  }
1340 
1341  /* We also clear relhasrules and relhastriggers if needed */
1342  if (pgcform->relhasrules && relation->rd_rules == NULL)
1343  {
1344  pgcform->relhasrules = false;
1345  dirty = true;
1346  }
1347  if (pgcform->relhastriggers && relation->trigdesc == NULL)
1348  {
1349  pgcform->relhastriggers = false;
1350  dirty = true;
1351  }
1352  }
1353 
1354  /*
1355  * Update relfrozenxid, unless caller passed InvalidTransactionId
1356  * indicating it has no new data.
1357  *
1358  * Ordinarily, we don't let relfrozenxid go backwards. However, if the
1359  * stored relfrozenxid is "in the future" then it seems best to assume
1360  * it's corrupt, and overwrite with the oldest remaining XID in the table.
1361  * This should match vac_update_datfrozenxid() concerning what we consider
1362  * to be "in the future".
1363  */
1364  oldfrozenxid = pgcform->relfrozenxid;
1365  futurexid = false;
1366  if (frozenxid_updated)
1367  *frozenxid_updated = false;
1368  if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
1369  {
1370  bool update = false;
1371 
1372  if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
1373  update = true;
1374  else if (TransactionIdPrecedes(ReadNextTransactionId(), oldfrozenxid))
1375  futurexid = update = true;
1376 
1377  if (update)
1378  {
1379  pgcform->relfrozenxid = frozenxid;
1380  dirty = true;
1381  if (frozenxid_updated)
1382  *frozenxid_updated = true;
1383  }
1384  }
1385 
1386  /* Similarly for relminmxid */
1387  oldminmulti = pgcform->relminmxid;
1388  futuremxid = false;
1389  if (minmulti_updated)
1390  *minmulti_updated = false;
1391  if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
1392  {
1393  bool update = false;
1394 
1395  if (MultiXactIdPrecedes(oldminmulti, minmulti))
1396  update = true;
1397  else if (MultiXactIdPrecedes(ReadNextMultiXactId(), oldminmulti))
1398  futuremxid = update = true;
1399 
1400  if (update)
1401  {
1402  pgcform->relminmxid = minmulti;
1403  dirty = true;
1404  if (minmulti_updated)
1405  *minmulti_updated = true;
1406  }
1407  }
1408 
1409  /* If anything changed, write out the tuple. */
1410  if (dirty)
1411  heap_inplace_update(rd, ctup);
1412 
1414 
1415  if (futurexid)
1416  ereport(WARNING,
1418  errmsg_internal("overwrote invalid relfrozenxid value %u with new value %u for table \"%s\"",
1419  oldfrozenxid, frozenxid,
1420  RelationGetRelationName(relation))));
1421  if (futuremxid)
1422  ereport(WARNING,
1424  errmsg_internal("overwrote invalid relminmxid value %u with new value %u for table \"%s\"",
1425  oldminmulti, minmulti,
1426  RelationGetRelationName(relation))));
1427 }
signed int int32
Definition: c.h:430
float float4
Definition: c.h:565
int errmsg_internal(const char *fmt,...)
Definition: elog.c:993
#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 296 of file vacuum.c.

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

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_PROCESS_TOAST, VACOPT_VACUUM, vacuum_rel(), VacuumActiveNWorkers, VacuumCostActive, VacuumCostBalance, VacuumCostBalanceLocal, VacuumCostDelay, VacuumPageDirty, VacuumPageHit, VacuumPageMiss, and VacuumSharedCostBalance.

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

◆ vacuum_delay_point()

void vacuum_delay_point ( void  )

Definition at line 2166 of file vacuum.c.

2167 {
2168  double msec = 0;
2169 
2170  /* Always check for interrupts */
2172 
2174  return;
2175 
2176  /*
2177  * For parallel vacuum, the delay is computed based on the shared cost
2178  * balance. See compute_parallel_delay.
2179  */
2180  if (VacuumSharedCostBalance != NULL)
2181  msec = compute_parallel_delay();
2182  else if (VacuumCostBalance >= VacuumCostLimit)
2184 
2185  /* Nap if appropriate */
2186  if (msec > 0)
2187  {
2188  if (msec > VacuumCostDelay * 4)
2189  msec = VacuumCostDelay * 4;
2190 
2191  (void) WaitLatch(MyLatch,
2193  msec,
2196 
2197  VacuumCostBalance = 0;
2198 
2199  /* update balance values for workers */
2201 
2202  /* Might have gotten an interrupt while sleeping */
2204  }
2205 }
void AutoVacuumUpdateDelay(void)
Definition: autovacuum.c:1781
volatile sig_atomic_t InterruptPending
Definition: globals.c:30
int VacuumCostLimit
Definition: globals.c:145
struct Latch * MyLatch
Definition: globals.c:58
void ResetLatch(Latch *latch)
Definition: latch.c:683
int WaitLatch(Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info)
Definition: latch.c:476
#define WL_TIMEOUT
Definition: latch.h:128
#define WL_EXIT_ON_PM_DEATH
Definition: latch.h:130
#define WL_LATCH_SET
Definition: latch.h:125
static double compute_parallel_delay(void)
Definition: vacuum.c:2230
@ WAIT_EVENT_VACUUM_DELAY
Definition: wait_event.h:149

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

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

◆ vacuum_is_permitted_for_relation()

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

Definition at line 556 of file vacuum.c.

558 {
559  char *relname;
560  AclMode mode = 0;
561 
563 
564  /*
565  * A role has privileges to vacuum or analyze the relation if any of the
566  * following are true:
567  * - the role is a superuser
568  * - the role owns the relation
569  * - the role owns the current database and the relation is not shared
570  * - the role has been granted privileges to vacuum/analyze the relation
571  */
572  if (options & VACOPT_VACUUM)
573  mode |= ACL_VACUUM;
574  if (options & VACOPT_ANALYZE)
575  mode |= ACL_ANALYZE;
576  if (object_ownercheck(RelationRelationId, relid, GetUserId()) ||
577  (object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) && !reltuple->relisshared) ||
579  return true;
580 
581  relname = NameStr(reltuple->relname);
582 
583  if ((options & VACOPT_VACUUM) != 0)
584  {
586  (errmsg("permission denied to vacuum \"%s\", skipping it",
587  relname)));
588 
589  /*
590  * For VACUUM ANALYZE, both logs could show up, but just generate
591  * information for VACUUM as that would be the first one to be
592  * processed.
593  */
594  return false;
595  }
596 
597  if ((options & VACOPT_ANALYZE) != 0)
599  (errmsg("permission denied to analyze \"%s\", skipping it",
600  relname)));
601 
602  return false;
603 }
@ ACLCHECK_OK
Definition: acl.h:184
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4799
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4746
#define NameStr(name)
Definition: c.h:682
Oid GetUserId(void)
Definition: miscinit.c:497
uint64 AclMode
Definition: parsenodes.h:81
#define ACL_ANALYZE
Definition: parsenodes.h:98
#define ACL_VACUUM
Definition: parsenodes.h:97
static PgChecksumMode mode
Definition: pg_checksums.c:65
NameData relname
Definition: pg_class.h:38

References ACL_ANALYZE, ACL_VACUUM, ACLCHECK_OK, Assert(), ereport, errmsg(), GetUserId(), mode, 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 614 of file vacuum.c.

616 {
617  Relation rel;
618  bool rel_lock = true;
619  int elevel;
620 
622 
623  /*
624  * Open the relation and get the appropriate lock on it.
625  *
626  * There's a race condition here: the relation may have gone away since
627  * the last time we saw it. If so, we don't need to vacuum or analyze it.
628  *
629  * If we've been asked not to wait for the relation lock, acquire it first
630  * in non-blocking mode, before calling try_relation_open().
631  */
632  if (!(options & VACOPT_SKIP_LOCKED))
633  rel = try_relation_open(relid, lmode);
634  else if (ConditionalLockRelationOid(relid, lmode))
635  rel = try_relation_open(relid, NoLock);
636  else
637  {
638  rel = NULL;
639  rel_lock = false;
640  }
641 
642  /* if relation is opened, leave */
643  if (rel)
644  return rel;
645 
646  /*
647  * Relation could not be opened, hence generate if possible a log
648  * informing on the situation.
649  *
650  * If the RangeVar is not defined, we do not have enough information to
651  * provide a meaningful log statement. Chances are that the caller has
652  * intentionally not provided this information so that this logging is
653  * skipped, anyway.
654  */
655  if (relation == NULL)
656  return NULL;
657 
658  /*
659  * Determine the log level.
660  *
661  * For manual VACUUM or ANALYZE, we emit a WARNING to match the log
662  * statements in the permission checks; otherwise, only log if the caller
663  * so requested.
664  */
666  elevel = WARNING;
667  else if (verbose)
668  elevel = LOG;
669  else
670  return NULL;
671 
672  if ((options & VACOPT_VACUUM) != 0)
673  {
674  if (!rel_lock)
675  ereport(elevel,
676  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
677  errmsg("skipping vacuum of \"%s\" --- lock not available",
678  relation->relname)));
679  else
680  ereport(elevel,
682  errmsg("skipping vacuum of \"%s\" --- relation no longer exists",
683  relation->relname)));
684 
685  /*
686  * For VACUUM ANALYZE, both logs could show up, but just generate
687  * information for VACUUM as that would be the first one to be
688  * processed.
689  */
690  return NULL;
691  }
692 
693  if ((options & VACOPT_ANALYZE) != 0)
694  {
695  if (!rel_lock)
696  ereport(elevel,
697  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
698  errmsg("skipping analyze of \"%s\" --- lock not available",
699  relation->relname)));
700  else
701  ereport(elevel,
703  errmsg("skipping analyze of \"%s\" --- relation no longer exists",
704  relation->relname)));
705  }
706 
707  return NULL;
708 }
#define LOG
Definition: elog.h:27
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:152
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:77
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
char * relname
Definition: primnodes.h:77

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_set_xid_limits()

bool vacuum_set_xid_limits ( Relation  rel,
const VacuumParams params,
TransactionId OldestXmin,
MultiXactId OldestMxact,
TransactionId FreezeLimit,
MultiXactId MultiXactCutoff 
)

Definition at line 940 of file vacuum.c.

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

References Assert(), autovacuum_freeze_max_age, ereport, errhint(), errmsg(), FirstMultiXactId, FirstNormalTransactionId, VacuumParams::freeze_min_age, VacuumParams::freeze_table_age, GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), Min, VacuumParams::multixact_freeze_min_age, VacuumParams::multixact_freeze_table_age, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdPrecedesOrEquals(), MultiXactMemberFreezeThreshold(), OldSnapshotThresholdActive(), RelationData::rd_rel, ReadNextMultiXactId(), ReadNextTransactionId(), 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_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( TransactionId  relfrozenxid,
MultiXactId  relminmxid 
)

Definition at line 1126 of file vacuum.c.

1127 {
1128  TransactionId xid_skip_limit;
1129  MultiXactId multi_skip_limit;
1130  int skip_index_vacuum;
1131 
1132  Assert(TransactionIdIsNormal(relfrozenxid));
1133  Assert(MultiXactIdIsValid(relminmxid));
1134 
1135  /*
1136  * Determine the index skipping age to use. In any case no less than
1137  * autovacuum_freeze_max_age * 1.05.
1138  */
1139  skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
1140 
1141  xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
1142  if (!TransactionIdIsNormal(xid_skip_limit))
1143  xid_skip_limit = FirstNormalTransactionId;
1144 
1145  if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
1146  {
1147  /* The table's relfrozenxid is too old */
1148  return true;
1149  }
1150 
1151  /*
1152  * Similar to above, determine the index skipping age to use for
1153  * multixact. In any case no less than autovacuum_multixact_freeze_max_age *
1154  * 1.05.
1155  */
1156  skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
1158 
1159  multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
1160  if (multi_skip_limit < FirstMultiXactId)
1161  multi_skip_limit = FirstMultiXactId;
1162 
1163  if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
1164  {
1165  /* The table's relminmxid is too old */
1166  return true;
1167  }
1168 
1169  return false;
1170 }
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:127
#define Max(x, y)
Definition: c.h:931
int vacuum_multixact_failsafe_age
Definition: vacuum.c:71
int vacuum_failsafe_age
Definition: vacuum.c:70

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

Referenced by lazy_check_wraparound_failsafe().

Variable Documentation

◆ default_statistics_target

PGDLLIMPORT int default_statistics_target
extern

◆ vacuum_failsafe_age

PGDLLIMPORT int vacuum_failsafe_age
extern

Definition at line 70 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

PGDLLIMPORT int vacuum_freeze_min_age
extern

Definition at line 66 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ vacuum_freeze_table_age

PGDLLIMPORT int vacuum_freeze_table_age
extern

Definition at line 67 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ vacuum_multixact_failsafe_age

PGDLLIMPORT int vacuum_multixact_failsafe_age
extern

Definition at line 71 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 68 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ vacuum_multixact_freeze_table_age

PGDLLIMPORT int vacuum_multixact_freeze_table_age
extern

Definition at line 69 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_set_xid_limits().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

PGDLLIMPORT int VacuumCostBalanceLocal
extern

◆ VacuumSharedCostBalance