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_relation_owner (Oid relid, Form_pg_class reltuple, bits32 options)
 
Relation vacuum_open_relation (Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
 
IndexBulkDeleteResultvac_bulkdel_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat, VacDeadItems *dead_items)
 
IndexBulkDeleteResultvac_cleanup_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat)
 
Size vac_max_items_to_alloc_size (int max_items)
 
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 308 of file vacuum.h.

◆ MAXDEADITEMS

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

Definition at line 291 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 121 of file analyze.c.

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

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

Referenced by vacuum().

◆ anl_get_next_S()

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

Definition at line 296 of file sampling.c.

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

◆ anl_init_selection_state()

double anl_init_selection_state ( int  n)

Definition at line 281 of file sampling.c.

282 {
283  /* initialize if first time through */
285  {
287  &oldrs.randstate);
288  oldrs_initialized = true;
289  }
290 
291  /* Initial value of W (for use when Algorithm Z is first applied) */
292  return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
293 }
#define unlikely(x)
Definition: c.h:300
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 1855 of file autovacuum.c.

1856 {
1857  if (!MyWorkerInfo)
1858  return;
1859 
1860  /*
1861  * note: in cost_limit, zero also means use value from elsewhere, because
1862  * zero is not a valid value.
1863  */
1864 
1867  else
1868  {
1869  int nworkers_for_balance;
1870 
1871  if (autovacuum_vac_cost_limit > 0)
1873  else
1875 
1876  /* Only balance limit if no cost-related storage parameters specified */
1878  return;
1879 
1881 
1882  nworkers_for_balance = pg_atomic_read_u32(&AutoVacuumShmem->av_nworkersForBalance);
1883 
1884  /* There is at least 1 autovac worker (this worker) */
1885  if (nworkers_for_balance <= 0)
1886  elog(ERROR, "nworkers_for_balance must be > 0");
1887 
1888  vacuum_cost_limit = Max(vacuum_cost_limit / nworkers_for_balance, 1);
1889  }
1890 }
static bool pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr)
Definition: atomics.h:193
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:236
int autovacuum_vac_cost_limit
Definition: autovacuum.c:130
static int av_storage_param_cost_limit
Definition: autovacuum.c:152
static AutoVacuumShmemStruct * AutoVacuumShmem
Definition: autovacuum.c:303
static WorkerInfo MyWorkerInfo
Definition: autovacuum.c:313
#define Max(x, y)
Definition: c.h:987
#define ERROR
Definition: elog.h:39
int VacuumCostLimit
Definition: globals.c:148
Assert(fmt[strlen(fmt) - 1] !='\n')
pg_atomic_uint32 av_nworkersForBalance
Definition: autovacuum.c:300
pg_atomic_flag wi_dobalance
Definition: autovacuum.c:238
int vacuum_cost_limit
Definition: vacuum.c:83

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

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

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, 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::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:61
double reltuples
bool estimated_count
static void parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scans, bool vacuum)

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

Referenced by lazy_vacuum_all_indexes().

◆ parallel_vacuum_cleanup_all_indexes()

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

Definition at line 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:928
int i
Definition: isn.c:73
void pfree(void *pointer)
Definition: mcxt.c:1456
void * palloc0(Size size)
Definition: mcxt.c:1257
bool istat_updated
IndexBulkDeleteResult istat
ParallelContext * pcxt
PVIndStats * indstats
void ExitParallelMode(void)
Definition: xact.c:1049

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

Referenced by dead_items_cleanup().

◆ parallel_vacuum_get_dead_items()

VacDeadItems* parallel_vacuum_get_dead_items ( ParallelVacuumState pvs)

Definition at line 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:218
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:204
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:166
#define Min(x, y)
Definition: c.h:993
unsigned char uint8
Definition: c.h:493
#define MemSet(start, val, len)
Definition: c.h:1009
size_t Size
Definition: c.h:594
int GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
Definition: freelist.c:624
int maintenance_work_mem
Definition: globals.c:127
const char * debug_query_string
Definition: postgres.c:85
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition: shm_toc.c:171
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition: shm_toc.c:88
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
#define shm_toc_estimate_keys(e, cnt)
Definition: shm_toc.h:53
Size mul_size(Size s1, Size s2)
Definition: shmem.c:519
bool amusemaintenanceworkmem
Definition: amapi.h:246
uint8 amparallelvacuumoptions
Definition: amapi.h:250
pg_atomic_uint32 cost_balance
int maintenance_work_mem_worker
pg_atomic_uint32 active_nworkers
int ring_nbuffers
pg_atomic_uint32 idx
shm_toc_estimator estimator
Definition: parallel.h:42
shm_toc * toc
Definition: parallel.h:45
BufferAccessStrategy bstrategy
BufferUsage * buffer_usage
struct IndexAmRoutine * rd_indam
Definition: rel.h:205
ItemPointerData items[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuum.h:288
int max_items
Definition: vacuum.h:284
int num_items
Definition: vacuum.h:285
Size vac_max_items_to_alloc_size(int max_items)
Definition: vacuum.c:2522
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:62
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:47
#define VACUUM_OPTION_MAX_VALID_VALUE
Definition: vacuum.h:65
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:54
static int parallel_vacuum_compute_workers(Relation *indrels, int nindexes, int nrequested, bool *will_parallel_vacuum)
#define PARALLEL_VACUUM_KEY_INDEX_STATS
#define PARALLEL_VACUUM_KEY_QUERY_TEXT
#define PARALLEL_VACUUM_KEY_BUFFER_USAGE
#define PARALLEL_VACUUM_KEY_SHARED
#define PARALLEL_VACUUM_KEY_WAL_USAGE
struct PVShared PVShared
#define PARALLEL_VACUUM_KEY_DEAD_ITEMS
void EnterParallelMode(void)
Definition: xact.c:1036

References PVShared::active_nworkers, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::amusemaintenanceworkmem, Assert(), ParallelVacuumState::bstrategy, ParallelVacuumState::buffer_usage, PVShared::cost_balance, CreateParallelContext(), ParallelVacuumState::dead_items, debug_query_string, PVShared::elevel, EnterParallelMode(), ParallelContext::estimator, 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:1786
int ParallelWorkerNumber
Definition: parallel.c:114
void pgstat_report_activity(BackendState state, const char *cmd_str)
@ STATE_RUNNING
ErrorContextCallback * error_context_stack
Definition: elog.c:95
#define DEBUG1
Definition: elog.h:30
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:639
int64 VacuumPageHit
Definition: globals.c:151
int64 VacuumPageMiss
Definition: globals.c:152
int64 VacuumPageDirty
Definition: globals.c:153
int VacuumCostBalance
Definition: globals.c:155
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:3348
char * pstrdup(const char *in)
Definition: mcxt.c:1644
#define PROC_IN_VACUUM
Definition: proc.h:57
#define RelationGetNamespace(relation)
Definition: rel.h:545
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition: shm_toc.c:232
PGPROC * MyProc
Definition: proc.c:66
struct ErrorContextCallback * previous
Definition: elog.h:295
void(* callback)(void *arg)
Definition: elog.h:296
uint8 statusFlags
Definition: proc.h:233
PVIndVacStatus status
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:105
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2258
int VacuumCostBalanceLocal
Definition: vacuum.c:106
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2301
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:104
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 1876 of file analyze.c.

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

2478 {
2479  /* Do bulk deletion */
2480  istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2481  (void *) dead_items);
2482 
2483  ereport(ivinfo->message_level,
2484  (errmsg("scanned index \"%s\" to remove %d row versions",
2485  RelationGetRelationName(ivinfo->index),
2486  dead_items->num_items)));
2487 
2488  return istat;
2489 }
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:699
Relation index
Definition: genam.h:46
int message_level
Definition: genam.h:51
static bool vac_tid_reaped(ItemPointer itemptr, void *state)
Definition: vacuum.c:2537

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

2498 {
2499  istat = index_vacuum_cleanup(ivinfo, istat);
2500 
2501  if (istat)
2502  ereport(ivinfo->message_level,
2503  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2504  RelationGetRelationName(ivinfo->index),
2505  istat->num_index_tuples,
2506  istat->num_pages),
2507  errdetail("%.0f index row versions were removed.\n"
2508  "%u index pages were newly deleted.\n"
2509  "%u index pages are currently deleted, of which %u are currently reusable.",
2510  istat->tuples_removed,
2511  istat->pages_newly_deleted,
2512  istat->pages_deleted, istat->pages_free)));
2513 
2514  return istat;
2515 }
int errdetail(const char *fmt,...)
Definition: elog.c:1202
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:720
BlockNumber pages_deleted
Definition: genam.h: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 2301 of file vacuum.c.

2302 {
2303  if (Irel == NULL)
2304  return;
2305 
2306  while (nindexes--)
2307  {
2308  Relation ind = Irel[nindexes];
2309 
2310  index_close(ind, lockmode);
2311  }
2312  pfree(Irel);
2313 }
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158

References index_close(), and pfree().

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

◆ vac_estimate_reltuples()

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

Definition at line 1305 of file vacuum.c.

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

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

2523 {
2524  Assert(max_items <= MAXDEADITEMS(MaxAllocSize));
2525 
2526  return offsetof(VacDeadItems, items) + sizeof(ItemPointerData) * max_items;
2527 }
struct ItemPointerData ItemPointerData
#define MaxAllocSize
Definition: memutils.h:40
#define MAXDEADITEMS(avail_mem)
Definition: vacuum.h:291

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

2260 {
2261  List *indexoidlist;
2262  ListCell *indexoidscan;
2263  int i;
2264 
2265  Assert(lockmode != NoLock);
2266 
2267  indexoidlist = RelationGetIndexList(relation);
2268 
2269  /* allocate enough memory for all indexes */
2270  i = list_length(indexoidlist);
2271 
2272  if (i > 0)
2273  *Irel = (Relation *) palloc(i * sizeof(Relation));
2274  else
2275  *Irel = NULL;
2276 
2277  /* collect just the ready indexes */
2278  i = 0;
2279  foreach(indexoidscan, indexoidlist)
2280  {
2281  Oid indexoid = lfirst_oid(indexoidscan);
2282  Relation indrel;
2283 
2284  indrel = index_open(indexoid, lockmode);
2285  if (indrel->rd_index->indisready)
2286  (*Irel)[i++] = indrel;
2287  else
2288  index_close(indrel, lockmode);
2289  }
2290 
2291  *nindexes = i;
2292 
2293  list_free(indexoidlist);
2294 }
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
void list_free(List *list)
Definition: list.c:1545
static int list_length(const List *l)
Definition: pg_list.h:152
#define lfirst_oid(lc)
Definition: pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4740
Definition: pg_list.h:54
Form_pg_index rd_index
Definition: rel.h:191

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

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

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

Referenced by do_autovacuum(), and vacuum().

◆ vac_update_relstats()

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

Definition at line 1401 of file vacuum.c.

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

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

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

◆ vacuum()

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

Definition at line 478 of file vacuum.c.

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

References ActiveSnapshotSet(), analyze_rel(), Assert(), CommandCounterIncrement(), CommitTransactionCommand(), cur, ereport, errcode(), errmsg(), ERROR, expand_vacuum_rel(), get_all_vacuum_rels(), GetTransactionSnapshot(), IsAutoVacuumWorkerProcess(), 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 2322 of file vacuum.c.

2323 {
2324  double msec = 0;
2325 
2326  /* Always check for interrupts */
2328 
2329  if (InterruptPending ||
2331  return;
2332 
2333  /*
2334  * Autovacuum workers should reload the configuration file if requested.
2335  * This allows changes to [autovacuum_]vacuum_cost_limit and
2336  * [autovacuum_]vacuum_cost_delay to take effect while a table is being
2337  * vacuumed or analyzed.
2338  */
2340  {
2341  ConfigReloadPending = false;
2344  }
2345 
2346  /*
2347  * If we disabled cost-based delays after reloading the config file,
2348  * return.
2349  */
2350  if (!VacuumCostActive)
2351  return;
2352 
2353  /*
2354  * For parallel vacuum, the delay is computed based on the shared cost
2355  * balance. See compute_parallel_delay.
2356  */
2357  if (VacuumSharedCostBalance != NULL)
2358  msec = compute_parallel_delay();
2361 
2362  /* Nap if appropriate */
2363  if (msec > 0)
2364  {
2365  if (msec > vacuum_cost_delay * 4)
2366  msec = vacuum_cost_delay * 4;
2367 
2368  pgstat_report_wait_start(WAIT_EVENT_VACUUM_DELAY);
2369  pg_usleep(msec * 1000);
2371 
2372  /*
2373  * We don't want to ignore postmaster death during very long vacuums
2374  * with vacuum_cost_delay configured. We can't use the usual
2375  * WaitLatch() approach here because we want microsecond-based sleep
2376  * durations above.
2377  */
2379  exit(1);
2380 
2381  VacuumCostBalance = 0;
2382 
2383  /*
2384  * Balance and update limit values for autovacuum workers. We must do
2385  * this periodically, as the number of workers across which we are
2386  * balancing the limit may have changed.
2387  *
2388  * TODO: There may be better criteria for determining when to do this
2389  * besides "check after napping".
2390  */
2392 
2393  /* Might have gotten an interrupt while sleeping */
2395  }
2396 }
void AutoVacuumUpdateCostLimit(void)
Definition: autovacuum.c:1855
volatile sig_atomic_t InterruptPending
Definition: globals.c:30
bool IsUnderPostmaster
Definition: globals.c:113
@ 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:82
static double compute_parallel_delay(void)
Definition: vacuum.c:2421
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 AutoVacuumUpdateCostLimit(), CHECK_FOR_INTERRUPTS, compute_parallel_delay(), ConfigReloadPending, exit(), InterruptPending, IsAutoVacuumWorkerProcess(), 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_scan_skip(), lazy_vacuum_heap_rel(), spgprocesspending(), and spgvacuumpage().

◆ vacuum_get_cutoffs()

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

Definition at line 1075 of file vacuum.c.

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

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

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

Definition at line 707 of file vacuum.c.

708 {
709  char *relname;
710 
712 
713  /*
714  * Check permissions.
715  *
716  * We allow the user to vacuum or analyze a table if he is superuser, the
717  * table owner, or the database owner (but in the latter case, only if
718  * it's not a shared relation). object_ownercheck includes the superuser
719  * case.
720  *
721  * Note we choose to treat permissions failure as a WARNING and keep
722  * trying to vacuum or analyze the rest of the DB --- is this appropriate?
723  */
724  if (object_ownercheck(RelationRelationId, relid, GetUserId()) ||
725  (object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) && !reltuple->relisshared))
726  return true;
727 
728  relname = NameStr(reltuple->relname);
729 
730  if ((options & VACOPT_VACUUM) != 0)
731  {
733  (errmsg("permission denied to vacuum \"%s\", skipping it",
734  relname)));
735 
736  /*
737  * For VACUUM ANALYZE, both logs could show up, but just generate
738  * information for VACUUM as that would be the first one to be
739  * processed.
740  */
741  return false;
742  }
743 
744  if ((options & VACOPT_ANALYZE) != 0)
746  (errmsg("permission denied to analyze \"%s\", skipping it",
747  relname)));
748 
749  return false;
750 }
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3961
#define NameStr(name)
Definition: c.h:735
Oid GetUserId(void)
Definition: miscinit.c:509
NameData relname
Definition: pg_class.h:38

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

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

◆ vacuum_open_relation()

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

Definition at line 761 of file vacuum.c.

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

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

Referenced by analyze_rel(), and vacuum_rel().

◆ vacuum_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( const struct VacuumCutoffs cutoffs)

Definition at line 1243 of file vacuum.c.

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

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 1786 of file autovacuum.c.

1787 {
1788  if (MyWorkerInfo)
1789  {
1790  if (av_storage_param_cost_delay >= 0)
1792  else if (autovacuum_vac_cost_delay >= 0)
1794  else
1795  /* fall back to VacuumCostDelay */
1797 
1799  }
1800  else
1801  {
1802  /* Must be explicit VACUUM or ANALYZE */
1805  }
1806 
1807  /*
1808  * If configuration changes are allowed to impact VacuumCostActive, make
1809  * sure it is updated.
1810  */
1813  else if (vacuum_cost_delay > 0)
1814  VacuumCostActive = true;
1815  else
1816  {
1817  VacuumCostActive = false;
1818  VacuumCostBalance = 0;
1819  }
1820 
1821  /*
1822  * Since the cost logging requires a lock, avoid rendering the log message
1823  * in case we are using a message level where the log wouldn't be emitted.
1824  */
1826  {
1827  Oid dboid,
1828  tableoid;
1829 
1830  Assert(!LWLockHeldByMe(AutovacuumLock));
1831 
1832  LWLockAcquire(AutovacuumLock, LW_SHARED);
1833  dboid = MyWorkerInfo->wi_dboid;
1834  tableoid = MyWorkerInfo->wi_tableoid;
1835  LWLockRelease(AutovacuumLock);
1836 
1837  elog(DEBUG2,
1838  "Autovacuum VacuumUpdateCosts(db=%u, rel=%u, dobalance=%s, cost_limit=%d, cost_delay=%g active=%s failsafe=%s)",
1839  dboid, tableoid, pg_atomic_unlocked_test_flag(&MyWorkerInfo->wi_dobalance) ? "no" : "yes",
1841  vacuum_cost_delay > 0 ? "yes" : "no",
1842  VacuumFailsafeActive ? "yes" : "no");
1843  }
1844 }
static double av_storage_param_cost_delay
Definition: autovacuum.c:151
double autovacuum_vac_cost_delay
Definition: autovacuum.c:129
bool message_level_is_interesting(int elevel)
Definition: elog.c:277
double VacuumCostDelay
Definition: globals.c:149
bool LWLockHeldByMe(LWLock *lock)
Definition: lwlock.c:1920
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1195
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
@ 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 73 of file vacuum.c.

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

PGDLLIMPORT int vacuum_freeze_min_age
extern

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

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

PGDLLIMPORT int VacuumCostBalanceLocal
extern

◆ VacuumFailsafeActive

◆ VacuumSharedCostBalance