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

Go to the source code of this file.

Data Structures

struct  VacAttrStats
 
struct  VacuumParams
 
struct  VacuumCutoffs
 
struct  VacDeadItems
 

Macros

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

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, MemoryContext vac_context, bool isTopLevel)
 
void vac_open_indexes (Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
 
void vac_close_indexes (int nindexes, Relation *Irel, LOCKMODE lockmode)
 
double vac_estimate_reltuples (Relation relation, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples)
 
void vac_update_relstats (Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti, bool *frozenxid_updated, bool *minmulti_updated, bool in_outer_xact)
 
bool vacuum_get_cutoffs (Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
 
bool vacuum_xid_failsafe_check (const struct VacuumCutoffs *cutoffs)
 
void vac_update_datfrozenxid (void)
 
void vacuum_delay_point (void)
 
bool vacuum_is_permitted_for_relation (Oid relid, Form_pg_class reltuple, bits32 options)
 
Relation vacuum_open_relation (Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
 
IndexBulkDeleteResultvac_bulkdel_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat, VacDeadItems *dead_items)
 
IndexBulkDeleteResultvac_cleanup_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat)
 
Size vac_max_items_to_alloc_size (int max_items)
 
void AutoVacuumUpdateCostLimit (void)
 
void VacuumUpdateCosts (void)
 
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
 
PGDLLIMPORT bool VacuumFailsafeActive
 
PGDLLIMPORT double vacuum_cost_delay
 
PGDLLIMPORT int vacuum_cost_limit
 

Macro Definition Documentation

◆ MAX_STATISTICS_TARGET

#define MAX_STATISTICS_TARGET   10000

Definition at line 309 of file vacuum.h.

◆ MAXDEADITEMS

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

Definition at line 292 of file vacuum.h.

◆ VACOPT_ANALYZE

#define VACOPT_ANALYZE   0x02 /* do ANALYZE */

Definition at line 180 of file vacuum.h.

◆ VACOPT_DISABLE_PAGE_SKIPPING

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

Definition at line 187 of file vacuum.h.

◆ VACOPT_FREEZE

#define VACOPT_FREEZE   0x08 /* FREEZE option */

Definition at line 182 of file vacuum.h.

◆ VACOPT_FULL

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

Definition at line 183 of file vacuum.h.

◆ VACOPT_ONLY_DATABASE_STATS

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

Definition at line 189 of file vacuum.h.

◆ VACOPT_PROCESS_MAIN

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

Definition at line 185 of file vacuum.h.

◆ VACOPT_PROCESS_TOAST

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

Definition at line 186 of file vacuum.h.

◆ VACOPT_SKIP_DATABASE_STATS

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

Definition at line 188 of file vacuum.h.

◆ VACOPT_SKIP_LOCKED

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

Definition at line 184 of file vacuum.h.

◆ VACOPT_VACUUM

#define VACOPT_VACUUM   0x01 /* do VACUUM */

Definition at line 179 of file vacuum.h.

◆ VACOPT_VERBOSE

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

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

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

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 111 of file analyze.c.

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

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_VACUUM, VACOPT_VERBOSE, vacuum_is_permitted_for_relation(), vacuum_open_relation(), and WARNING.

Referenced by vacuum().

◆ anl_get_next_S()

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

Definition at line 296 of file sampling.c.

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

◆ anl_init_selection_state()

double anl_init_selection_state ( int  n)

Definition at line 281 of file sampling.c.

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

◆ anl_random_fract()

double anl_random_fract ( void  )

Definition at line 266 of file sampling.c.

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

◆ AutoVacuumUpdateCostLimit()

void AutoVacuumUpdateCostLimit ( void  )

Definition at line 1701 of file autovacuum.c.

1702 {
1703  if (!MyWorkerInfo)
1704  return;
1705 
1706  /*
1707  * note: in cost_limit, zero also means use value from elsewhere, because
1708  * zero is not a valid value.
1709  */
1710 
1713  else
1714  {
1715  int nworkers_for_balance;
1716 
1717  if (autovacuum_vac_cost_limit > 0)
1719  else
1721 
1722  /* Only balance limit if no cost-related storage parameters specified */
1724  return;
1725 
1727 
1728  nworkers_for_balance = pg_atomic_read_u32(&AutoVacuumShmem->av_nworkersForBalance);
1729 
1730  /* There is at least 1 autovac worker (this worker) */
1731  if (nworkers_for_balance <= 0)
1732  elog(ERROR, "nworkers_for_balance must be > 0");
1733 
1734  vacuum_cost_limit = Max(vacuum_cost_limit / nworkers_for_balance, 1);
1735  }
1736 }
static bool pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr)
Definition: atomics.h:191
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:234
int autovacuum_vac_cost_limit
Definition: autovacuum.c:129
static int av_storage_param_cost_limit
Definition: autovacuum.c:147
static AutoVacuumShmemStruct * AutoVacuumShmem
Definition: autovacuum.c:298
static WorkerInfo MyWorkerInfo
Definition: autovacuum.c:308
#define Max(x, y)
Definition: c.h:985
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
int VacuumCostLimit
Definition: globals.c:151
Assert(fmt[strlen(fmt) - 1] !='\n')
pg_atomic_uint32 av_nworkersForBalance
Definition: autovacuum.c:295
pg_atomic_flag wi_dobalance
Definition: autovacuum.c:233
int vacuum_cost_limit
Definition: vacuum.c:81

References Assert(), autovacuum_vac_cost_limit, AutoVacuumShmem, AutoVacuumShmemStruct::av_nworkersForBalance, av_storage_param_cost_limit, elog, ERROR, Max, MyWorkerInfo, pg_atomic_read_u32(), pg_atomic_unlocked_test_flag(), vacuum_cost_limit, VacuumCostLimit, and WorkerInfoData::wi_dobalance.

Referenced by vacuum_delay_point(), and VacuumUpdateCosts().

◆ ExecVacuum()

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

Definition at line 148 of file vacuum.c.

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

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

471 {
473 
474  /*
475  * We can only provide an approximate value of num_heap_tuples, at least
476  * for now.
477  */
478  pvs->shared->reltuples = num_table_tuples;
479  pvs->shared->estimated_count = true;
480 
481  parallel_vacuum_process_all_indexes(pvs, num_index_scans, true);
482 }
#define IsParallelWorker()
Definition: parallel.h:60
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 488 of file vacuumparallel.c.

490 {
492 
493  /*
494  * We can provide a better estimate of total number of surviving tuples
495  * (we assume indexes are more interested in that than in the number of
496  * nominally live tuples).
497  */
498  pvs->shared->reltuples = num_table_tuples;
499  pvs->shared->estimated_count = estimated_count;
500 
501  parallel_vacuum_process_all_indexes(pvs, num_index_scans, false);
502 }

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

434 {
436 
437  /* Copy the updated statistics */
438  for (int i = 0; i < pvs->nindexes; i++)
439  {
440  PVIndStats *indstats = &(pvs->indstats[i]);
441 
442  if (indstats->istat_updated)
443  {
444  istats[i] = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
445  memcpy(istats[i], &indstats->istat, sizeof(IndexBulkDeleteResult));
446  }
447  else
448  istats[i] = NULL;
449  }
450 
453 
455  pfree(pvs);
456 }
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:929
int i
Definition: isn.c:73
void pfree(void *pointer)
Definition: mcxt.c:1508
void * palloc0(Size size)
Definition: mcxt.c:1334
bool istat_updated
IndexBulkDeleteResult istat
ParallelContext * pcxt
PVIndStats * indstats
void ExitParallelMode(void)
Definition: xact.c:1050

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

461 {
462  return pvs->dead_items;
463 }
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 234 of file vacuumparallel.c.

237 {
238  ParallelVacuumState *pvs;
239  ParallelContext *pcxt;
240  PVShared *shared;
241  VacDeadItems *dead_items;
242  PVIndStats *indstats;
243  BufferUsage *buffer_usage;
244  WalUsage *wal_usage;
245  bool *will_parallel_vacuum;
246  Size est_indstats_len;
247  Size est_shared_len;
248  Size est_dead_items_len;
249  int nindexes_mwm = 0;
250  int parallel_workers = 0;
251  int querylen;
252 
253  /*
254  * A parallel vacuum must be requested and there must be indexes on the
255  * relation
256  */
257  Assert(nrequested_workers >= 0);
258  Assert(nindexes > 0);
259 
260  /*
261  * Compute the number of parallel vacuum workers to launch
262  */
263  will_parallel_vacuum = (bool *) palloc0(sizeof(bool) * nindexes);
264  parallel_workers = parallel_vacuum_compute_workers(indrels, nindexes,
265  nrequested_workers,
266  will_parallel_vacuum);
267  if (parallel_workers <= 0)
268  {
269  /* Can't perform vacuum in parallel -- return NULL */
270  pfree(will_parallel_vacuum);
271  return NULL;
272  }
273 
275  pvs->indrels = indrels;
276  pvs->nindexes = nindexes;
277  pvs->will_parallel_vacuum = will_parallel_vacuum;
278  pvs->bstrategy = bstrategy;
279  pvs->heaprel = rel;
280 
282  pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
283  parallel_workers);
284  Assert(pcxt->nworkers > 0);
285  pvs->pcxt = pcxt;
286 
287  /* Estimate size for index vacuum stats -- PARALLEL_VACUUM_KEY_INDEX_STATS */
288  est_indstats_len = mul_size(sizeof(PVIndStats), nindexes);
289  shm_toc_estimate_chunk(&pcxt->estimator, est_indstats_len);
290  shm_toc_estimate_keys(&pcxt->estimator, 1);
291 
292  /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
293  est_shared_len = sizeof(PVShared);
294  shm_toc_estimate_chunk(&pcxt->estimator, est_shared_len);
295  shm_toc_estimate_keys(&pcxt->estimator, 1);
296 
297  /* Estimate size for dead_items -- PARALLEL_VACUUM_KEY_DEAD_ITEMS */
298  est_dead_items_len = vac_max_items_to_alloc_size(max_items);
299  shm_toc_estimate_chunk(&pcxt->estimator, est_dead_items_len);
300  shm_toc_estimate_keys(&pcxt->estimator, 1);
301 
302  /*
303  * Estimate space for BufferUsage and WalUsage --
304  * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
305  *
306  * If there are no extensions loaded that care, we could skip this. We
307  * have no way of knowing whether anyone's looking at pgBufferUsage or
308  * pgWalUsage, so do it unconditionally.
309  */
311  mul_size(sizeof(BufferUsage), pcxt->nworkers));
312  shm_toc_estimate_keys(&pcxt->estimator, 1);
314  mul_size(sizeof(WalUsage), pcxt->nworkers));
315  shm_toc_estimate_keys(&pcxt->estimator, 1);
316 
317  /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
318  if (debug_query_string)
319  {
320  querylen = strlen(debug_query_string);
321  shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
322  shm_toc_estimate_keys(&pcxt->estimator, 1);
323  }
324  else
325  querylen = 0; /* keep compiler quiet */
326 
327  InitializeParallelDSM(pcxt);
328 
329  /* Prepare index vacuum stats */
330  indstats = (PVIndStats *) shm_toc_allocate(pcxt->toc, est_indstats_len);
331  MemSet(indstats, 0, est_indstats_len);
332  for (int i = 0; i < nindexes; i++)
333  {
334  Relation indrel = indrels[i];
335  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
336 
337  /*
338  * Cleanup option should be either disabled, always performing in
339  * parallel or conditionally performing in parallel.
340  */
341  Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
342  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
343  Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
344 
345  if (!will_parallel_vacuum[i])
346  continue;
347 
348  if (indrel->rd_indam->amusemaintenanceworkmem)
349  nindexes_mwm++;
350 
351  /*
352  * Remember the number of indexes that support parallel operation for
353  * each phase.
354  */
355  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
357  if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
359  if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
361  }
363  pvs->indstats = indstats;
364 
365  /* Prepare shared information */
366  shared = (PVShared *) shm_toc_allocate(pcxt->toc, est_shared_len);
367  MemSet(shared, 0, est_shared_len);
368  shared->relid = RelationGetRelid(rel);
369  shared->elevel = elevel;
371  (nindexes_mwm > 0) ?
372  maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
374 
375  /* Use the same buffer size for all workers */
376  shared->ring_nbuffers = GetAccessStrategyBufferCount(bstrategy);
377 
378  pg_atomic_init_u32(&(shared->cost_balance), 0);
379  pg_atomic_init_u32(&(shared->active_nworkers), 0);
380  pg_atomic_init_u32(&(shared->idx), 0);
381 
383  pvs->shared = shared;
384 
385  /* Prepare the dead_items space */
386  dead_items = (VacDeadItems *) shm_toc_allocate(pcxt->toc,
387  est_dead_items_len);
388  dead_items->max_items = max_items;
389  dead_items->num_items = 0;
390  MemSet(dead_items->items, 0, sizeof(ItemPointerData) * max_items);
392  pvs->dead_items = dead_items;
393 
394  /*
395  * Allocate space for each worker's BufferUsage and WalUsage; no need to
396  * initialize
397  */
398  buffer_usage = shm_toc_allocate(pcxt->toc,
399  mul_size(sizeof(BufferUsage), pcxt->nworkers));
401  pvs->buffer_usage = buffer_usage;
402  wal_usage = shm_toc_allocate(pcxt->toc,
403  mul_size(sizeof(WalUsage), pcxt->nworkers));
405  pvs->wal_usage = wal_usage;
406 
407  /* Store query string for workers */
408  if (debug_query_string)
409  {
410  char *sharedquery;
411 
412  sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
413  memcpy(sharedquery, debug_query_string, querylen + 1);
414  sharedquery[querylen] = '\0';
415  shm_toc_insert(pcxt->toc,
416  PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
417  }
418 
419  /* Success -- return parallel vacuum state */
420  return pvs;
421 }
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:216
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:205
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:167
#define Min(x, y)
Definition: c.h:991
unsigned char uint8
Definition: c.h:491
#define MemSet(start, val, len)
Definition: c.h:1007
size_t Size
Definition: c.h:592
int GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
Definition: freelist.c:624
int maintenance_work_mem
Definition: globals.c:130
const char * debug_query_string
Definition: postgres.c:87
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:510
bool amusemaintenanceworkmem
Definition: amapi.h:251
uint8 amparallelvacuumoptions
Definition: amapi.h:255
pg_atomic_uint32 cost_balance
int maintenance_work_mem_worker
pg_atomic_uint32 active_nworkers
int ring_nbuffers
pg_atomic_uint32 idx
shm_toc_estimator estimator
Definition: parallel.h:41
shm_toc * toc
Definition: parallel.h:44
BufferAccessStrategy bstrategy
BufferUsage * buffer_usage
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
ItemPointerData items[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuum.h:289
int max_items
Definition: vacuum.h:285
int num_items
Definition: vacuum.h:286
Size vac_max_items_to_alloc_size(int max_items)
Definition: vacuum.c:2537
#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:1037

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, GetAccessStrategyBufferCount(), ParallelVacuumState::heaprel, 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, PVShared::ring_nbuffers, 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 957 of file vacuumparallel.c.

958 {
960  Relation rel;
961  Relation *indrels;
962  PVIndStats *indstats;
963  PVShared *shared;
964  VacDeadItems *dead_items;
965  BufferUsage *buffer_usage;
966  WalUsage *wal_usage;
967  int nindexes;
968  char *sharedquery;
969  ErrorContextCallback errcallback;
970 
971  /*
972  * A parallel vacuum worker must have only PROC_IN_VACUUM flag since we
973  * don't support parallel vacuum for autovacuum as of now.
974  */
976 
977  elog(DEBUG1, "starting parallel vacuum worker");
978 
979  shared = (PVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false);
980 
981  /* Set debug_query_string for individual workers */
982  sharedquery = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_QUERY_TEXT, true);
983  debug_query_string = sharedquery;
985 
986  /*
987  * Open table. The lock mode is the same as the leader process. It's
988  * okay because the lock mode does not conflict among the parallel
989  * workers.
990  */
991  rel = table_open(shared->relid, ShareUpdateExclusiveLock);
992 
993  /*
994  * Open all indexes. indrels are sorted in order by OID, which should be
995  * matched to the leader's one.
996  */
997  vac_open_indexes(rel, RowExclusiveLock, &nindexes, &indrels);
998  Assert(nindexes > 0);
999 
1000  if (shared->maintenance_work_mem_worker > 0)
1002 
1003  /* Set index statistics */
1004  indstats = (PVIndStats *) shm_toc_lookup(toc,
1006  false);
1007 
1008  /* Set dead_items space */
1009  dead_items = (VacDeadItems *) shm_toc_lookup(toc,
1011  false);
1012 
1013  /* Set cost-based vacuum delay */
1015  VacuumCostBalance = 0;
1016  VacuumPageHit = 0;
1017  VacuumPageMiss = 0;
1018  VacuumPageDirty = 0;
1022 
1023  /* Set parallel vacuum state */
1024  pvs.indrels = indrels;
1025  pvs.nindexes = nindexes;
1026  pvs.indstats = indstats;
1027  pvs.shared = shared;
1028  pvs.dead_items = dead_items;
1031  pvs.heaprel = rel;
1032 
1033  /* These fields will be filled during index vacuum or cleanup */
1034  pvs.indname = NULL;
1036 
1037  /* Each parallel VACUUM worker gets its own access strategy. */
1039  shared->ring_nbuffers * (BLCKSZ / 1024));
1040 
1041  /* Setup error traceback support for ereport() */
1043  errcallback.arg = &pvs;
1044  errcallback.previous = error_context_stack;
1045  error_context_stack = &errcallback;
1046 
1047  /* Prepare to track buffer usage during parallel execution */
1049 
1050  /* Process indexes to perform vacuum/cleanup */
1052 
1053  /* Report buffer/WAL usage during parallel execution */
1054  buffer_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, false);
1055  wal_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_WAL_USAGE, false);
1057  &wal_usage[ParallelWorkerNumber]);
1058 
1059  /* Pop the error context stack */
1060  error_context_stack = errcallback.previous;
1061 
1062  vac_close_indexes(nindexes, indrels, RowExclusiveLock);
1065 }
void VacuumUpdateCosts(void)
Definition: autovacuum.c:1632
int ParallelWorkerNumber
Definition: parallel.c:112
void pgstat_report_activity(BackendState state, const char *cmd_str)
@ STATE_RUNNING
ErrorContextCallback * error_context_stack
Definition: elog.c:94
#define DEBUG1
Definition: elog.h:30
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:639
int64 VacuumPageHit
Definition: globals.c:154
int64 VacuumPageMiss
Definition: globals.c:155
int64 VacuumPageDirty
Definition: globals.c:156
int VacuumCostBalance
Definition: globals.c:158
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:3322
char * pstrdup(const char *in)
Definition: mcxt.c:1683
#define PROC_IN_VACUUM
Definition: proc.h:58
#define RelationGetNamespace(relation)
Definition: rel.h:546
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition: shm_toc.c:232
PGPROC * MyProc
Definition: proc.c:66
struct ErrorContextCallback * previous
Definition: elog.h:295
void(* callback)(void *arg)
Definition: elog.h:296
uint8 statusFlags
Definition: proc.h:238
PVIndVacStatus status
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:103
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2273
int VacuumCostBalanceLocal
Definition: vacuum.c:104
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2316
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:102
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(), GetAccessStrategyWithSize(), ParallelVacuumState::heaprel, 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, PVShared::ring_nbuffers, RowExclusiveLock, ParallelVacuumState::shared, ShareUpdateExclusiveLock, shm_toc_lookup(), STATE_RUNNING, ParallelVacuumState::status, PGPROC::statusFlags, table_close(), table_open(), vac_close_indexes(), vac_open_indexes(), VacuumActiveNWorkers, VacuumCostBalance, VacuumCostBalanceLocal, VacuumPageDirty, VacuumPageHit, VacuumPageMiss, VacuumSharedCostBalance, and VacuumUpdateCosts().

◆ std_typanalyze()

bool std_typanalyze ( VacAttrStats stats)

Definition at line 1886 of file analyze.c.

1887 {
1888  Oid ltopr;
1889  Oid eqopr;
1890  StdAnalyzeData *mystats;
1891 
1892  /* If the attstattarget column is negative, use the default value */
1893  if (stats->attstattarget < 0)
1895 
1896  /* Look for default "<" and "=" operators for column's type */
1898  false, false, false,
1899  &ltopr, &eqopr, NULL,
1900  NULL);
1901 
1902  /* Save the operator info for compute_stats routines */
1903  mystats = (StdAnalyzeData *) palloc(sizeof(StdAnalyzeData));
1904  mystats->eqopr = eqopr;
1905  mystats->eqfunc = OidIsValid(eqopr) ? get_opcode(eqopr) : InvalidOid;
1906  mystats->ltopr = ltopr;
1907  stats->extra_data = mystats;
1908 
1909  /*
1910  * Determine which standard statistics algorithm to use
1911  */
1912  if (OidIsValid(eqopr) && OidIsValid(ltopr))
1913  {
1914  /* Seems to be a scalar datatype */
1916  /*--------------------
1917  * The following choice of minrows is based on the paper
1918  * "Random sampling for histogram construction: how much is enough?"
1919  * by Surajit Chaudhuri, Rajeev Motwani and Vivek Narasayya, in
1920  * Proceedings of ACM SIGMOD International Conference on Management
1921  * of Data, 1998, Pages 436-447. Their Corollary 1 to Theorem 5
1922  * says that for table size n, histogram size k, maximum relative
1923  * error in bin size f, and error probability gamma, the minimum
1924  * random sample size is
1925  * r = 4 * k * ln(2*n/gamma) / f^2
1926  * Taking f = 0.5, gamma = 0.01, n = 10^6 rows, we obtain
1927  * r = 305.82 * k
1928  * Note that because of the log function, the dependence on n is
1929  * quite weak; even at n = 10^12, a 300*k sample gives <= 0.66
1930  * bin size error with probability 0.99. So there's no real need to
1931  * scale for n, which is a good thing because we don't necessarily
1932  * know it at this point.
1933  *--------------------
1934  */
1935  stats->minrows = 300 * stats->attstattarget;
1936  }
1937  else if (OidIsValid(eqopr))
1938  {
1939  /* We can still recognize distinct values */
1941  /* Might as well use the same minrows as above */
1942  stats->minrows = 300 * stats->attstattarget;
1943  }
1944  else
1945  {
1946  /* Can't do much but the trivial stuff */
1948  /* Might as well use the same minrows as above */
1949  stats->minrows = 300 * stats->attstattarget;
1950  }
1951 
1952  return true;
1953 }
#define OidIsValid(objectId)
Definition: c.h:762
static void compute_scalar_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
Definition: analyze.c:2397
int default_statistics_target
Definition: analyze.c:73
static void compute_distinct_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
Definition: analyze.c:2054
static void compute_trivial_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows)
Definition: analyze.c:1964
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1263
void * palloc(Size size)
Definition: mcxt.c:1304
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:180
unsigned int Oid
Definition: postgres_ext.h:31
Oid attrtypid
Definition: vacuum.h:125
int minrows
Definition: vacuum.h:136
int attstattarget
Definition: vacuum.h:124
void * extra_data
Definition: vacuum.h:137
AnalyzeAttrComputeStatsFunc compute_stats
Definition: vacuum.h:135

References VacAttrStats::attrtypid, VacAttrStats::attstattarget, 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 2491 of file vacuum.c.

2493 {
2494  /* Do bulk deletion */
2495  istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2496  (void *) dead_items);
2497 
2498  ereport(ivinfo->message_level,
2499  (errmsg("scanned index \"%s\" to remove %d row versions",
2500  RelationGetRelationName(ivinfo->index),
2501  dead_items->num_items)));
2502 
2503  return istat;
2504 }
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:751
Relation index
Definition: genam.h:46
int message_level
Definition: genam.h:51
static bool vac_tid_reaped(ItemPointer itemptr, void *state)
Definition: vacuum.c:2552

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

2513 {
2514  istat = index_vacuum_cleanup(ivinfo, istat);
2515 
2516  if (istat)
2517  ereport(ivinfo->message_level,
2518  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2519  RelationGetRelationName(ivinfo->index),
2520  istat->num_index_tuples,
2521  istat->num_pages),
2522  errdetail("%.0f index row versions were removed.\n"
2523  "%u index pages were newly deleted.\n"
2524  "%u index pages are currently deleted, of which %u are currently reusable.",
2525  istat->tuples_removed,
2526  istat->pages_newly_deleted,
2527  istat->pages_deleted, istat->pages_free)));
2528 
2529  return istat;
2530 }
int errdetail(const char *fmt,...)
Definition: elog.c:1205
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:772
BlockNumber pages_deleted
Definition: genam.h:82
BlockNumber pages_newly_deleted
Definition: genam.h:81
BlockNumber pages_free
Definition: genam.h:83
BlockNumber num_pages
Definition: genam.h:77
double tuples_removed
Definition: genam.h:80
double num_index_tuples
Definition: genam.h:79

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

2317 {
2318  if (Irel == NULL)
2319  return;
2320 
2321  while (nindexes--)
2322  {
2323  Relation ind = Irel[nindexes];
2324 
2325  index_close(ind, lockmode);
2326  }
2327  pfree(Irel);
2328 }
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177

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

1307 {
1308  BlockNumber old_rel_pages = relation->rd_rel->relpages;
1309  double old_rel_tuples = relation->rd_rel->reltuples;
1310  double old_density;
1311  double unscanned_pages;
1312  double total_tuples;
1313 
1314  /* If we did scan the whole table, just use the count as-is */
1315  if (scanned_pages >= total_pages)
1316  return scanned_tuples;
1317 
1318  /*
1319  * When successive VACUUM commands scan the same few pages again and
1320  * again, without anything from the table really changing, there is a risk
1321  * that our beliefs about tuple density will gradually become distorted.
1322  * This might be caused by vacuumlazy.c implementation details, such as
1323  * its tendency to always scan the last heap page. Handle that here.
1324  *
1325  * If the relation is _exactly_ the same size according to the existing
1326  * pg_class entry, and only a few of its pages (less than 2%) were
1327  * scanned, keep the existing value of reltuples. Also keep the existing
1328  * value when only a subset of rel's pages <= a single page were scanned.
1329  *
1330  * (Note: we might be returning -1 here.)
1331  */
1332  if (old_rel_pages == total_pages &&
1333  scanned_pages < (double) total_pages * 0.02)
1334  return old_rel_tuples;
1335  if (scanned_pages <= 1)
1336  return old_rel_tuples;
1337 
1338  /*
1339  * If old density is unknown, we can't do much except scale up
1340  * scanned_tuples to match total_pages.
1341  */
1342  if (old_rel_tuples < 0 || old_rel_pages == 0)
1343  return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1344 
1345  /*
1346  * Okay, we've covered the corner cases. The normal calculation is to
1347  * convert the old measurement to a density (tuples per page), then
1348  * estimate the number of tuples in the unscanned pages using that figure,
1349  * and finally add on the number of tuples in the scanned pages.
1350  */
1351  old_density = old_rel_tuples / old_rel_pages;
1352  unscanned_pages = (double) total_pages - (double) scanned_pages;
1353  total_tuples = old_density * unscanned_pages + scanned_tuples;
1354  return floor(total_tuples + 0.5);
1355 }

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

2538 {
2539  Assert(max_items <= MAXDEADITEMS(MaxAllocSize));
2540 
2541  return offsetof(VacDeadItems, items) + sizeof(ItemPointerData) * max_items;
2542 }
struct ItemPointerData ItemPointerData
#define MaxAllocSize
Definition: memutils.h:40
#define MAXDEADITEMS(avail_mem)
Definition: vacuum.h:292

References Assert(), MaxAllocSize, and MAXDEADITEMS.

Referenced by dead_items_alloc(), and parallel_vacuum_init().

◆ vac_open_indexes()

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

Definition at line 2273 of file vacuum.c.

2275 {
2276  List *indexoidlist;
2277  ListCell *indexoidscan;
2278  int i;
2279 
2280  Assert(lockmode != NoLock);
2281 
2282  indexoidlist = RelationGetIndexList(relation);
2283 
2284  /* allocate enough memory for all indexes */
2285  i = list_length(indexoidlist);
2286 
2287  if (i > 0)
2288  *Irel = (Relation *) palloc(i * sizeof(Relation));
2289  else
2290  *Irel = NULL;
2291 
2292  /* collect just the ready indexes */
2293  i = 0;
2294  foreach(indexoidscan, indexoidlist)
2295  {
2296  Oid indexoid = lfirst_oid(indexoidscan);
2297  Relation indrel;
2298 
2299  indrel = index_open(indexoid, lockmode);
2300  if (indrel->rd_index->indisready)
2301  (*Irel)[i++] = indrel;
2302  else
2303  index_close(indrel, lockmode);
2304  }
2305 
2306  *nindexes = i;
2307 
2308  list_free(indexoidlist);
2309 }
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void list_free(List *list)
Definition: list.c:1546
static int list_length(const List *l)
Definition: pg_list.h:152
#define lfirst_oid(lc)
Definition: pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4749
Definition: pg_list.h:54
Form_pg_index rd_index
Definition: rel.h:192

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

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

◆ vac_update_datfrozenxid()

void vac_update_datfrozenxid ( void  )

Definition at line 1566 of file vacuum.c.

1567 {
1568  HeapTuple tuple;
1569  Form_pg_database dbform;
1570  Relation relation;
1571  SysScanDesc scan;
1572  HeapTuple classTup;
1573  TransactionId newFrozenXid;
1574  MultiXactId newMinMulti;
1575  TransactionId lastSaneFrozenXid;
1576  MultiXactId lastSaneMinMulti;
1577  bool bogus = false;
1578  bool dirty = false;
1579  ScanKeyData key[1];
1580 
1581  /*
1582  * Restrict this task to one backend per database. This avoids race
1583  * conditions that would move datfrozenxid or datminmxid backward. It
1584  * avoids calling vac_truncate_clog() with a datfrozenxid preceding a
1585  * datfrozenxid passed to an earlier vac_truncate_clog() call.
1586  */
1588 
1589  /*
1590  * Initialize the "min" calculation with
1591  * GetOldestNonRemovableTransactionId(), which is a reasonable
1592  * approximation to the minimum relfrozenxid for not-yet-committed
1593  * pg_class entries for new tables; see AddNewRelationTuple(). So we
1594  * cannot produce a wrong minimum by starting with this.
1595  */
1596  newFrozenXid = GetOldestNonRemovableTransactionId(NULL);
1597 
1598  /*
1599  * Similarly, initialize the MultiXact "min" with the value that would be
1600  * used on pg_class for new tables. See AddNewRelationTuple().
1601  */
1602  newMinMulti = GetOldestMultiXactId();
1603 
1604  /*
1605  * Identify the latest relfrozenxid and relminmxid values that we could
1606  * validly see during the scan. These are conservative values, but it's
1607  * not really worth trying to be more exact.
1608  */
1609  lastSaneFrozenXid = ReadNextTransactionId();
1610  lastSaneMinMulti = ReadNextMultiXactId();
1611 
1612  /*
1613  * We must seqscan pg_class to find the minimum Xid, because there is no
1614  * index that can help us here.
1615  */
1616  relation = table_open(RelationRelationId, AccessShareLock);
1617 
1618  scan = systable_beginscan(relation, InvalidOid, false,
1619  NULL, 0, NULL);
1620 
1621  while ((classTup = systable_getnext(scan)) != NULL)
1622  {
1623  Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
1624 
1625  /*
1626  * Only consider relations able to hold unfrozen XIDs (anything else
1627  * should have InvalidTransactionId in relfrozenxid anyway).
1628  */
1629  if (classForm->relkind != RELKIND_RELATION &&
1630  classForm->relkind != RELKIND_MATVIEW &&
1631  classForm->relkind != RELKIND_TOASTVALUE)
1632  {
1633  Assert(!TransactionIdIsValid(classForm->relfrozenxid));
1634  Assert(!MultiXactIdIsValid(classForm->relminmxid));
1635  continue;
1636  }
1637 
1638  /*
1639  * Some table AMs might not need per-relation xid / multixid horizons.
1640  * It therefore seems reasonable to allow relfrozenxid and relminmxid
1641  * to not be set (i.e. set to their respective Invalid*Id)
1642  * independently. Thus validate and compute horizon for each only if
1643  * set.
1644  *
1645  * If things are working properly, no relation should have a
1646  * relfrozenxid or relminmxid that is "in the future". However, such
1647  * cases have been known to arise due to bugs in pg_upgrade. If we
1648  * see any entries that are "in the future", chicken out and don't do
1649  * anything. This ensures we won't truncate clog & multixact SLRUs
1650  * before those relations have been scanned and cleaned up.
1651  */
1652 
1653  if (TransactionIdIsValid(classForm->relfrozenxid))
1654  {
1655  Assert(TransactionIdIsNormal(classForm->relfrozenxid));
1656 
1657  /* check for values in the future */
1658  if (TransactionIdPrecedes(lastSaneFrozenXid, classForm->relfrozenxid))
1659  {
1660  bogus = true;
1661  break;
1662  }
1663 
1664  /* determine new horizon */
1665  if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
1666  newFrozenXid = classForm->relfrozenxid;
1667  }
1668 
1669  if (MultiXactIdIsValid(classForm->relminmxid))
1670  {
1671  /* check for values in the future */
1672  if (MultiXactIdPrecedes(lastSaneMinMulti, classForm->relminmxid))
1673  {
1674  bogus = true;
1675  break;
1676  }
1677 
1678  /* determine new horizon */
1679  if (MultiXactIdPrecedes(classForm->relminmxid, newMinMulti))
1680  newMinMulti = classForm->relminmxid;
1681  }
1682  }
1683 
1684  /* we're done with pg_class */
1685  systable_endscan(scan);
1686  table_close(relation, AccessShareLock);
1687 
1688  /* chicken out if bogus data found */
1689  if (bogus)
1690  return;
1691 
1692  Assert(TransactionIdIsNormal(newFrozenXid));
1693  Assert(MultiXactIdIsValid(newMinMulti));
1694 
1695  /* Now fetch the pg_database tuple we need to update. */
1696  relation = table_open(DatabaseRelationId, RowExclusiveLock);
1697 
1698  /*
1699  * Get the pg_database tuple to scribble on. Note that this does not
1700  * directly rely on the syscache to avoid issues with flattened toast
1701  * values for the in-place update.
1702  */
1703  ScanKeyInit(&key[0],
1704  Anum_pg_database_oid,
1705  BTEqualStrategyNumber, F_OIDEQ,
1707 
1708  scan = systable_beginscan(relation, DatabaseOidIndexId, true,
1709  NULL, 1, key);
1710  tuple = systable_getnext(scan);
1711  tuple = heap_copytuple(tuple);
1712  systable_endscan(scan);
1713 
1714  if (!HeapTupleIsValid(tuple))
1715  elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
1716 
1717  dbform = (Form_pg_database) GETSTRUCT(tuple);
1718 
1719  /*
1720  * As in vac_update_relstats(), we ordinarily don't want to let
1721  * datfrozenxid go backward; but if it's "in the future" then it must be
1722  * corrupt and it seems best to overwrite it.
1723  */
1724  if (dbform->datfrozenxid != newFrozenXid &&
1725  (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid) ||
1726  TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid)))
1727  {
1728  dbform->datfrozenxid = newFrozenXid;
1729  dirty = true;
1730  }
1731  else
1732  newFrozenXid = dbform->datfrozenxid;
1733 
1734  /* Ditto for datminmxid */
1735  if (dbform->datminmxid != newMinMulti &&
1736  (MultiXactIdPrecedes(dbform->datminmxid, newMinMulti) ||
1737  MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid)))
1738  {
1739  dbform->datminmxid = newMinMulti;
1740  dirty = true;
1741  }
1742  else
1743  newMinMulti = dbform->datminmxid;
1744 
1745  if (dirty)
1746  heap_inplace_update(relation, tuple);
1747 
1748  heap_freetuple(tuple);
1749  table_close(relation, RowExclusiveLock);
1750 
1751  /*
1752  * If we were able to advance datfrozenxid or datminmxid, see if we can
1753  * truncate pg_xact and/or pg_multixact. Also do it if the shared
1754  * XID-wrap-limit info is stale, since this action will update that too.
1755  */
1756  if (dirty || ForceTransactionIdLimitUpdate())
1757  vac_truncate_clog(newFrozenXid, newMinMulti,
1758  lastSaneFrozenXid, lastSaneMinMulti);
1759 }
TransactionId MultiXactId
Definition: c.h:649
uint32 TransactionId
Definition: c.h:639
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
Oid MyDatabaseId
Definition: globals.c:91
void heap_inplace_update(Relation relation, HeapTuple tuple)
Definition: heapam.c:5889
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:776
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void LockDatabaseFrozenIds(LOCKMODE lockmode)
Definition: lmgr.c:497
#define AccessShareLock
Definition: lockdefs.h:36
#define ExclusiveLock
Definition: lockdefs.h:42
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3234
MultiXactId GetOldestMultiXactId(void)
Definition: multixact.c:2585
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:722
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
FormData_pg_database * Form_pg_database
Definition: pg_database.h:96
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:1993
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
static TransactionId ReadNextTransactionId(void)
Definition: transam.h:315
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
static void vac_truncate_clog(TransactionId frozenXID, MultiXactId minMulti, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti)
Definition: vacuum.c:1780
bool ForceTransactionIdLimitUpdate(void)
Definition: varsup.c:517

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

1406 {
1407  Oid relid = RelationGetRelid(relation);
1408  Relation rd;
1409  HeapTuple ctup;
1410  Form_pg_class pgcform;
1411  bool dirty,
1412  futurexid,
1413  futuremxid;
1414  TransactionId oldfrozenxid;
1415  MultiXactId oldminmulti;
1416 
1417  rd = table_open(RelationRelationId, RowExclusiveLock);
1418 
1419  /* Fetch a copy of the tuple to scribble on */
1420  ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
1421  if (!HeapTupleIsValid(ctup))
1422  elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
1423  relid);
1424  pgcform = (Form_pg_class) GETSTRUCT(ctup);
1425 
1426  /* Apply statistical updates, if any, to copied tuple */
1427 
1428  dirty = false;
1429  if (pgcform->relpages != (int32) num_pages)
1430  {
1431  pgcform->relpages = (int32) num_pages;
1432  dirty = true;
1433  }
1434  if (pgcform->reltuples != (float4) num_tuples)
1435  {
1436  pgcform->reltuples = (float4) num_tuples;
1437  dirty = true;
1438  }
1439  if (pgcform->relallvisible != (int32) num_all_visible_pages)
1440  {
1441  pgcform->relallvisible = (int32) num_all_visible_pages;
1442  dirty = true;
1443  }
1444 
1445  /* Apply DDL updates, but not inside an outer transaction (see above) */
1446 
1447  if (!in_outer_xact)
1448  {
1449  /*
1450  * If we didn't find any indexes, reset relhasindex.
1451  */
1452  if (pgcform->relhasindex && !hasindex)
1453  {
1454  pgcform->relhasindex = false;
1455  dirty = true;
1456  }
1457 
1458  /* We also clear relhasrules and relhastriggers if needed */
1459  if (pgcform->relhasrules && relation->rd_rules == NULL)
1460  {
1461  pgcform->relhasrules = false;
1462  dirty = true;
1463  }
1464  if (pgcform->relhastriggers && relation->trigdesc == NULL)
1465  {
1466  pgcform->relhastriggers = false;
1467  dirty = true;
1468  }
1469  }
1470 
1471  /*
1472  * Update relfrozenxid, unless caller passed InvalidTransactionId
1473  * indicating it has no new data.
1474  *
1475  * Ordinarily, we don't let relfrozenxid go backwards. However, if the
1476  * stored relfrozenxid is "in the future" then it seems best to assume
1477  * it's corrupt, and overwrite with the oldest remaining XID in the table.
1478  * This should match vac_update_datfrozenxid() concerning what we consider
1479  * to be "in the future".
1480  */
1481  oldfrozenxid = pgcform->relfrozenxid;
1482  futurexid = false;
1483  if (frozenxid_updated)
1484  *frozenxid_updated = false;
1485  if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
1486  {
1487  bool update = false;
1488 
1489  if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
1490  update = true;
1491  else if (TransactionIdPrecedes(ReadNextTransactionId(), oldfrozenxid))
1492  futurexid = update = true;
1493 
1494  if (update)
1495  {
1496  pgcform->relfrozenxid = frozenxid;
1497  dirty = true;
1498  if (frozenxid_updated)
1499  *frozenxid_updated = true;
1500  }
1501  }
1502 
1503  /* Similarly for relminmxid */
1504  oldminmulti = pgcform->relminmxid;
1505  futuremxid = false;
1506  if (minmulti_updated)
1507  *minmulti_updated = false;
1508  if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
1509  {
1510  bool update = false;
1511 
1512  if (MultiXactIdPrecedes(oldminmulti, minmulti))
1513  update = true;
1514  else if (MultiXactIdPrecedes(ReadNextMultiXactId(), oldminmulti))
1515  futuremxid = update = true;
1516 
1517  if (update)
1518  {
1519  pgcform->relminmxid = minmulti;
1520  dirty = true;
1521  if (minmulti_updated)
1522  *minmulti_updated = true;
1523  }
1524  }
1525 
1526  /* If anything changed, write out the tuple. */
1527  if (dirty)
1528  heap_inplace_update(rd, ctup);
1529 
1531 
1532  if (futurexid)
1533  ereport(WARNING,
1535  errmsg_internal("overwrote invalid relfrozenxid value %u with new value %u for table \"%s\"",
1536  oldfrozenxid, frozenxid,
1537  RelationGetRelationName(relation))));
1538  if (futuremxid)
1539  ereport(WARNING,
1541  errmsg_internal("overwrote invalid relminmxid value %u with new value %u for table \"%s\"",
1542  oldminmulti, minmulti,
1543  RelationGetRelationName(relation))));
1544 }
signed int int32
Definition: c.h:481
float float4
Definition: c.h:616
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1159
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:41
TriggerDesc * trigdesc
Definition: rel.h:117
RuleLock * rd_rules
Definition: rel.h:115
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:86

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, 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,
MemoryContext  vac_context,
bool  isTopLevel 
)

Definition at line 479 of file vacuum.c.

481 {
482  static bool in_vacuum = false;
483 
484  const char *stmttype;
485  volatile bool in_outer_xact,
486  use_own_xacts;
487 
488  Assert(params != NULL);
489 
490  stmttype = (params->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
491 
492  /*
493  * We cannot run VACUUM inside a user transaction block; if we were inside
494  * a transaction, then our commit- and start-transaction-command calls
495  * would not have the intended effect! There are numerous other subtle
496  * dependencies on this, too.
497  *
498  * ANALYZE (without VACUUM) can run either way.
499  */
500  if (params->options & VACOPT_VACUUM)
501  {
502  PreventInTransactionBlock(isTopLevel, stmttype);
503  in_outer_xact = false;
504  }
505  else
506  in_outer_xact = IsInTransactionBlock(isTopLevel);
507 
508  /*
509  * Check for and disallow recursive calls. This could happen when VACUUM
510  * FULL or ANALYZE calls a hostile index expression that itself calls
511  * ANALYZE.
512  */
513  if (in_vacuum)
514  ereport(ERROR,
515  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
516  errmsg("%s cannot be executed from VACUUM or ANALYZE",
517  stmttype)));
518 
519  /*
520  * Build list of relation(s) to process, putting any new data in
521  * vac_context for safekeeping.
522  */
523  if (params->options & VACOPT_ONLY_DATABASE_STATS)
524  {
525  /* We don't process any tables in this case */
526  Assert(relations == NIL);
527  }
528  else if (relations != NIL)
529  {
530  List *newrels = NIL;
531  ListCell *lc;
532 
533  foreach(lc, relations)
534  {
536  List *sublist;
537  MemoryContext old_context;
538 
539  sublist = expand_vacuum_rel(vrel, vac_context, params->options);
540  old_context = MemoryContextSwitchTo(vac_context);
541  newrels = list_concat(newrels, sublist);
542  MemoryContextSwitchTo(old_context);
543  }
544  relations = newrels;
545  }
546  else
547  relations = get_all_vacuum_rels(vac_context, params->options);
548 
549  /*
550  * Decide whether we need to start/commit our own transactions.
551  *
552  * For VACUUM (with or without ANALYZE): always do so, so that we can
553  * release locks as soon as possible. (We could possibly use the outer
554  * transaction for a one-table VACUUM, but handling TOAST tables would be
555  * problematic.)
556  *
557  * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
558  * start/commit our own transactions. Also, there's no need to do so if
559  * only processing one relation. For multiple relations when not within a
560  * transaction block, and also in an autovacuum worker, use own
561  * transactions so we can release locks sooner.
562  */
563  if (params->options & VACOPT_VACUUM)
564  use_own_xacts = true;
565  else
566  {
567  Assert(params->options & VACOPT_ANALYZE);
569  use_own_xacts = true;
570  else if (in_outer_xact)
571  use_own_xacts = false;
572  else if (list_length(relations) > 1)
573  use_own_xacts = true;
574  else
575  use_own_xacts = false;
576  }
577 
578  /*
579  * vacuum_rel expects to be entered with no transaction active; it will
580  * start and commit its own transaction. But we are called by an SQL
581  * command, and so we are executing inside a transaction already. We
582  * commit the transaction started in PostgresMain() here, and start
583  * another one before exiting to match the commit waiting for us back in
584  * PostgresMain().
585  */
586  if (use_own_xacts)
587  {
588  Assert(!in_outer_xact);
589 
590  /* ActiveSnapshot is not set by autovacuum */
591  if (ActiveSnapshotSet())
593 
594  /* matches the StartTransaction in PostgresMain() */
596  }
597 
598  /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
599  PG_TRY();
600  {
601  ListCell *cur;
602 
603  in_vacuum = true;
604  VacuumFailsafeActive = false;
606  VacuumCostBalance = 0;
607  VacuumPageHit = 0;
608  VacuumPageMiss = 0;
609  VacuumPageDirty = 0;
612  VacuumActiveNWorkers = NULL;
613 
614  /*
615  * Loop to process each selected relation.
616  */
617  foreach(cur, relations)
618  {
620 
621  if (params->options & VACOPT_VACUUM)
622  {
623  if (!vacuum_rel(vrel->oid, vrel->relation, params, bstrategy))
624  continue;
625  }
626 
627  if (params->options & VACOPT_ANALYZE)
628  {
629  /*
630  * If using separate xacts, start one for analyze. Otherwise,
631  * we can use the outer transaction.
632  */
633  if (use_own_xacts)
634  {
636  /* functions in indexes may want a snapshot set */
638  }
639 
640  analyze_rel(vrel->oid, vrel->relation, params,
641  vrel->va_cols, in_outer_xact, bstrategy);
642 
643  if (use_own_xacts)
644  {
647  }
648  else
649  {
650  /*
651  * If we're not using separate xacts, better separate the
652  * ANALYZE actions with CCIs. This avoids trouble if user
653  * says "ANALYZE t, t".
654  */
656  }
657  }
658 
659  /*
660  * Ensure VacuumFailsafeActive has been reset before vacuuming the
661  * next relation.
662  */
663  VacuumFailsafeActive = false;
664  }
665  }
666  PG_FINALLY();
667  {
668  in_vacuum = false;
669  VacuumCostActive = false;
670  VacuumFailsafeActive = false;
671  VacuumCostBalance = 0;
672  }
673  PG_END_TRY();
674 
675  /*
676  * Finish up processing.
677  */
678  if (use_own_xacts)
679  {
680  /* here, we are not in a transaction */
681 
682  /*
683  * This matches the CommitTransaction waiting for us in
684  * PostgresMain().
685  */
687  }
688 
689  if ((params->options & VACOPT_VACUUM) &&
690  !(params->options & VACOPT_SKIP_DATABASE_STATS))
691  {
692  /*
693  * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
694  */
696  }
697 
698 }
void analyze_rel(Oid relid, RangeVar *relation, VacuumParams *params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
Definition: analyze.c:111
struct cursor * cur
Definition: ecpg.c:28
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define PG_FINALLY(...)
Definition: elog.h:387
bool VacuumCostActive
Definition: globals.c:159
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
#define AmAutoVacuumWorkerProcess()
Definition: miscadmin.h:372
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:216
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:648
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:782
void PopActiveSnapshot(void)
Definition: snapmgr.c:743
RangeVar * relation
Definition: parsenodes.h:3709
static List * expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, int options)
Definition: vacuum.c:871
static List * get_all_vacuum_rels(MemoryContext vac_context, int options)
Definition: vacuum.c:1011
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, BufferAccessStrategy bstrategy)
Definition: vacuum.c:1949
void vac_update_datfrozenxid(void)
Definition: vacuum.c:1566
bool VacuumFailsafeActive
Definition: vacuum.c:96
bool IsInTransactionBlock(bool isTopLevel)
Definition: xact.c:3688
void CommandCounterIncrement(void)
Definition: xact.c:1079
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3557
void StartTransactionCommand(void)
Definition: xact.c:2955
void CommitTransactionCommand(void)
Definition: xact.c:3053

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

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

◆ vacuum_delay_point()

void vacuum_delay_point ( void  )

Definition at line 2337 of file vacuum.c.

2338 {
2339  double msec = 0;
2340 
2341  /* Always check for interrupts */
2343 
2344  if (InterruptPending ||
2346  return;
2347 
2348  /*
2349  * Autovacuum workers should reload the configuration file if requested.
2350  * This allows changes to [autovacuum_]vacuum_cost_limit and
2351  * [autovacuum_]vacuum_cost_delay to take effect while a table is being
2352  * vacuumed or analyzed.
2353  */
2355  {
2356  ConfigReloadPending = false;
2359  }
2360 
2361  /*
2362  * If we disabled cost-based delays after reloading the config file,
2363  * return.
2364  */
2365  if (!VacuumCostActive)
2366  return;
2367 
2368  /*
2369  * For parallel vacuum, the delay is computed based on the shared cost
2370  * balance. See compute_parallel_delay.
2371  */
2372  if (VacuumSharedCostBalance != NULL)
2373  msec = compute_parallel_delay();
2376 
2377  /* Nap if appropriate */
2378  if (msec > 0)
2379  {
2380  if (msec > vacuum_cost_delay * 4)
2381  msec = vacuum_cost_delay * 4;
2382 
2383  pgstat_report_wait_start(WAIT_EVENT_VACUUM_DELAY);
2384  pg_usleep(msec * 1000);
2386 
2387  /*
2388  * We don't want to ignore postmaster death during very long vacuums
2389  * with vacuum_cost_delay configured. We can't use the usual
2390  * WaitLatch() approach here because we want microsecond-based sleep
2391  * durations above.
2392  */
2394  exit(1);
2395 
2396  VacuumCostBalance = 0;
2397 
2398  /*
2399  * Balance and update limit values for autovacuum workers. We must do
2400  * this periodically, as the number of workers across which we are
2401  * balancing the limit may have changed.
2402  *
2403  * TODO: There may be better criteria for determining when to do this
2404  * besides "check after napping".
2405  */
2407 
2408  /* Might have gotten an interrupt while sleeping */
2410  }
2411 }
void AutoVacuumUpdateCostLimit(void)
Definition: autovacuum.c:1701
volatile sig_atomic_t InterruptPending
Definition: globals.c:30
bool IsUnderPostmaster
Definition: globals.c:117
@ PGC_SIGHUP
Definition: guc.h:71
void ProcessConfigFile(GucContext context)
volatile sig_atomic_t ConfigReloadPending
Definition: interrupt.c:27
exit(1)
#define PostmasterIsAlive()
Definition: pmsignal.h:102
void pg_usleep(long microsec)
Definition: signal.c:53
double vacuum_cost_delay
Definition: vacuum.c:80
static double compute_parallel_delay(void)
Definition: vacuum.c:2436
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:88
static void pgstat_report_wait_end(void)
Definition: wait_event.h:104

References AmAutoVacuumWorkerProcess, AutoVacuumUpdateCostLimit(), CHECK_FOR_INTERRUPTS, compute_parallel_delay(), ConfigReloadPending, exit(), InterruptPending, IsUnderPostmaster, pg_usleep(), PGC_SIGHUP, pgstat_report_wait_end(), pgstat_report_wait_start(), PostmasterIsAlive, ProcessConfigFile(), vacuum_cost_delay, vacuum_cost_limit, VacuumCostActive, VacuumCostBalance, VacuumSharedCostBalance, and VacuumUpdateCosts().

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

◆ vacuum_get_cutoffs()

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

Definition at line 1073 of file vacuum.c.

1075 {
1076  int freeze_min_age,
1077  multixact_freeze_min_age,
1078  freeze_table_age,
1079  multixact_freeze_table_age,
1080  effective_multixact_freeze_max_age;
1081  TransactionId nextXID,
1082  safeOldestXmin,
1083  aggressiveXIDCutoff;
1084  MultiXactId nextMXID,
1085  safeOldestMxact,
1086  aggressiveMXIDCutoff;
1087 
1088  /* Use mutable copies of freeze age parameters */
1089  freeze_min_age = params->freeze_min_age;
1090  multixact_freeze_min_age = params->multixact_freeze_min_age;
1091  freeze_table_age = params->freeze_table_age;
1092  multixact_freeze_table_age = params->multixact_freeze_table_age;
1093 
1094  /* Set pg_class fields in cutoffs */
1095  cutoffs->relfrozenxid = rel->rd_rel->relfrozenxid;
1096  cutoffs->relminmxid = rel->rd_rel->relminmxid;
1097 
1098  /*
1099  * Acquire OldestXmin.
1100  *
1101  * We can always ignore processes running lazy vacuum. This is because we
1102  * use these values only for deciding which tuples we must keep in the
1103  * tables. Since lazy vacuum doesn't write its XID anywhere (usually no
1104  * XID assigned), it's safe to ignore it. In theory it could be
1105  * problematic to ignore lazy vacuums in a full vacuum, but keep in mind
1106  * that only one vacuum process can be working on a particular table at
1107  * any time, and that each vacuum is always an independent transaction.
1108  */
1110 
1112 
1113  /* Acquire OldestMxact */
1114  cutoffs->OldestMxact = GetOldestMultiXactId();
1116 
1117  /* Acquire next XID/next MXID values used to apply age-based settings */
1118  nextXID = ReadNextTransactionId();
1119  nextMXID = ReadNextMultiXactId();
1120 
1121  /*
1122  * Also compute the multixact age for which freezing is urgent. This is
1123  * normally autovacuum_multixact_freeze_max_age, but may be less if we are
1124  * short of multixact member space.
1125  */
1126  effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
1127 
1128  /*
1129  * Almost ready to set freeze output parameters; check if OldestXmin or
1130  * OldestMxact are held back to an unsafe degree before we start on that
1131  */
1132  safeOldestXmin = nextXID - autovacuum_freeze_max_age;
1133  if (!TransactionIdIsNormal(safeOldestXmin))
1134  safeOldestXmin = FirstNormalTransactionId;
1135  safeOldestMxact = nextMXID - effective_multixact_freeze_max_age;
1136  if (safeOldestMxact < FirstMultiXactId)
1137  safeOldestMxact = FirstMultiXactId;
1138  if (TransactionIdPrecedes(cutoffs->OldestXmin, safeOldestXmin))
1139  ereport(WARNING,
1140  (errmsg("cutoff for removing and freezing tuples is far in the past"),
1141  errhint("Close open transactions soon to avoid wraparound problems.\n"
1142  "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1143  if (MultiXactIdPrecedes(cutoffs->OldestMxact, safeOldestMxact))
1144  ereport(WARNING,
1145  (errmsg("cutoff for freezing multixacts is far in the past"),
1146  errhint("Close open transactions soon to avoid wraparound problems.\n"
1147  "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1148 
1149  /*
1150  * Determine the minimum freeze age to use: as specified by the caller, or
1151  * vacuum_freeze_min_age, but in any case not more than half
1152  * autovacuum_freeze_max_age, so that autovacuums to prevent XID
1153  * wraparound won't occur too frequently.
1154  */
1155  if (freeze_min_age < 0)
1156  freeze_min_age = vacuum_freeze_min_age;
1157  freeze_min_age = Min(freeze_min_age, autovacuum_freeze_max_age / 2);
1158  Assert(freeze_min_age >= 0);
1159 
1160  /* Compute FreezeLimit, being careful to generate a normal XID */
1161  cutoffs->FreezeLimit = nextXID - freeze_min_age;
1162  if (!TransactionIdIsNormal(cutoffs->FreezeLimit))
1164  /* FreezeLimit must always be <= OldestXmin */
1165  if (TransactionIdPrecedes(cutoffs->OldestXmin, cutoffs->FreezeLimit))
1166  cutoffs->FreezeLimit = cutoffs->OldestXmin;
1167 
1168  /*
1169  * Determine the minimum multixact freeze age to use: as specified by
1170  * caller, or vacuum_multixact_freeze_min_age, but in any case not more
1171  * than half effective_multixact_freeze_max_age, so that autovacuums to
1172  * prevent MultiXact wraparound won't occur too frequently.
1173  */
1174  if (multixact_freeze_min_age < 0)
1175  multixact_freeze_min_age = vacuum_multixact_freeze_min_age;
1176  multixact_freeze_min_age = Min(multixact_freeze_min_age,
1177  effective_multixact_freeze_max_age / 2);
1178  Assert(multixact_freeze_min_age >= 0);
1179 
1180  /* Compute MultiXactCutoff, being careful to generate a valid value */
1181  cutoffs->MultiXactCutoff = nextMXID - multixact_freeze_min_age;
1182  if (cutoffs->MultiXactCutoff < FirstMultiXactId)
1183  cutoffs->MultiXactCutoff = FirstMultiXactId;
1184  /* MultiXactCutoff must always be <= OldestMxact */
1185  if (MultiXactIdPrecedes(cutoffs->OldestMxact, cutoffs->MultiXactCutoff))
1186  cutoffs->MultiXactCutoff = cutoffs->OldestMxact;
1187 
1188  /*
1189  * Finally, figure out if caller needs to do an aggressive VACUUM or not.
1190  *
1191  * Determine the table freeze age to use: as specified by the caller, or
1192  * the value of the vacuum_freeze_table_age GUC, but in any case not more
1193  * than autovacuum_freeze_max_age * 0.95, so that if you have e.g nightly
1194  * VACUUM schedule, the nightly VACUUM gets a chance to freeze XIDs before
1195  * anti-wraparound autovacuum is launched.
1196  */
1197  if (freeze_table_age < 0)
1198  freeze_table_age = vacuum_freeze_table_age;
1199  freeze_table_age = Min(freeze_table_age, autovacuum_freeze_max_age * 0.95);
1200  Assert(freeze_table_age >= 0);
1201  aggressiveXIDCutoff = nextXID - freeze_table_age;
1202  if (!TransactionIdIsNormal(aggressiveXIDCutoff))
1203  aggressiveXIDCutoff = FirstNormalTransactionId;
1204  if (TransactionIdPrecedesOrEquals(rel->rd_rel->relfrozenxid,
1205  aggressiveXIDCutoff))
1206  return true;
1207 
1208  /*
1209  * Similar to the above, determine the table freeze age to use for
1210  * multixacts: as specified by the caller, or the value of the
1211  * vacuum_multixact_freeze_table_age GUC, but in any case not more than
1212  * effective_multixact_freeze_max_age * 0.95, so that if you have e.g.
1213  * nightly VACUUM schedule, the nightly VACUUM gets a chance to freeze
1214  * multixacts before anti-wraparound autovacuum is launched.
1215  */
1216  if (multixact_freeze_table_age < 0)
1217  multixact_freeze_table_age = vacuum_multixact_freeze_table_age;
1218  multixact_freeze_table_age =
1219  Min(multixact_freeze_table_age,
1220  effective_multixact_freeze_max_age * 0.95);
1221  Assert(multixact_freeze_table_age >= 0);
1222  aggressiveMXIDCutoff = nextMXID - multixact_freeze_table_age;
1223  if (aggressiveMXIDCutoff < FirstMultiXactId)
1224  aggressiveMXIDCutoff = FirstMultiXactId;
1225  if (MultiXactIdPrecedesOrEquals(rel->rd_rel->relminmxid,
1226  aggressiveMXIDCutoff))
1227  return true;
1228 
1229  /* Non-aggressive VACUUM */
1230  return false;
1231 }
int autovacuum_freeze_max_age
Definition: autovacuum.c:125
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3248
int MultiXactMemberFreezeThreshold(void)
Definition: multixact.c:2903
#define FirstMultiXactId
Definition: multixact.h:25
TransactionId FreezeLimit
Definition: vacuum.h:276
TransactionId OldestXmin
Definition: vacuum.h:266
TransactionId relfrozenxid
Definition: vacuum.h:250
MultiXactId relminmxid
Definition: vacuum.h:251
MultiXactId MultiXactCutoff
Definition: vacuum.h:277
MultiXactId OldestMxact
Definition: vacuum.h:267
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:299
#define FirstNormalTransactionId
Definition: transam.h:34
int vacuum_freeze_min_age
Definition: vacuum.c:67
int vacuum_multixact_freeze_table_age
Definition: vacuum.c:70
int vacuum_freeze_table_age
Definition: vacuum.c:68
int vacuum_multixact_freeze_min_age
Definition: vacuum.c:69

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

Referenced by copy_table_data(), and heap_vacuum_rel().

◆ vacuum_is_permitted_for_relation()

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

Definition at line 707 of file vacuum.c.

709 {
710  char *relname;
711 
713 
714  /*----------
715  * A role has privileges to vacuum or analyze the relation if any of the
716  * following are true:
717  * - the role owns the current database and the relation is not shared
718  * - the role has the MAINTAIN privilege on the relation
719  *----------
720  */
721  if ((object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) &&
722  !reltuple->relisshared) ||
724  return true;
725 
726  relname = NameStr(reltuple->relname);
727 
728  if ((options & VACOPT_VACUUM) != 0)
729  {
731  (errmsg("permission denied to vacuum \"%s\", skipping it",
732  relname)));
733 
734  /*
735  * For VACUUM ANALYZE, both logs could show up, but just generate
736  * information for VACUUM as that would be the first one to be
737  * processed.
738  */
739  return false;
740  }
741 
742  if ((options & VACOPT_ANALYZE) != 0)
744  (errmsg("permission denied to analyze \"%s\", skipping it",
745  relname)));
746 
747  return false;
748 }
@ ACLCHECK_OK
Definition: acl.h:183
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4130
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4079
#define NameStr(name)
Definition: c.h:733
Oid GetUserId(void)
Definition: miscinit.c:514
#define ACL_MAINTAIN
Definition: parsenodes.h:90
NameData relname
Definition: pg_class.h:38

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

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

◆ vacuum_open_relation()

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

Definition at line 759 of file vacuum.c.

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

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

Referenced by analyze_rel(), and vacuum_rel().

◆ vacuum_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( const struct VacuumCutoffs cutoffs)

Definition at line 1241 of file vacuum.c.

1242 {
1243  TransactionId relfrozenxid = cutoffs->relfrozenxid;
1244  MultiXactId relminmxid = cutoffs->relminmxid;
1245  TransactionId xid_skip_limit;
1246  MultiXactId multi_skip_limit;
1247  int skip_index_vacuum;
1248 
1249  Assert(TransactionIdIsNormal(relfrozenxid));
1250  Assert(MultiXactIdIsValid(relminmxid));
1251 
1252  /*
1253  * Determine the index skipping age to use. In any case no less than
1254  * autovacuum_freeze_max_age * 1.05.
1255  */
1256  skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
1257 
1258  xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
1259  if (!TransactionIdIsNormal(xid_skip_limit))
1260  xid_skip_limit = FirstNormalTransactionId;
1261 
1262  if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
1263  {
1264  /* The table's relfrozenxid is too old */
1265  return true;
1266  }
1267 
1268  /*
1269  * Similar to above, determine the index skipping age to use for
1270  * multixact. In any case no less than autovacuum_multixact_freeze_max_age *
1271  * 1.05.
1272  */
1273  skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
1275 
1276  multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
1277  if (multi_skip_limit < FirstMultiXactId)
1278  multi_skip_limit = FirstMultiXactId;
1279 
1280  if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
1281  {
1282  /* The table's relminmxid is too old */
1283  return true;
1284  }
1285 
1286  return false;
1287 }
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:126
int vacuum_multixact_failsafe_age
Definition: vacuum.c:72
int vacuum_failsafe_age
Definition: vacuum.c:71

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

Referenced by lazy_check_wraparound_failsafe().

◆ VacuumUpdateCosts()

void VacuumUpdateCosts ( void  )

Definition at line 1632 of file autovacuum.c.

1633 {
1634  if (MyWorkerInfo)
1635  {
1636  if (av_storage_param_cost_delay >= 0)
1638  else if (autovacuum_vac_cost_delay >= 0)
1640  else
1641  /* fall back to VacuumCostDelay */
1643 
1645  }
1646  else
1647  {
1648  /* Must be explicit VACUUM or ANALYZE */
1651  }
1652 
1653  /*
1654  * If configuration changes are allowed to impact VacuumCostActive, make
1655  * sure it is updated.
1656  */
1659  else if (vacuum_cost_delay > 0)
1660  VacuumCostActive = true;
1661  else
1662  {
1663  VacuumCostActive = false;
1664  VacuumCostBalance = 0;
1665  }
1666 
1667  /*
1668  * Since the cost logging requires a lock, avoid rendering the log message
1669  * in case we are using a message level where the log wouldn't be emitted.
1670  */
1672  {
1673  Oid dboid,
1674  tableoid;
1675 
1676  Assert(!LWLockHeldByMe(AutovacuumLock));
1677 
1678  LWLockAcquire(AutovacuumLock, LW_SHARED);
1679  dboid = MyWorkerInfo->wi_dboid;
1680  tableoid = MyWorkerInfo->wi_tableoid;
1681  LWLockRelease(AutovacuumLock);
1682 
1683  elog(DEBUG2,
1684  "Autovacuum VacuumUpdateCosts(db=%u, rel=%u, dobalance=%s, cost_limit=%d, cost_delay=%g active=%s failsafe=%s)",
1685  dboid, tableoid, pg_atomic_unlocked_test_flag(&MyWorkerInfo->wi_dobalance) ? "no" : "yes",
1687  vacuum_cost_delay > 0 ? "yes" : "no",
1688  VacuumFailsafeActive ? "yes" : "no");
1689  }
1690 }
static double av_storage_param_cost_delay
Definition: autovacuum.c:146
double autovacuum_vac_cost_delay
Definition: autovacuum.c:128
bool message_level_is_interesting(int elevel)
Definition: elog.c:276
double VacuumCostDelay
Definition: globals.c:152
bool LWLockHeldByMe(LWLock *lock)
Definition: lwlock.c:1897
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1172
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1785
@ LW_SHARED
Definition: lwlock.h:117

References Assert(), autovacuum_vac_cost_delay, AutoVacuumUpdateCostLimit(), av_storage_param_cost_delay, DEBUG2, elog, LW_SHARED, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), message_level_is_interesting(), MyWorkerInfo, pg_atomic_unlocked_test_flag(), vacuum_cost_delay, vacuum_cost_limit, VacuumCostActive, VacuumCostBalance, VacuumCostDelay, VacuumCostLimit, VacuumFailsafeActive, WorkerInfoData::wi_dboid, WorkerInfoData::wi_dobalance, and WorkerInfoData::wi_tableoid.

Referenced by do_autovacuum(), parallel_vacuum_main(), vacuum(), and vacuum_delay_point().

Variable Documentation

◆ default_statistics_target

PGDLLIMPORT int default_statistics_target
extern

◆ vacuum_cost_delay

PGDLLIMPORT double vacuum_cost_delay
extern

◆ vacuum_cost_limit

PGDLLIMPORT int vacuum_cost_limit
extern

◆ vacuum_failsafe_age

PGDLLIMPORT int vacuum_failsafe_age
extern

Definition at line 71 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

PGDLLIMPORT int vacuum_freeze_min_age
extern

Definition at line 67 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_freeze_table_age

PGDLLIMPORT int vacuum_freeze_table_age
extern

Definition at line 68 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_failsafe_age

PGDLLIMPORT int vacuum_multixact_failsafe_age
extern

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

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_multixact_freeze_table_age

PGDLLIMPORT int vacuum_multixact_freeze_table_age
extern

Definition at line 70 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

PGDLLIMPORT int VacuumCostBalanceLocal
extern

◆ VacuumFailsafeActive

◆ VacuumSharedCostBalance