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, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, 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_relation_owner (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 ownership. 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 relation ownership could have
166  * changed in-between. Make sure to generate only logs for ANALYZE in
167  * this case.
168  */
170  onerel->rd_rel,
171  params->options & VACOPT_ANALYZE))
172  {
174  return;
175  }
176 
177  /*
178  * Silently ignore tables that are temp tables of other backends ---
179  * trying to analyze these is rather pointless, since their contents are
180  * probably not up-to-date on disk. (We don't throw a warning here; it
181  * would just lead to chatter during a database-wide ANALYZE.)
182  */
183  if (RELATION_IS_OTHER_TEMP(onerel))
184  {
186  return;
187  }
188 
189  /*
190  * We can ANALYZE any table except pg_statistic. See update_attstats
191  */
192  if (RelationGetRelid(onerel) == StatisticRelationId)
193  {
195  return;
196  }
197 
198  /*
199  * Check that it's of an analyzable relkind, and set up appropriately.
200  */
201  if (onerel->rd_rel->relkind == RELKIND_RELATION ||
202  onerel->rd_rel->relkind == RELKIND_MATVIEW)
203  {
204  /* Regular table, so we'll use the regular row acquisition function */
205  acquirefunc = acquire_sample_rows;
206  /* Also get regular table's size */
207  relpages = RelationGetNumberOfBlocks(onerel);
208  }
209  else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
210  {
211  /*
212  * For a foreign table, call the FDW's hook function to see whether it
213  * supports analysis.
214  */
215  FdwRoutine *fdwroutine;
216  bool ok = false;
217 
218  fdwroutine = GetFdwRoutineForRelation(onerel, false);
219 
220  if (fdwroutine->AnalyzeForeignTable != NULL)
221  ok = fdwroutine->AnalyzeForeignTable(onerel,
222  &acquirefunc,
223  &relpages);
224 
225  if (!ok)
226  {
228  (errmsg("skipping \"%s\" --- cannot analyze this foreign table",
229  RelationGetRelationName(onerel))));
231  return;
232  }
233  }
234  else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
235  {
236  /*
237  * For partitioned tables, we want to do the recursive ANALYZE below.
238  */
239  }
240  else
241  {
242  /* No need for a WARNING if we already complained during VACUUM */
243  if (!(params->options & VACOPT_VACUUM))
245  (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
246  RelationGetRelationName(onerel))));
248  return;
249  }
250 
251  /*
252  * OK, let's do it. First, initialize progress reporting.
253  */
255  RelationGetRelid(onerel));
256 
257  /*
258  * Do the normal non-recursive ANALYZE. We can skip this for partitioned
259  * tables, which don't contain any rows.
260  */
261  if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
262  do_analyze_rel(onerel, params, va_cols, acquirefunc,
263  relpages, false, in_outer_xact, elevel);
264 
265  /*
266  * If there are child tables, do recursive ANALYZE.
267  */
268  if (onerel->rd_rel->relhassubclass)
269  do_analyze_rel(onerel, params, va_cols, acquirefunc, relpages,
270  true, in_outer_xact, elevel);
271 
272  /*
273  * Close source relation now, but keep lock so that no one deletes it
274  * before we commit. (If someone did, they'd fail to clean up the entries
275  * we made in pg_statistic. Also, releasing the lock before commit would
276  * expose us to concurrent-update failures in update_attstats.)
277  */
278  relation_close(onerel, NoLock);
279 
281 }
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:216
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:1132
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:291
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define WARNING
Definition: elog.h:30
#define DEBUG2
Definition: elog.h:23
#define INFO
Definition: elog.h:28
#define ereport(elevel,...)
Definition: elog.h:143
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:428
#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:489
#define RelationGetRelationName(relation)
Definition: rel.h:523
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:643
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:109
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:633
bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, bits32 options)
Definition: vacuum.c:559
#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_relation_owner(), 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:273
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  ListCell *lc;
237 
238  foreach(lc, vacstmt->rels)
239  {
241 
242  if (vrel->va_cols != NIL)
243  ereport(ERROR,
244  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
245  errmsg("ANALYZE option must be specified when a column list is provided")));
246  }
247  }
248 
249  /*
250  * All freeze ages are zero if the FREEZE option is given; otherwise pass
251  * them as -1 which means to use the default values.
252  */
253  if (params.options & VACOPT_FREEZE)
254  {
255  params.freeze_min_age = 0;
256  params.freeze_table_age = 0;
257  params.multixact_freeze_min_age = 0;
258  params.multixact_freeze_table_age = 0;
259  }
260  else
261  {
262  params.freeze_min_age = -1;
263  params.freeze_table_age = -1;
264  params.multixact_freeze_min_age = -1;
265  params.multixact_freeze_table_age = -1;
266  }
267 
268  /* user-invoked vacuum is never "for wraparound" */
269  params.is_wraparound = false;
270 
271  /* user-invoked vacuum uses VACOPT_VERBOSE instead of log_min_duration */
272  params.log_min_duration = -1;
273 
274  /* Now go through the common routine */
275  vacuum(vacstmt->rels, &params, NULL, isTopLevel);
276 }
#define MAX_PARALLEL_WORKER_LIMIT
int32 defGetInt32(DefElem *def)
Definition: define.c:163
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
int errcode(int sqlerrcode)
Definition: elog.c:693
#define ERROR
Definition: elog.h:33
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:169
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:65
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
static long analyze(struct nfa *nfa)
Definition: regc_nfa.c:3044
char * defname
Definition: parsenodes.h:765
int location
Definition: parsenodes.h:769
Node * arg
Definition: parsenodes.h:766
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:3713
bool is_vacuumcmd
Definition: parsenodes.h:3715
List * rels
Definition: parsenodes.h:3714
static VacOptValue get_vacoptval_from_boolean(DefElem *def)
Definition: vacuum.c:2315
void vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy, bool isTopLevel)
Definition: vacuum.c:298
#define VACOPT_FREEZE
Definition: vacuum.h:186
#define VACOPT_SKIP_LOCKED
Definition: vacuum.h:188
#define VACOPT_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 454 of file vacuumparallel.c.

456 {
458 
459  /*
460  * We can only provide an approximate value of num_heap_tuples, at least
461  * for now.
462  */
463  pvs->shared->reltuples = num_table_tuples;
464  pvs->shared->estimated_count = true;
465 
466  parallel_vacuum_process_all_indexes(pvs, num_index_scans, true);
467 }
#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 473 of file vacuumparallel.c.

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

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 418 of file vacuumparallel.c.

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

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 445 of file vacuumparallel.c.

446 {
447  return pvs->dead_items;
448 }
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  for (int i = 0; i < nindexes; i++)
321  {
322  Relation indrel = indrels[i];
323  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
324 
325  /*
326  * Cleanup option should be either disabled, always performing in
327  * parallel or conditionally performing in parallel.
328  */
329  Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
330  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
331  Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
332 
333  if (!will_parallel_vacuum[i])
334  continue;
335 
336  if (indrel->rd_indam->amusemaintenanceworkmem)
337  nindexes_mwm++;
338 
339  /*
340  * Remember the number of indexes that support parallel operation for
341  * each phase.
342  */
343  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
345  if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
347  if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
349  }
351  pvs->indstats = indstats;
352 
353  /* Prepare shared information */
354  shared = (PVShared *) shm_toc_allocate(pcxt->toc, est_shared_len);
355  MemSet(shared, 0, est_shared_len);
356  shared->relid = RelationGetRelid(rel);
357  shared->elevel = elevel;
359  (nindexes_mwm > 0) ?
360  maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
362 
363  pg_atomic_init_u32(&(shared->cost_balance), 0);
364  pg_atomic_init_u32(&(shared->active_nworkers), 0);
365  pg_atomic_init_u32(&(shared->idx), 0);
366 
368  pvs->shared = shared;
369 
370  /* Prepare the dead_items space */
371  dead_items = (VacDeadItems *) shm_toc_allocate(pcxt->toc,
372  est_dead_items_len);
373  dead_items->max_items = max_items;
374  dead_items->num_items = 0;
375  MemSet(dead_items->items, 0, sizeof(ItemPointerData) * max_items);
377  pvs->dead_items = dead_items;
378 
379  /*
380  * Allocate space for each worker's BufferUsage and WalUsage; no need to
381  * initialize
382  */
383  buffer_usage = shm_toc_allocate(pcxt->toc,
384  mul_size(sizeof(BufferUsage), pcxt->nworkers));
386  pvs->buffer_usage = buffer_usage;
387  wal_usage = shm_toc_allocate(pcxt->toc,
388  mul_size(sizeof(WalUsage), pcxt->nworkers));
390  pvs->wal_usage = wal_usage;
391 
392  /* Store query string for workers */
393  if (debug_query_string)
394  {
395  char *sharedquery;
396 
397  sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
398  memcpy(sharedquery, debug_query_string, querylen + 1);
399  sharedquery[querylen] = '\0';
400  shm_toc_insert(pcxt->toc,
401  PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
402  }
403 
404  /* Success -- return parallel vacuum state */
405  return pvs;
406 }
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:223
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:202
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:164
#define Min(x, y)
Definition: c.h:986
unsigned char uint8
Definition: c.h:439
#define MemSet(start, val, len)
Definition: c.h:1008
size_t Size
Definition: c.h:540
int maintenance_work_mem
Definition: globals.c:127
const char * debug_query_string
Definition: postgres.c:89
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:250
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:2372
#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:1032

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 935 of file vacuumparallel.c.

936 {
938  Relation rel;
939  Relation *indrels;
940  PVIndStats *indstats;
941  PVShared *shared;
942  VacDeadItems *dead_items;
943  BufferUsage *buffer_usage;
944  WalUsage *wal_usage;
945  int nindexes;
946  char *sharedquery;
947  ErrorContextCallback errcallback;
948 
949  /*
950  * A parallel vacuum worker must have only PROC_IN_VACUUM flag since we
951  * don't support parallel vacuum for autovacuum as of now.
952  */
954 
955  elog(DEBUG1, "starting parallel vacuum worker");
956 
957  shared = (PVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false);
958 
959  /* Set debug_query_string for individual workers */
960  sharedquery = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_QUERY_TEXT, true);
961  debug_query_string = sharedquery;
963 
964  /*
965  * Open table. The lock mode is the same as the leader process. It's
966  * okay because the lock mode does not conflict among the parallel
967  * workers.
968  */
969  rel = table_open(shared->relid, ShareUpdateExclusiveLock);
970 
971  /*
972  * Open all indexes. indrels are sorted in order by OID, which should be
973  * matched to the leader's one.
974  */
975  vac_open_indexes(rel, RowExclusiveLock, &nindexes, &indrels);
976  Assert(nindexes > 0);
977 
978  if (shared->maintenance_work_mem_worker > 0)
980 
981  /* Set index statistics */
982  indstats = (PVIndStats *) shm_toc_lookup(toc,
984  false);
985 
986  /* Set dead_items space */
987  dead_items = (VacDeadItems *) shm_toc_lookup(toc,
989  false);
990 
991  /* Set cost-based vacuum delay */
993  VacuumCostBalance = 0;
994  VacuumPageHit = 0;
995  VacuumPageMiss = 0;
996  VacuumPageDirty = 0;
1000 
1001  /* Set parallel vacuum state */
1002  pvs.indrels = indrels;
1003  pvs.nindexes = nindexes;
1004  pvs.indstats = indstats;
1005  pvs.shared = shared;
1006  pvs.dead_items = dead_items;
1009 
1010  /* These fields will be filled during index vacuum or cleanup */
1011  pvs.indname = NULL;
1013 
1014  /* Each parallel VACUUM worker gets its own access strategy */
1016 
1017  /* Setup error traceback support for ereport() */
1019  errcallback.arg = &pvs;
1020  errcallback.previous = error_context_stack;
1021  error_context_stack = &errcallback;
1022 
1023  /* Prepare to track buffer usage during parallel execution */
1025 
1026  /* Process indexes to perform vacuum/cleanup */
1028 
1029  /* Report buffer/WAL usage during parallel execution */
1030  buffer_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, false);
1031  wal_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_WAL_USAGE, false);
1033  &wal_usage[ParallelWorkerNumber]);
1034 
1035  /* Pop the error context stack */
1036  error_context_stack = errcallback.previous;
1037 
1038  vac_close_indexes(nindexes, indrels, RowExclusiveLock);
1041 }
int ParallelWorkerNumber
Definition: parallel.c:112
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:93
#define DEBUG1
Definition: elog.h:24
#define elog(elevel,...)
Definition: elog.h:218
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:3326
char * pstrdup(const char *in)
Definition: mcxt.c:1305
#define PROC_IN_VACUUM
Definition: proc.h:55
#define RelationGetNamespace(relation)
Definition: rel.h:530
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:232
void(* callback)(void *arg)
Definition: elog.h:233
uint8 statusFlags
Definition: proc.h:227
PVIndVacStatus status
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:84
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2143
int VacuumCostBalanceLocal
Definition: vacuum.c:85
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2186
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 1855 of file analyze.c.

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

2328 {
2329  /* Do bulk deletion */
2330  istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2331  (void *) dead_items);
2332 
2333  ereport(ivinfo->message_level,
2334  (errmsg("scanned index \"%s\" to remove %d row versions",
2335  RelationGetRelationName(ivinfo->index),
2336  dead_items->num_items)));
2337 
2338  return istat;
2339 }
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:2387

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

2348 {
2349  istat = index_vacuum_cleanup(ivinfo, istat);
2350 
2351  if (istat)
2352  ereport(ivinfo->message_level,
2353  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2354  RelationGetRelationName(ivinfo->index),
2355  istat->num_index_tuples,
2356  istat->num_pages),
2357  errdetail("%.0f index row versions were removed.\n"
2358  "%u index pages were newly deleted.\n"
2359  "%u index pages are currently deleted, of which %u are currently reusable.",
2360  istat->tuples_removed,
2361  istat->pages_newly_deleted,
2362  istat->pages_deleted, istat->pages_free)));
2363 
2364  return istat;
2365 }
int errdetail(const char *fmt,...)
Definition: elog.c:1037
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:712
BlockNumber pages_deleted
Definition: genam.h:81
BlockNumber pages_newly_deleted
Definition: genam.h:80
BlockNumber pages_free
Definition: genam.h:82
BlockNumber num_pages
Definition: genam.h:76
double tuples_removed
Definition: genam.h:79
double num_index_tuples
Definition: genam.h:78

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

Referenced by lazy_cleanup_one_index(), and parallel_vacuum_process_one_index().

◆ vac_close_indexes()

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

Definition at line 2186 of file vacuum.c.

2187 {
2188  if (Irel == NULL)
2189  return;
2190 
2191  while (nindexes--)
2192  {
2193  Relation ind = Irel[nindexes];
2194 
2195  index_close(ind, lockmode);
2196  }
2197  pfree(Irel);
2198 }
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 1222 of file vacuum.c.

1226 {
1227  BlockNumber old_rel_pages = relation->rd_rel->relpages;
1228  double old_rel_tuples = relation->rd_rel->reltuples;
1229  double old_density;
1230  double unscanned_pages;
1231  double total_tuples;
1232 
1233  /* If we did scan the whole table, just use the count as-is */
1234  if (scanned_pages >= total_pages)
1235  return scanned_tuples;
1236 
1237  /*
1238  * If scanned_pages is zero but total_pages isn't, keep the existing value
1239  * of reltuples. (Note: we might be returning -1 in this case.)
1240  */
1241  if (scanned_pages == 0)
1242  return old_rel_tuples;
1243 
1244  /*
1245  * When successive VACUUM commands scan the same few pages again and
1246  * again, without anything from the table really changing, there is a risk
1247  * that our beliefs about tuple density will gradually become distorted.
1248  * It's particularly important to avoid becoming confused in this way due
1249  * to vacuumlazy.c implementation details. For example, the tendency for
1250  * our caller to always scan the last heap page should not ever cause us
1251  * to believe that every page in the table must be just like the last
1252  * page.
1253  *
1254  * We apply a heuristic to avoid these problems: if the relation is
1255  * exactly the same size as it was at the end of the last VACUUM, and only
1256  * a few of its pages (less than a quasi-arbitrary threshold of 2%) were
1257  * scanned by this VACUUM, assume that reltuples has not changed at all.
1258  */
1259  if (old_rel_pages == total_pages &&
1260  scanned_pages < (double) total_pages * 0.02)
1261  return old_rel_tuples;
1262 
1263  /*
1264  * If old density is unknown, we can't do much except scale up
1265  * scanned_tuples to match total_pages.
1266  */
1267  if (old_rel_tuples < 0 || old_rel_pages == 0)
1268  return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1269 
1270  /*
1271  * Okay, we've covered the corner cases. The normal calculation is to
1272  * convert the old measurement to a density (tuples per page), then
1273  * estimate the number of tuples in the unscanned pages using that figure,
1274  * and finally add on the number of tuples in the scanned pages.
1275  */
1276  old_density = old_rel_tuples / old_rel_pages;
1277  unscanned_pages = (double) total_pages - (double) scanned_pages;
1278  total_tuples = old_density * unscanned_pages + scanned_tuples;
1279  return floor(total_tuples + 0.5);
1280 }

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

2373 {
2374  Assert(max_items <= MAXDEADITEMS(MaxAllocSize));
2375 
2376  return offsetof(VacDeadItems, items) + sizeof(ItemPointerData) * max_items;
2377 }
#define offsetof(type, field)
Definition: c.h:727
struct ItemPointerData ItemPointerData
#define MaxAllocSize
Definition: memutils.h:40
#define MAXDEADITEMS(avail_mem)
Definition: vacuum.h:250

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

Referenced by dead_items_alloc(), and parallel_vacuum_init().

◆ vac_open_indexes()

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

Definition at line 2143 of file vacuum.c.

2145 {
2146  List *indexoidlist;
2147  ListCell *indexoidscan;
2148  int i;
2149 
2150  Assert(lockmode != NoLock);
2151 
2152  indexoidlist = RelationGetIndexList(relation);
2153 
2154  /* allocate enough memory for all indexes */
2155  i = list_length(indexoidlist);
2156 
2157  if (i > 0)
2158  *Irel = (Relation *) palloc(i * sizeof(Relation));
2159  else
2160  *Irel = NULL;
2161 
2162  /* collect just the ready indexes */
2163  i = 0;
2164  foreach(indexoidscan, indexoidlist)
2165  {
2166  Oid indexoid = lfirst_oid(indexoidscan);
2167  Relation indrel;
2168 
2169  indrel = index_open(indexoid, lockmode);
2170  if (indrel->rd_index->indisready)
2171  (*Irel)[i++] = indrel;
2172  else
2173  index_close(indrel, lockmode);
2174  }
2175 
2176  *nindexes = i;
2177 
2178  list_free(indexoidlist);
2179 }
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
void list_free(List *list)
Definition: list.c:1505
static int list_length(const List *l)
Definition: pg_list.h:149
#define lfirst_oid(lc)
Definition: pg_list.h:171
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4675
Definition: pg_list.h:51
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 1491 of file vacuum.c.

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

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

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

2208 {
2209  double msec = 0;
2210 
2211  /* Always check for interrupts */
2213 
2215  return;
2216 
2217  /*
2218  * For parallel vacuum, the delay is computed based on the shared cost
2219  * balance. See compute_parallel_delay.
2220  */
2221  if (VacuumSharedCostBalance != NULL)
2222  msec = compute_parallel_delay();
2223  else if (VacuumCostBalance >= VacuumCostLimit)
2225 
2226  /* Nap if appropriate */
2227  if (msec > 0)
2228  {
2229  if (msec > VacuumCostDelay * 4)
2230  msec = VacuumCostDelay * 4;
2231 
2232  (void) WaitLatch(MyLatch,
2234  msec,
2237 
2238  VacuumCostBalance = 0;
2239 
2240  /* update balance values for workers */
2242 
2243  /* Might have gotten an interrupt while sleeping */
2245  }
2246 }
void AutoVacuumUpdateDelay(void)
Definition: autovacuum.c:1779
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:658
int WaitLatch(Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info)
Definition: latch.c:451
#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:2271
@ WAIT_EVENT_VACUUM_DELAY
Definition: wait_event.h:148

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

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

Definition at line 559 of file vacuum.c.

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

References Assert(), ereport, errmsg(), GetUserId(), MyDatabaseId, NameStr, pg_class_ownercheck(), pg_database_ownercheck(), relname, VACOPT_ANALYZE, VACOPT_VACUUM, and WARNING.

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

◆ vacuum_open_relation()

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

Definition at line 633 of file vacuum.c.

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

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,
int  freeze_min_age,
int  freeze_table_age,
int  multixact_freeze_min_age,
int  multixact_freeze_table_age,
TransactionId oldestXmin,
MultiXactId oldestMxact,
TransactionId freezeLimit,
MultiXactId multiXactCutoff 
)

Definition at line 959 of file vacuum.c.

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

References Assert(), autovacuum_freeze_max_age, ereport, errhint(), errmsg(), FirstMultiXactId, FirstNormalTransactionId, GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), Min, MultiXactIdPrecedes(), 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 1162 of file vacuum.c.

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

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

Referenced by lazy_check_wraparound_failsafe().

Variable Documentation

◆ 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