Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

PostgreSQL Source Code git master
vacuum.h File Reference
#include "access/htup.h"
#include "access/genam.h"
#include "access/parallel.h"
#include "access/tidstore.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  VacDeadItemsInfo
 

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 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 VacDeadItemsInfo VacDeadItemsInfo
 

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, const 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, BlockNumber num_all_frozen_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 (bool is_analyze)
 
bool vacuum_is_permitted_for_relation (Oid relid, Form_pg_class reltuple, bits32 options)
 
Relation vacuum_open_relation (Oid relid, RangeVar *relation, bits32 options, bool verbose, LOCKMODE lmode)
 
IndexBulkDeleteResultvac_bulkdel_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat, TidStore *dead_items, VacDeadItemsInfo *dead_items_info)
 
IndexBulkDeleteResultvac_cleanup_one_index (IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat)
 
void AutoVacuumUpdateCostLimit (void)
 
void VacuumUpdateCosts (void)
 
ParallelVacuumStateparallel_vacuum_init (Relation rel, Relation *indrels, int nindexes, int nrequested_workers, int vac_work_mem, int elevel, BufferAccessStrategy bstrategy)
 
void parallel_vacuum_end (ParallelVacuumState *pvs, IndexBulkDeleteResult **istats)
 
TidStoreparallel_vacuum_get_dead_items (ParallelVacuumState *pvs, VacDeadItemsInfo **dead_items_info_p)
 
void parallel_vacuum_reset_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, const 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 bool track_cost_delay_timing
 
PGDLLIMPORT bool vacuum_truncate
 
PGDLLIMPORT double vacuum_max_eager_freeze_failure_rate
 
PGDLLIMPORT pg_atomic_uint32VacuumSharedCostBalance
 
PGDLLIMPORT pg_atomic_uint32VacuumActiveNWorkers
 
PGDLLIMPORT int VacuumCostBalanceLocal
 
PGDLLIMPORT bool VacuumFailsafeActive
 
PGDLLIMPORT double vacuum_cost_delay
 
PGDLLIMPORT int vacuum_cost_limit
 
PGDLLIMPORT int64 parallel_vacuum_worker_delay_ns
 

Macro Definition Documentation

◆ MAX_STATISTICS_TARGET

#define MAX_STATISTICS_TARGET   10000

Definition at line 324 of file vacuum.h.

◆ VACOPT_ANALYZE

#define VACOPT_ANALYZE   0x02 /* do ANALYZE */

Definition at line 181 of file vacuum.h.

◆ VACOPT_DISABLE_PAGE_SKIPPING

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

Definition at line 188 of file vacuum.h.

◆ VACOPT_FREEZE

#define VACOPT_FREEZE   0x08 /* FREEZE option */

Definition at line 183 of file vacuum.h.

◆ VACOPT_FULL

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

Definition at line 184 of file vacuum.h.

◆ VACOPT_ONLY_DATABASE_STATS

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

Definition at line 190 of file vacuum.h.

◆ VACOPT_PROCESS_MAIN

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

Definition at line 186 of file vacuum.h.

◆ VACOPT_PROCESS_TOAST

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

Definition at line 187 of file vacuum.h.

◆ VACOPT_SKIP_DATABASE_STATS

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

Definition at line 189 of file vacuum.h.

◆ VACOPT_SKIP_LOCKED

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

Definition at line 185 of file vacuum.h.

◆ VACOPT_VACUUM

#define VACOPT_VACUUM   0x01 /* do VACUUM */

Definition at line 180 of file vacuum.h.

◆ VACOPT_VERBOSE

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

Definition at line 182 of file vacuum.h.

◆ VACUUM_OPTION_MAX_VALID_VALUE

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

Definition at line 66 of file vacuum.h.

◆ VACUUM_OPTION_NO_PARALLEL

#define VACUUM_OPTION_NO_PARALLEL   0

Definition at line 42 of file vacuum.h.

◆ VACUUM_OPTION_PARALLEL_BULKDEL

#define VACUUM_OPTION_PARALLEL_BULKDEL   (1 << 0)

Definition at line 48 of file vacuum.h.

◆ VACUUM_OPTION_PARALLEL_CLEANUP

#define VACUUM_OPTION_PARALLEL_CLEANUP   (1 << 2)

Definition at line 63 of file vacuum.h.

◆ VACUUM_OPTION_PARALLEL_COND_CLEANUP

#define VACUUM_OPTION_PARALLEL_COND_CLEANUP   (1 << 1)

Definition at line 55 of file vacuum.h.

Typedef Documentation

◆ AnalyzeAttrComputeStatsFunc

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

Definition at line 111 of file vacuum.h.

◆ AnalyzeAttrFetchFunc

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

Definition at line 108 of file vacuum.h.

◆ ParallelVacuumState

Definition at line 69 of file vacuum.h.

◆ VacAttrStats

typedef struct VacAttrStats VacAttrStats

◆ VacAttrStatsP

typedef struct VacAttrStats* VacAttrStatsP

Definition at line 106 of file vacuum.h.

◆ VacDeadItemsInfo

◆ VacOptValue

typedef enum VacOptValue VacOptValue

◆ VacuumParams

typedef struct VacuumParams VacuumParams

Enumeration Type Documentation

◆ VacOptValue

Enumerator
VACOPTVALUE_UNSPECIFIED 
VACOPTVALUE_AUTO 
VACOPTVALUE_DISABLED 
VACOPTVALUE_ENABLED 

Definition at line 200 of file vacuum.h.

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

Function Documentation

◆ analyze_rel()

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

Definition at line 108 of file analyze.c.

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

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

Referenced by vacuum().

◆ anl_get_next_S()

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

Definition at line 296 of file sampling.c.

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

◆ anl_init_selection_state()

double anl_init_selection_state ( int  n)

Definition at line 281 of file sampling.c.

282{
283 /* initialize if first time through */
285 {
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:402
uint32 pg_prng_uint32(pg_prng_state *state)
Definition: pg_prng.c:227
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 {
273 oldrs_initialized = true;
274 }
275
276 /* and compute a random fraction */
278}

◆ AutoVacuumUpdateCostLimit()

void AutoVacuumUpdateCostLimit ( void  )

Definition at line 1736 of file autovacuum.c.

1737{
1738 if (!MyWorkerInfo)
1739 return;
1740
1741 /*
1742 * note: in cost_limit, zero also means use value from elsewhere, because
1743 * zero is not a valid value.
1744 */
1745
1748 else
1749 {
1750 int nworkers_for_balance;
1751
1754 else
1756
1757 /* Only balance limit if no cost-related storage parameters specified */
1759 return;
1760
1762
1764
1765 /* There is at least 1 autovac worker (this worker) */
1766 if (nworkers_for_balance <= 0)
1767 elog(ERROR, "nworkers_for_balance must be > 0");
1768
1769 vacuum_cost_limit = Max(vacuum_cost_limit / nworkers_for_balance, 1);
1770 }
1771}
static bool pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr)
Definition: atomics.h:194
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:237
int autovacuum_vac_cost_limit
Definition: autovacuum.c:133
static int av_storage_param_cost_limit
Definition: autovacuum.c:151
static AutoVacuumShmemStruct * AutoVacuumShmem
Definition: autovacuum.c:303
static WorkerInfo MyWorkerInfo
Definition: autovacuum.c:323
#define Max(x, y)
Definition: c.h:997
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
int VacuumCostLimit
Definition: globals.c:154
Assert(PointerIsAligned(start, uint64))
pg_atomic_uint32 av_nworkersForBalance
Definition: autovacuum.c:300
pg_atomic_flag wi_dobalance
Definition: autovacuum.c:237
int vacuum_cost_limit
Definition: vacuum.c:91

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

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

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

502{
504
505 /*
506 * We can only provide an approximate value of num_heap_tuples, at least
507 * for now.
508 */
509 pvs->shared->reltuples = num_table_tuples;
510 pvs->shared->estimated_count = true;
511
512 parallel_vacuum_process_all_indexes(pvs, num_index_scans, true);
513}
#define IsParallelWorker()
Definition: parallel.h:60
double reltuples
bool estimated_count
static void parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scans, bool vacuum)

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

Referenced by lazy_vacuum_all_indexes().

◆ parallel_vacuum_cleanup_all_indexes()

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

Definition at line 519 of file vacuumparallel.c.

521{
523
524 /*
525 * We can provide a better estimate of total number of surviving tuples
526 * (we assume indexes are more interested in that than in the number of
527 * nominally live tuples).
528 */
529 pvs->shared->reltuples = num_table_tuples;
530 pvs->shared->estimated_count = estimated_count;
531
532 parallel_vacuum_process_all_indexes(pvs, num_index_scans, false);
533}

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

437{
439
440 /* Copy the updated statistics */
441 for (int i = 0; i < pvs->nindexes; i++)
442 {
443 PVIndStats *indstats = &(pvs->indstats[i]);
444
445 if (indstats->istat_updated)
446 {
448 memcpy(istats[i], &indstats->istat, sizeof(IndexBulkDeleteResult));
449 }
450 else
451 istats[i] = NULL;
452 }
453
455
458
460 pfree(pvs);
461}
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:950
int i
Definition: isn.c:77
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
bool istat_updated
IndexBulkDeleteResult istat
ParallelContext * pcxt
PVIndStats * indstats
void TidStoreDestroy(TidStore *ts)
Definition: tidstore.c:317
void ExitParallelMode(void)
Definition: xact.c:1064

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

Referenced by dead_items_cleanup().

◆ parallel_vacuum_get_dead_items()

TidStore * parallel_vacuum_get_dead_items ( ParallelVacuumState pvs,
VacDeadItemsInfo **  dead_items_info_p 
)

Definition at line 467 of file vacuumparallel.c.

468{
469 *dead_items_info_p = &(pvs->shared->dead_items_info);
470 return pvs->dead_items;
471}
VacDeadItemsInfo dead_items_info

References ParallelVacuumState::dead_items, PVShared::dead_items_info, and ParallelVacuumState::shared.

Referenced by dead_items_alloc().

◆ parallel_vacuum_init()

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

Definition at line 243 of file vacuumparallel.c.

246{
248 ParallelContext *pcxt;
249 PVShared *shared;
250 TidStore *dead_items;
251 PVIndStats *indstats;
252 BufferUsage *buffer_usage;
253 WalUsage *wal_usage;
254 bool *will_parallel_vacuum;
255 Size est_indstats_len;
256 Size est_shared_len;
257 int nindexes_mwm = 0;
258 int parallel_workers = 0;
259 int querylen;
260
261 /*
262 * A parallel vacuum must be requested and there must be indexes on the
263 * relation
264 */
265 Assert(nrequested_workers >= 0);
266 Assert(nindexes > 0);
267
268 /*
269 * Compute the number of parallel vacuum workers to launch
270 */
271 will_parallel_vacuum = (bool *) palloc0(sizeof(bool) * nindexes);
272 parallel_workers = parallel_vacuum_compute_workers(indrels, nindexes,
273 nrequested_workers,
274 will_parallel_vacuum);
275 if (parallel_workers <= 0)
276 {
277 /* Can't perform vacuum in parallel -- return NULL */
278 pfree(will_parallel_vacuum);
279 return NULL;
280 }
281
283 pvs->indrels = indrels;
284 pvs->nindexes = nindexes;
285 pvs->will_parallel_vacuum = will_parallel_vacuum;
286 pvs->bstrategy = bstrategy;
287 pvs->heaprel = rel;
288
290 pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
291 parallel_workers);
292 Assert(pcxt->nworkers > 0);
293 pvs->pcxt = pcxt;
294
295 /* Estimate size for index vacuum stats -- PARALLEL_VACUUM_KEY_INDEX_STATS */
296 est_indstats_len = mul_size(sizeof(PVIndStats), nindexes);
297 shm_toc_estimate_chunk(&pcxt->estimator, est_indstats_len);
299
300 /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
301 est_shared_len = sizeof(PVShared);
302 shm_toc_estimate_chunk(&pcxt->estimator, est_shared_len);
304
305 /*
306 * Estimate space for BufferUsage and WalUsage --
307 * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
308 *
309 * If there are no extensions loaded that care, we could skip this. We
310 * have no way of knowing whether anyone's looking at pgBufferUsage or
311 * pgWalUsage, so do it unconditionally.
312 */
314 mul_size(sizeof(BufferUsage), pcxt->nworkers));
317 mul_size(sizeof(WalUsage), pcxt->nworkers));
319
320 /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
322 {
323 querylen = strlen(debug_query_string);
324 shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
326 }
327 else
328 querylen = 0; /* keep compiler quiet */
329
331
332 /* Prepare index vacuum stats */
333 indstats = (PVIndStats *) shm_toc_allocate(pcxt->toc, est_indstats_len);
334 MemSet(indstats, 0, est_indstats_len);
335 for (int i = 0; i < nindexes; i++)
336 {
337 Relation indrel = indrels[i];
338 uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
339
340 /*
341 * Cleanup option should be either disabled, always performing in
342 * parallel or conditionally performing in parallel.
343 */
344 Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
345 ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
347
348 if (!will_parallel_vacuum[i])
349 continue;
350
352 nindexes_mwm++;
353
354 /*
355 * Remember the number of indexes that support parallel operation for
356 * each phase.
357 */
358 if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
360 if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
362 if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
364 }
366 pvs->indstats = indstats;
367
368 /* Prepare shared information */
369 shared = (PVShared *) shm_toc_allocate(pcxt->toc, est_shared_len);
370 MemSet(shared, 0, est_shared_len);
371 shared->relid = RelationGetRelid(rel);
372 shared->elevel = elevel;
375 (nindexes_mwm > 0) ?
376 maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
378 shared->dead_items_info.max_bytes = vac_work_mem * (size_t) 1024;
379
380 /* Prepare DSA space for dead items */
382 LWTRANCHE_PARALLEL_VACUUM_DSA);
383 pvs->dead_items = dead_items;
384 shared->dead_items_handle = TidStoreGetHandle(dead_items);
386
387 /* Use the same buffer size for all workers */
388 shared->ring_nbuffers = GetAccessStrategyBufferCount(bstrategy);
389
390 pg_atomic_init_u32(&(shared->cost_balance), 0);
391 pg_atomic_init_u32(&(shared->active_nworkers), 0);
392 pg_atomic_init_u32(&(shared->idx), 0);
393
395 pvs->shared = shared;
396
397 /*
398 * Allocate space for each worker's BufferUsage and WalUsage; no need to
399 * initialize
400 */
401 buffer_usage = shm_toc_allocate(pcxt->toc,
402 mul_size(sizeof(BufferUsage), pcxt->nworkers));
404 pvs->buffer_usage = buffer_usage;
405 wal_usage = shm_toc_allocate(pcxt->toc,
406 mul_size(sizeof(WalUsage), pcxt->nworkers));
408 pvs->wal_usage = wal_usage;
409
410 /* Store query string for workers */
412 {
413 char *sharedquery;
414
415 sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
416 memcpy(sharedquery, debug_query_string, querylen + 1);
417 sharedquery[querylen] = '\0';
418 shm_toc_insert(pcxt->toc,
419 PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
420 }
421
422 /* Success -- return parallel vacuum state */
423 return pvs;
424}
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:219
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:211
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:173
int64 pgstat_get_my_query_id(void)
#define Min(x, y)
Definition: c.h:1003
uint8_t uint8
Definition: c.h:536
#define MemSet(start, val, len)
Definition: c.h:1019
size_t Size
Definition: c.h:610
dsa_handle dsa_get_handle(dsa_area *area)
Definition: dsa.c:498
int GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
Definition: freelist.c:549
int maintenance_work_mem
Definition: globals.c:133
const char * debug_query_string
Definition: postgres.c:88
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition: shm_toc.c:88
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition: shm_toc.c:171
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
#define shm_toc_estimate_keys(e, cnt)
Definition: shm_toc.h:53
Size mul_size(Size s1, Size s2)
Definition: shmem.c:510
bool amusemaintenanceworkmem
Definition: amapi.h:280
uint8 amparallelvacuumoptions
Definition: amapi.h:284
pg_atomic_uint32 cost_balance
int maintenance_work_mem_worker
int64 queryid
pg_atomic_uint32 active_nworkers
dsa_pointer dead_items_handle
dsa_handle dead_items_dsa_handle
int ring_nbuffers
pg_atomic_uint32 idx
shm_toc_estimator estimator
Definition: parallel.h:41
shm_toc * toc
Definition: parallel.h:44
BufferAccessStrategy bstrategy
BufferUsage * buffer_usage
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
size_t max_bytes
Definition: vacuum.h:294
dsa_area * TidStoreGetDSA(TidStore *ts)
Definition: tidstore.c:544
dsa_pointer TidStoreGetHandle(TidStore *ts)
Definition: tidstore.c:552
TidStore * TidStoreCreateShared(size_t max_bytes, int tranche_id)
Definition: tidstore.c:208
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:63
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:48
#define VACUUM_OPTION_MAX_VALID_VALUE
Definition: vacuum.h:66
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:55
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
void EnterParallelMode(void)
Definition: xact.c:1051

References PVShared::active_nworkers, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::amusemaintenanceworkmem, Assert(), ParallelVacuumState::bstrategy, ParallelVacuumState::buffer_usage, PVShared::cost_balance, CreateParallelContext(), ParallelVacuumState::dead_items, PVShared::dead_items_dsa_handle, PVShared::dead_items_handle, PVShared::dead_items_info, debug_query_string, dsa_get_handle(), PVShared::elevel, EnterParallelMode(), ParallelContext::estimator, GetAccessStrategyBufferCount(), ParallelVacuumState::heaprel, i, PVShared::idx, ParallelVacuumState::indrels, ParallelVacuumState::indstats, InitializeParallelDSM(), maintenance_work_mem, PVShared::maintenance_work_mem_worker, VacDeadItemsInfo::max_bytes, MemSet, Min, mul_size(), ParallelVacuumState::nindexes, ParallelVacuumState::nindexes_parallel_bulkdel, ParallelVacuumState::nindexes_parallel_cleanup, ParallelVacuumState::nindexes_parallel_condcleanup, ParallelContext::nworkers, palloc0(), parallel_vacuum_compute_workers(), PARALLEL_VACUUM_KEY_BUFFER_USAGE, 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(), pgstat_get_my_query_id(), PVShared::queryid, 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(), TidStoreCreateShared(), TidStoreGetDSA(), TidStoreGetHandle(), ParallelContext::toc, 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 989 of file vacuumparallel.c.

990{
992 Relation rel;
993 Relation *indrels;
994 PVIndStats *indstats;
995 PVShared *shared;
996 TidStore *dead_items;
997 BufferUsage *buffer_usage;
998 WalUsage *wal_usage;
999 int nindexes;
1000 char *sharedquery;
1001 ErrorContextCallback errcallback;
1002
1003 /*
1004 * A parallel vacuum worker must have only PROC_IN_VACUUM flag since we
1005 * don't support parallel vacuum for autovacuum as of now.
1006 */
1008
1009 elog(DEBUG1, "starting parallel vacuum worker");
1010
1011 shared = (PVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false);
1012
1013 /* Set debug_query_string for individual workers */
1014 sharedquery = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_QUERY_TEXT, true);
1015 debug_query_string = sharedquery;
1017
1018 /* Track query ID */
1019 pgstat_report_query_id(shared->queryid, false);
1020
1021 /*
1022 * Open table. The lock mode is the same as the leader process. It's
1023 * okay because the lock mode does not conflict among the parallel
1024 * workers.
1025 */
1027
1028 /*
1029 * Open all indexes. indrels are sorted in order by OID, which should be
1030 * matched to the leader's one.
1031 */
1032 vac_open_indexes(rel, RowExclusiveLock, &nindexes, &indrels);
1033 Assert(nindexes > 0);
1034
1035 /*
1036 * Apply the desired value of maintenance_work_mem within this process.
1037 * Really we should use SetConfigOption() to change a GUC, but since we're
1038 * already in parallel mode guc.c would complain about that. Fortunately,
1039 * by the same token guc.c will not let any user-defined code change it.
1040 * So just avert your eyes while we do this:
1041 */
1042 if (shared->maintenance_work_mem_worker > 0)
1044
1045 /* Set index statistics */
1046 indstats = (PVIndStats *) shm_toc_lookup(toc,
1048 false);
1049
1050 /* Find dead_items in shared memory */
1051 dead_items = TidStoreAttach(shared->dead_items_dsa_handle,
1052 shared->dead_items_handle);
1053
1054 /* Set cost-based vacuum delay */
1060
1061 /* Set parallel vacuum state */
1062 pvs.indrels = indrels;
1063 pvs.nindexes = nindexes;
1064 pvs.indstats = indstats;
1065 pvs.shared = shared;
1066 pvs.dead_items = dead_items;
1069 pvs.heaprel = rel;
1070
1071 /* These fields will be filled during index vacuum or cleanup */
1072 pvs.indname = NULL;
1074
1075 /* Each parallel VACUUM worker gets its own access strategy. */
1077 shared->ring_nbuffers * (BLCKSZ / 1024));
1078
1079 /* Setup error traceback support for ereport() */
1081 errcallback.arg = &pvs;
1082 errcallback.previous = error_context_stack;
1083 error_context_stack = &errcallback;
1084
1085 /* Prepare to track buffer usage during parallel execution */
1087
1088 /* Process indexes to perform vacuum/cleanup */
1090
1091 /* Report buffer/WAL usage during parallel execution */
1092 buffer_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, false);
1093 wal_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_WAL_USAGE, false);
1095 &wal_usage[ParallelWorkerNumber]);
1096
1097 /* Report any remaining cost-based vacuum delay time */
1101
1102 TidStoreDetach(dead_items);
1103
1104 /* Pop the error context stack */
1105 error_context_stack = errcallback.previous;
1106
1107 vac_close_indexes(nindexes, indrels, RowExclusiveLock);
1110}
void VacuumUpdateCosts(void)
Definition: autovacuum.c:1667
int ParallelWorkerNumber
Definition: parallel.c:115
void pgstat_progress_parallel_incr_param(int index, int64 incr)
void pgstat_report_query_id(int64 query_id, bool force)
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:606
int VacuumCostBalance
Definition: globals.c:157
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:3533
char * pstrdup(const char *in)
Definition: mcxt.c:1759
#define PROC_IN_VACUUM
Definition: proc.h:58
#define PROGRESS_VACUUM_DELAY_TIME
Definition: progress.h:31
#define RelationGetNamespace(relation)
Definition: rel.h:555
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:297
void(* callback)(void *arg)
Definition: elog.h:298
uint8 statusFlags
Definition: proc.h:259
PVIndVacStatus status
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
void TidStoreDetach(TidStore *ts)
Definition: tidstore.c:269
TidStore * TidStoreAttach(dsa_handle area_handle, dsa_pointer handle)
Definition: tidstore.c:244
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:116
bool track_cost_delay_timing
Definition: vacuum.c:81
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2359
int VacuumCostBalanceLocal
Definition: vacuum.c:117
int64 parallel_vacuum_worker_delay_ns
Definition: vacuum.c:94
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2402
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:115
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, PVShared::dead_items_dsa_handle, PVShared::dead_items_handle, 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_INDEX_STATS, PARALLEL_VACUUM_KEY_QUERY_TEXT, PARALLEL_VACUUM_KEY_SHARED, PARALLEL_VACUUM_KEY_WAL_USAGE, parallel_vacuum_process_safe_indexes(), parallel_vacuum_worker_delay_ns, ParallelWorkerNumber, pgstat_progress_parallel_incr_param(), pgstat_report_activity(), pgstat_report_query_id(), ErrorContextCallback::previous, PROC_IN_VACUUM, PROGRESS_VACUUM_DELAY_TIME, pstrdup(), PVShared::queryid, 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(), TidStoreAttach(), TidStoreDetach(), track_cost_delay_timing, vac_close_indexes(), vac_open_indexes(), VacuumActiveNWorkers, VacuumCostBalance, VacuumCostBalanceLocal, VacuumSharedCostBalance, and VacuumUpdateCosts().

◆ parallel_vacuum_reset_dead_items()

void parallel_vacuum_reset_dead_items ( ParallelVacuumState pvs)

Definition at line 475 of file vacuumparallel.c.

476{
477 VacDeadItemsInfo *dead_items_info = &(pvs->shared->dead_items_info);
478
479 /*
480 * Free the current tidstore and return allocated DSA segments to the
481 * operating system. Then we recreate the tidstore with the same max_bytes
482 * limitation we just used.
483 */
485 pvs->dead_items = TidStoreCreateShared(dead_items_info->max_bytes,
486 LWTRANCHE_PARALLEL_VACUUM_DSA);
487
488 /* Update the DSA pointer for dead_items to the new one */
491
492 /* Reset the counter */
493 dead_items_info->num_items = 0;
494}
int64 num_items
Definition: vacuum.h:295

References ParallelVacuumState::dead_items, PVShared::dead_items_dsa_handle, PVShared::dead_items_handle, PVShared::dead_items_info, dsa_get_handle(), VacDeadItemsInfo::max_bytes, VacDeadItemsInfo::num_items, ParallelVacuumState::shared, TidStoreCreateShared(), TidStoreDestroy(), TidStoreGetDSA(), and TidStoreGetHandle().

Referenced by dead_items_reset().

◆ std_typanalyze()

bool std_typanalyze ( VacAttrStats stats)

Definition at line 1889 of file analyze.c.

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

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,
TidStore dead_items,
VacDeadItemsInfo dead_items_info 
)

Definition at line 2630 of file vacuum.c.

2632{
2633 /* Do bulk deletion */
2634 istat = index_bulk_delete(ivinfo, istat, vac_tid_reaped,
2635 dead_items);
2636
2637 ereport(ivinfo->message_level,
2638 (errmsg("scanned index \"%s\" to remove %" PRId64 " row versions",
2640 dead_items_info->num_items)));
2641
2642 return istat;
2643}
IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *istat, IndexBulkDeleteCallback callback, void *callback_state)
Definition: indexam.c:805
Relation index
Definition: genam.h:73
int message_level
Definition: genam.h:78
static bool vac_tid_reaped(ItemPointer itemptr, void *state)
Definition: vacuum.c:2677

References ereport, errmsg(), IndexVacuumInfo::index, index_bulk_delete(), IndexVacuumInfo::message_level, VacDeadItemsInfo::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 2651 of file vacuum.c.

2652{
2653 istat = index_vacuum_cleanup(ivinfo, istat);
2654
2655 if (istat)
2656 ereport(ivinfo->message_level,
2657 (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
2659 istat->num_index_tuples,
2660 istat->num_pages),
2661 errdetail("%.0f index row versions were removed.\n"
2662 "%u index pages were newly deleted.\n"
2663 "%u index pages are currently deleted, of which %u are currently reusable.",
2664 istat->tuples_removed,
2665 istat->pages_newly_deleted,
2666 istat->pages_deleted, istat->pages_free)));
2667
2668 return istat;
2669}
int errdetail(const char *fmt,...)
Definition: elog.c:1207
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:826
BlockNumber pages_deleted
Definition: genam.h:109
BlockNumber pages_newly_deleted
Definition: genam.h:108
BlockNumber pages_free
Definition: genam.h:110
BlockNumber num_pages
Definition: genam.h:104
double tuples_removed
Definition: genam.h:107
double num_index_tuples
Definition: genam.h:106

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

2403{
2404 if (Irel == NULL)
2405 return;
2406
2407 while (nindexes--)
2408 {
2409 Relation ind = Irel[nindexes];
2410
2411 index_close(ind, lockmode);
2412 }
2413 pfree(Irel);
2414}
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177

References index_close(), and pfree().

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

◆ vac_estimate_reltuples()

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

Definition at line 1333 of file vacuum.c.

1337{
1338 BlockNumber old_rel_pages = relation->rd_rel->relpages;
1339 double old_rel_tuples = relation->rd_rel->reltuples;
1340 double old_density;
1341 double unscanned_pages;
1342 double total_tuples;
1343
1344 /* If we did scan the whole table, just use the count as-is */
1345 if (scanned_pages >= total_pages)
1346 return scanned_tuples;
1347
1348 /*
1349 * When successive VACUUM commands scan the same few pages again and
1350 * again, without anything from the table really changing, there is a risk
1351 * that our beliefs about tuple density will gradually become distorted.
1352 * This might be caused by vacuumlazy.c implementation details, such as
1353 * its tendency to always scan the last heap page. Handle that here.
1354 *
1355 * If the relation is _exactly_ the same size according to the existing
1356 * pg_class entry, and only a few of its pages (less than 2%) were
1357 * scanned, keep the existing value of reltuples. Also keep the existing
1358 * value when only a subset of rel's pages <= a single page were scanned.
1359 *
1360 * (Note: we might be returning -1 here.)
1361 */
1362 if (old_rel_pages == total_pages &&
1363 scanned_pages < (double) total_pages * 0.02)
1364 return old_rel_tuples;
1365 if (scanned_pages <= 1)
1366 return old_rel_tuples;
1367
1368 /*
1369 * If old density is unknown, we can't do much except scale up
1370 * scanned_tuples to match total_pages.
1371 */
1372 if (old_rel_tuples < 0 || old_rel_pages == 0)
1373 return floor((scanned_tuples / scanned_pages) * total_pages + 0.5);
1374
1375 /*
1376 * Okay, we've covered the corner cases. The normal calculation is to
1377 * convert the old measurement to a density (tuples per page), then
1378 * estimate the number of tuples in the unscanned pages using that figure,
1379 * and finally add on the number of tuples in the scanned pages.
1380 */
1381 old_density = old_rel_tuples / old_rel_pages;
1382 unscanned_pages = (double) total_pages - (double) scanned_pages;
1383 total_tuples = old_density * unscanned_pages + scanned_tuples;
1384 return floor(total_tuples + 0.5);
1385}

References RelationData::rd_rel.

Referenced by lazy_scan_heap(), and statapprox_heap().

◆ vac_open_indexes()

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

Definition at line 2359 of file vacuum.c.

2361{
2362 List *indexoidlist;
2363 ListCell *indexoidscan;
2364 int i;
2365
2366 Assert(lockmode != NoLock);
2367
2368 indexoidlist = RelationGetIndexList(relation);
2369
2370 /* allocate enough memory for all indexes */
2371 i = list_length(indexoidlist);
2372
2373 if (i > 0)
2374 *Irel = (Relation *) palloc(i * sizeof(Relation));
2375 else
2376 *Irel = NULL;
2377
2378 /* collect just the ready indexes */
2379 i = 0;
2380 foreach(indexoidscan, indexoidlist)
2381 {
2382 Oid indexoid = lfirst_oid(indexoidscan);
2383 Relation indrel;
2384
2385 indrel = index_open(indexoid, lockmode);
2386 if (indrel->rd_index->indisready)
2387 (*Irel)[i++] = indrel;
2388 else
2389 index_close(indrel, lockmode);
2390 }
2391
2392 *nindexes = i;
2393
2394 list_free(indexoidlist);
2395}
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void list_free(List *list)
Definition: list.c:1546
static int list_length(const List *l)
Definition: pg_list.h:152
#define lfirst_oid(lc)
Definition: pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4836
Definition: pg_list.h:54
Form_pg_index rd_index
Definition: rel.h:192

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

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

◆ vac_update_datfrozenxid()

void vac_update_datfrozenxid ( void  )

Definition at line 1611 of file vacuum.c.

1612{
1613 HeapTuple tuple;
1614 Form_pg_database dbform;
1615 Relation relation;
1616 SysScanDesc scan;
1617 HeapTuple classTup;
1618 TransactionId newFrozenXid;
1619 MultiXactId newMinMulti;
1620 TransactionId lastSaneFrozenXid;
1621 MultiXactId lastSaneMinMulti;
1622 bool bogus = false;
1623 bool dirty = false;
1624 ScanKeyData key[1];
1625 void *inplace_state;
1626
1627 /*
1628 * Restrict this task to one backend per database. This avoids race
1629 * conditions that would move datfrozenxid or datminmxid backward. It
1630 * avoids calling vac_truncate_clog() with a datfrozenxid preceding a
1631 * datfrozenxid passed to an earlier vac_truncate_clog() call.
1632 */
1634
1635 /*
1636 * Initialize the "min" calculation with
1637 * GetOldestNonRemovableTransactionId(), which is a reasonable
1638 * approximation to the minimum relfrozenxid for not-yet-committed
1639 * pg_class entries for new tables; see AddNewRelationTuple(). So we
1640 * cannot produce a wrong minimum by starting with this.
1641 */
1642 newFrozenXid = GetOldestNonRemovableTransactionId(NULL);
1643
1644 /*
1645 * Similarly, initialize the MultiXact "min" with the value that would be
1646 * used on pg_class for new tables. See AddNewRelationTuple().
1647 */
1648 newMinMulti = GetOldestMultiXactId();
1649
1650 /*
1651 * Identify the latest relfrozenxid and relminmxid values that we could
1652 * validly see during the scan. These are conservative values, but it's
1653 * not really worth trying to be more exact.
1654 */
1655 lastSaneFrozenXid = ReadNextTransactionId();
1656 lastSaneMinMulti = ReadNextMultiXactId();
1657
1658 /*
1659 * We must seqscan pg_class to find the minimum Xid, because there is no
1660 * index that can help us here.
1661 *
1662 * See vac_truncate_clog() for the race condition to prevent.
1663 */
1664 relation = table_open(RelationRelationId, AccessShareLock);
1665
1666 scan = systable_beginscan(relation, InvalidOid, false,
1667 NULL, 0, NULL);
1668
1669 while ((classTup = systable_getnext(scan)) != NULL)
1670 {
1671 volatile FormData_pg_class *classForm = (Form_pg_class) GETSTRUCT(classTup);
1672 TransactionId relfrozenxid = classForm->relfrozenxid;
1673 TransactionId relminmxid = classForm->relminmxid;
1674
1675 /*
1676 * Only consider relations able to hold unfrozen XIDs (anything else
1677 * should have InvalidTransactionId in relfrozenxid anyway).
1678 */
1679 if (classForm->relkind != RELKIND_RELATION &&
1680 classForm->relkind != RELKIND_MATVIEW &&
1681 classForm->relkind != RELKIND_TOASTVALUE)
1682 {
1683 Assert(!TransactionIdIsValid(relfrozenxid));
1684 Assert(!MultiXactIdIsValid(relminmxid));
1685 continue;
1686 }
1687
1688 /*
1689 * Some table AMs might not need per-relation xid / multixid horizons.
1690 * It therefore seems reasonable to allow relfrozenxid and relminmxid
1691 * to not be set (i.e. set to their respective Invalid*Id)
1692 * independently. Thus validate and compute horizon for each only if
1693 * set.
1694 *
1695 * If things are working properly, no relation should have a
1696 * relfrozenxid or relminmxid that is "in the future". However, such
1697 * cases have been known to arise due to bugs in pg_upgrade. If we
1698 * see any entries that are "in the future", chicken out and don't do
1699 * anything. This ensures we won't truncate clog & multixact SLRUs
1700 * before those relations have been scanned and cleaned up.
1701 */
1702
1703 if (TransactionIdIsValid(relfrozenxid))
1704 {
1705 Assert(TransactionIdIsNormal(relfrozenxid));
1706
1707 /* check for values in the future */
1708 if (TransactionIdPrecedes(lastSaneFrozenXid, relfrozenxid))
1709 {
1710 bogus = true;
1711 break;
1712 }
1713
1714 /* determine new horizon */
1715 if (TransactionIdPrecedes(relfrozenxid, newFrozenXid))
1716 newFrozenXid = relfrozenxid;
1717 }
1718
1719 if (MultiXactIdIsValid(relminmxid))
1720 {
1721 /* check for values in the future */
1722 if (MultiXactIdPrecedes(lastSaneMinMulti, relminmxid))
1723 {
1724 bogus = true;
1725 break;
1726 }
1727
1728 /* determine new horizon */
1729 if (MultiXactIdPrecedes(relminmxid, newMinMulti))
1730 newMinMulti = relminmxid;
1731 }
1732 }
1733
1734 /* we're done with pg_class */
1735 systable_endscan(scan);
1736 table_close(relation, AccessShareLock);
1737
1738 /* chicken out if bogus data found */
1739 if (bogus)
1740 return;
1741
1742 Assert(TransactionIdIsNormal(newFrozenXid));
1743 Assert(MultiXactIdIsValid(newMinMulti));
1744
1745 /* Now fetch the pg_database tuple we need to update. */
1746 relation = table_open(DatabaseRelationId, RowExclusiveLock);
1747
1748 /*
1749 * Fetch a copy of the tuple to scribble on. We could check the syscache
1750 * tuple first. If that concluded !dirty, we'd avoid waiting on
1751 * concurrent heap_update() and would avoid exclusive-locking the buffer.
1752 * For now, don't optimize that.
1753 */
1754 ScanKeyInit(&key[0],
1755 Anum_pg_database_oid,
1756 BTEqualStrategyNumber, F_OIDEQ,
1758
1759 systable_inplace_update_begin(relation, DatabaseOidIndexId, true,
1760 NULL, 1, key, &tuple, &inplace_state);
1761
1762 if (!HeapTupleIsValid(tuple))
1763 elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
1764
1765 dbform = (Form_pg_database) GETSTRUCT(tuple);
1766
1767 /*
1768 * As in vac_update_relstats(), we ordinarily don't want to let
1769 * datfrozenxid go backward; but if it's "in the future" then it must be
1770 * corrupt and it seems best to overwrite it.
1771 */
1772 if (dbform->datfrozenxid != newFrozenXid &&
1773 (TransactionIdPrecedes(dbform->datfrozenxid, newFrozenXid) ||
1774 TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid)))
1775 {
1776 dbform->datfrozenxid = newFrozenXid;
1777 dirty = true;
1778 }
1779 else
1780 newFrozenXid = dbform->datfrozenxid;
1781
1782 /* Ditto for datminmxid */
1783 if (dbform->datminmxid != newMinMulti &&
1784 (MultiXactIdPrecedes(dbform->datminmxid, newMinMulti) ||
1785 MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid)))
1786 {
1787 dbform->datminmxid = newMinMulti;
1788 dirty = true;
1789 }
1790 else
1791 newMinMulti = dbform->datminmxid;
1792
1793 if (dirty)
1794 systable_inplace_update_finish(inplace_state, tuple);
1795 else
1796 systable_inplace_update_cancel(inplace_state);
1797
1798 heap_freetuple(tuple);
1799 table_close(relation, RowExclusiveLock);
1800
1801 /*
1802 * If we were able to advance datfrozenxid or datminmxid, see if we can
1803 * truncate pg_xact and/or pg_multixact. Also do it if the shared
1804 * XID-wrap-limit info is stale, since this action will update that too.
1805 */
1806 if (dirty || ForceTransactionIdLimitUpdate())
1807 vac_truncate_clog(newFrozenXid, newMinMulti,
1808 lastSaneFrozenXid, lastSaneMinMulti);
1809}
TransactionId MultiXactId
Definition: c.h:667
uint32 TransactionId
Definition: c.h:657
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
void systable_inplace_update_cancel(void *state)
Definition: genam.c:902
void systable_inplace_update_begin(Relation relation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, const ScanKeyData *key, HeapTuple *oldtupcopy, void **state)
Definition: genam.c:807
void systable_inplace_update_finish(void *state, HeapTuple tuple)
Definition: genam.c:883
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
Oid MyDatabaseId
Definition: globals.c:94
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
void LockDatabaseFrozenIds(LOCKMODE lockmode)
Definition: lmgr.c:491
#define AccessShareLock
Definition: lockdefs.h:36
#define ExclusiveLock
Definition: lockdefs.h:42
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3265
MultiXactId GetOldestMultiXactId(void)
Definition: multixact.c:2594
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:762
#define MultiXactIdIsValid(multi)
Definition: multixact.h:29
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
FormData_pg_class
Definition: pg_class.h:145
FormData_pg_database * Form_pg_database
Definition: pg_database.h:96
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:1953
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:1830
bool ForceTransactionIdLimitUpdate(void)
Definition: varsup.c:517

References AccessShareLock, Assert(), BTEqualStrategyNumber, elog, ERROR, ExclusiveLock, ForceTransactionIdLimitUpdate(), FormData_pg_class, GetOldestMultiXactId(), GetOldestNonRemovableTransactionId(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, sort-test::key, LockDatabaseFrozenIds(), MultiXactIdIsValid, MultiXactIdPrecedes(), MyDatabaseId, ObjectIdGetDatum(), ReadNextMultiXactId(), ReadNextTransactionId(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), systable_inplace_update_begin(), systable_inplace_update_cancel(), systable_inplace_update_finish(), 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,
BlockNumber  num_all_frozen_pages,
bool  hasindex,
TransactionId  frozenxid,
MultiXactId  minmulti,
bool *  frozenxid_updated,
bool *  minmulti_updated,
bool  in_outer_xact 
)

Definition at line 1429 of file vacuum.c.

1437{
1438 Oid relid = RelationGetRelid(relation);
1439 Relation rd;
1440 ScanKeyData key[1];
1441 HeapTuple ctup;
1442 void *inplace_state;
1443 Form_pg_class pgcform;
1444 bool dirty,
1445 futurexid,
1446 futuremxid;
1447 TransactionId oldfrozenxid;
1448 MultiXactId oldminmulti;
1449
1450 rd = table_open(RelationRelationId, RowExclusiveLock);
1451
1452 /* Fetch a copy of the tuple to scribble on */
1453 ScanKeyInit(&key[0],
1454 Anum_pg_class_oid,
1455 BTEqualStrategyNumber, F_OIDEQ,
1456 ObjectIdGetDatum(relid));
1457 systable_inplace_update_begin(rd, ClassOidIndexId, true,
1458 NULL, 1, key, &ctup, &inplace_state);
1459 if (!HeapTupleIsValid(ctup))
1460 elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
1461 relid);
1462 pgcform = (Form_pg_class) GETSTRUCT(ctup);
1463
1464 /* Apply statistical updates, if any, to copied tuple */
1465
1466 dirty = false;
1467 if (pgcform->relpages != (int32) num_pages)
1468 {
1469 pgcform->relpages = (int32) num_pages;
1470 dirty = true;
1471 }
1472 if (pgcform->reltuples != (float4) num_tuples)
1473 {
1474 pgcform->reltuples = (float4) num_tuples;
1475 dirty = true;
1476 }
1477 if (pgcform->relallvisible != (int32) num_all_visible_pages)
1478 {
1479 pgcform->relallvisible = (int32) num_all_visible_pages;
1480 dirty = true;
1481 }
1482 if (pgcform->relallfrozen != (int32) num_all_frozen_pages)
1483 {
1484 pgcform->relallfrozen = (int32) num_all_frozen_pages;
1485 dirty = true;
1486 }
1487
1488 /* Apply DDL updates, but not inside an outer transaction (see above) */
1489
1490 if (!in_outer_xact)
1491 {
1492 /*
1493 * If we didn't find any indexes, reset relhasindex.
1494 */
1495 if (pgcform->relhasindex && !hasindex)
1496 {
1497 pgcform->relhasindex = false;
1498 dirty = true;
1499 }
1500
1501 /* We also clear relhasrules and relhastriggers if needed */
1502 if (pgcform->relhasrules && relation->rd_rules == NULL)
1503 {
1504 pgcform->relhasrules = false;
1505 dirty = true;
1506 }
1507 if (pgcform->relhastriggers && relation->trigdesc == NULL)
1508 {
1509 pgcform->relhastriggers = false;
1510 dirty = true;
1511 }
1512 }
1513
1514 /*
1515 * Update relfrozenxid, unless caller passed InvalidTransactionId
1516 * indicating it has no new data.
1517 *
1518 * Ordinarily, we don't let relfrozenxid go backwards. However, if the
1519 * stored relfrozenxid is "in the future" then it seems best to assume
1520 * it's corrupt, and overwrite with the oldest remaining XID in the table.
1521 * This should match vac_update_datfrozenxid() concerning what we consider
1522 * to be "in the future".
1523 */
1524 oldfrozenxid = pgcform->relfrozenxid;
1525 futurexid = false;
1526 if (frozenxid_updated)
1527 *frozenxid_updated = false;
1528 if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
1529 {
1530 bool update = false;
1531
1532 if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
1533 update = true;
1534 else if (TransactionIdPrecedes(ReadNextTransactionId(), oldfrozenxid))
1535 futurexid = update = true;
1536
1537 if (update)
1538 {
1539 pgcform->relfrozenxid = frozenxid;
1540 dirty = true;
1541 if (frozenxid_updated)
1542 *frozenxid_updated = true;
1543 }
1544 }
1545
1546 /* Similarly for relminmxid */
1547 oldminmulti = pgcform->relminmxid;
1548 futuremxid = false;
1549 if (minmulti_updated)
1550 *minmulti_updated = false;
1551 if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
1552 {
1553 bool update = false;
1554
1555 if (MultiXactIdPrecedes(oldminmulti, minmulti))
1556 update = true;
1557 else if (MultiXactIdPrecedes(ReadNextMultiXactId(), oldminmulti))
1558 futuremxid = update = true;
1559
1560 if (update)
1561 {
1562 pgcform->relminmxid = minmulti;
1563 dirty = true;
1564 if (minmulti_updated)
1565 *minmulti_updated = true;
1566 }
1567 }
1568
1569 /* If anything changed, write out the tuple. */
1570 if (dirty)
1571 systable_inplace_update_finish(inplace_state, ctup);
1572 else
1573 systable_inplace_update_cancel(inplace_state);
1574
1576
1577 if (futurexid)
1580 errmsg_internal("overwrote invalid relfrozenxid value %u with new value %u for table \"%s\"",
1581 oldfrozenxid, frozenxid,
1582 RelationGetRelationName(relation))));
1583 if (futuremxid)
1586 errmsg_internal("overwrote invalid relminmxid value %u with new value %u for table \"%s\"",
1587 oldminmulti, minmulti,
1588 RelationGetRelationName(relation))));
1589}
int32_t int32
Definition: c.h:534
float float4
Definition: c.h:634
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1161
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:42
TriggerDesc * trigdesc
Definition: rel.h:117
RuleLock * rd_rules
Definition: rel.h:115

References BTEqualStrategyNumber, elog, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, GETSTRUCT(), HeapTupleIsValid, sort-test::key, MultiXactIdIsValid, MultiXactIdPrecedes(), ObjectIdGetDatum(), RelationData::rd_rules, ReadNextMultiXactId(), ReadNextTransactionId(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_inplace_update_begin(), systable_inplace_update_cancel(), systable_inplace_update_finish(), 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,
const VacuumParams  params,
BufferAccessStrategy  bstrategy,
MemoryContext  vac_context,
bool  isTopLevel 
)

Definition at line 497 of file vacuum.c.

499{
500 static bool in_vacuum = false;
501
502 const char *stmttype;
503 volatile bool in_outer_xact,
504 use_own_xacts;
505
506 stmttype = (params.options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
507
508 /*
509 * We cannot run VACUUM inside a user transaction block; if we were inside
510 * a transaction, then our commit- and start-transaction-command calls
511 * would not have the intended effect! There are numerous other subtle
512 * dependencies on this, too.
513 *
514 * ANALYZE (without VACUUM) can run either way.
515 */
516 if (params.options & VACOPT_VACUUM)
517 {
518 PreventInTransactionBlock(isTopLevel, stmttype);
519 in_outer_xact = false;
520 }
521 else
522 in_outer_xact = IsInTransactionBlock(isTopLevel);
523
524 /*
525 * Check for and disallow recursive calls. This could happen when VACUUM
526 * FULL or ANALYZE calls a hostile index expression that itself calls
527 * ANALYZE.
528 */
529 if (in_vacuum)
531 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
532 errmsg("%s cannot be executed from VACUUM or ANALYZE",
533 stmttype)));
534
535 /*
536 * Build list of relation(s) to process, putting any new data in
537 * vac_context for safekeeping.
538 */
540 {
541 /* We don't process any tables in this case */
542 Assert(relations == NIL);
543 }
544 else if (relations != NIL)
545 {
546 List *newrels = NIL;
547 ListCell *lc;
548
549 foreach(lc, relations)
550 {
552 List *sublist;
553 MemoryContext old_context;
554
555 sublist = expand_vacuum_rel(vrel, vac_context, params.options);
556 old_context = MemoryContextSwitchTo(vac_context);
557 newrels = list_concat(newrels, sublist);
558 MemoryContextSwitchTo(old_context);
559 }
560 relations = newrels;
561 }
562 else
563 relations = get_all_vacuum_rels(vac_context, params.options);
564
565 /*
566 * Decide whether we need to start/commit our own transactions.
567 *
568 * For VACUUM (with or without ANALYZE): always do so, so that we can
569 * release locks as soon as possible. (We could possibly use the outer
570 * transaction for a one-table VACUUM, but handling TOAST tables would be
571 * problematic.)
572 *
573 * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
574 * start/commit our own transactions. Also, there's no need to do so if
575 * only processing one relation. For multiple relations when not within a
576 * transaction block, and also in an autovacuum worker, use own
577 * transactions so we can release locks sooner.
578 */
579 if (params.options & VACOPT_VACUUM)
580 use_own_xacts = true;
581 else
582 {
583 Assert(params.options & VACOPT_ANALYZE);
585 use_own_xacts = true;
586 else if (in_outer_xact)
587 use_own_xacts = false;
588 else if (list_length(relations) > 1)
589 use_own_xacts = true;
590 else
591 use_own_xacts = false;
592 }
593
594 /*
595 * vacuum_rel expects to be entered with no transaction active; it will
596 * start and commit its own transaction. But we are called by an SQL
597 * command, and so we are executing inside a transaction already. We
598 * commit the transaction started in PostgresMain() here, and start
599 * another one before exiting to match the commit waiting for us back in
600 * PostgresMain().
601 */
602 if (use_own_xacts)
603 {
604 Assert(!in_outer_xact);
605
606 /* ActiveSnapshot is not set by autovacuum */
607 if (ActiveSnapshotSet())
609
610 /* matches the StartTransaction in PostgresMain() */
612 }
613
614 /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
615 PG_TRY();
616 {
617 ListCell *cur;
618
619 in_vacuum = true;
620 VacuumFailsafeActive = false;
626
627 /*
628 * Loop to process each selected relation.
629 */
630 foreach(cur, relations)
631 {
633
634 if (params.options & VACOPT_VACUUM)
635 {
636 if (!vacuum_rel(vrel->oid, vrel->relation, params, bstrategy))
637 continue;
638 }
639
640 if (params.options & VACOPT_ANALYZE)
641 {
642 /*
643 * If using separate xacts, start one for analyze. Otherwise,
644 * we can use the outer transaction.
645 */
646 if (use_own_xacts)
647 {
649 /* functions in indexes may want a snapshot set */
651 }
652
653 analyze_rel(vrel->oid, vrel->relation, params,
654 vrel->va_cols, in_outer_xact, bstrategy);
655
656 if (use_own_xacts)
657 {
659 /* standard_ProcessUtility() does CCI if !use_own_xacts */
662 }
663 else
664 {
665 /*
666 * If we're not using separate xacts, better separate the
667 * ANALYZE actions with CCIs. This avoids trouble if user
668 * says "ANALYZE t, t".
669 */
671 }
672 }
673
674 /*
675 * Ensure VacuumFailsafeActive has been reset before vacuuming the
676 * next relation.
677 */
678 VacuumFailsafeActive = false;
679 }
680 }
681 PG_FINALLY();
682 {
683 in_vacuum = false;
684 VacuumCostActive = false;
685 VacuumFailsafeActive = false;
687 }
688 PG_END_TRY();
689
690 /*
691 * Finish up processing.
692 */
693 if (use_own_xacts)
694 {
695 /* here, we are not in a transaction */
696
697 /*
698 * This matches the CommitTransaction waiting for us in
699 * PostgresMain().
700 */
702 }
703
704 if ((params.options & VACOPT_VACUUM) &&
706 {
707 /*
708 * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
709 */
711 }
712
713}
void analyze_rel(Oid relid, RangeVar *relation, const VacuumParams params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
Definition: analyze.c:108
struct cursor * cur
Definition: ecpg.c:29
#define PG_TRY(...)
Definition: elog.h:372
#define PG_END_TRY(...)
Definition: elog.h:397
#define PG_FINALLY(...)
Definition: elog.h:389
bool VacuumCostActive
Definition: globals.c:158
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
#define AmAutoVacuumWorkerProcess()
Definition: miscadmin.h:382
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:680
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:810
void PopActiveSnapshot(void)
Definition: snapmgr.c:773
RangeVar * relation
Definition: parsenodes.h:3990
static List * expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, int options)
Definition: vacuum.c:886
static List * get_all_vacuum_rels(MemoryContext vac_context, int options)
Definition: vacuum.c:1041
void vac_update_datfrozenxid(void)
Definition: vacuum.c:1611
bool VacuumFailsafeActive
Definition: vacuum.c:109
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, BufferAccessStrategy bstrategy)
Definition: vacuum.c:1999
bool IsInTransactionBlock(bool isTopLevel)
Definition: xact.c:3781
void CommandCounterIncrement(void)
Definition: xact.c:1100
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3660
void StartTransactionCommand(void)
Definition: xact.c:3071
void CommitTransactionCommand(void)
Definition: xact.c:3169

References ActiveSnapshotSet(), AmAutoVacuumWorkerProcess, analyze_rel(), Assert(), CommandCounterIncrement(), CommitTransactionCommand(), cur, ereport, errcode(), errmsg(), ERROR, expand_vacuum_rel(), get_all_vacuum_rels(), GetTransactionSnapshot(), IsInTransactionBlock(), lfirst_node, list_concat(), list_length(), MemoryContextSwitchTo(), NIL, VacuumRelation::oid, VacuumParams::options, PG_END_TRY, PG_FINALLY, PG_TRY, PopActiveSnapshot(), PreventInTransactionBlock(), PushActiveSnapshot(), VacuumRelation::relation, StartTransactionCommand(), VacuumRelation::va_cols, vac_update_datfrozenxid(), VACOPT_ANALYZE, VACOPT_ONLY_DATABASE_STATS, VACOPT_SKIP_DATABASE_STATS, VACOPT_VACUUM, vacuum_rel(), VacuumActiveNWorkers, VacuumCostActive, VacuumCostBalance, VacuumCostBalanceLocal, VacuumFailsafeActive, 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 ( bool  is_analyze)

Definition at line 2423 of file vacuum.c.

2424{
2425 double msec = 0;
2426
2427 /* Always check for interrupts */
2429
2430 if (InterruptPending ||
2432 return;
2433
2434 /*
2435 * Autovacuum workers should reload the configuration file if requested.
2436 * This allows changes to [autovacuum_]vacuum_cost_limit and
2437 * [autovacuum_]vacuum_cost_delay to take effect while a table is being
2438 * vacuumed or analyzed.
2439 */
2441 {
2442 ConfigReloadPending = false;
2445 }
2446
2447 /*
2448 * If we disabled cost-based delays after reloading the config file,
2449 * return.
2450 */
2451 if (!VacuumCostActive)
2452 return;
2453
2454 /*
2455 * For parallel vacuum, the delay is computed based on the shared cost
2456 * balance. See compute_parallel_delay.
2457 */
2458 if (VacuumSharedCostBalance != NULL)
2459 msec = compute_parallel_delay();
2462
2463 /* Nap if appropriate */
2464 if (msec > 0)
2465 {
2466 instr_time delay_start;
2467
2468 if (msec > vacuum_cost_delay * 4)
2469 msec = vacuum_cost_delay * 4;
2470
2472 INSTR_TIME_SET_CURRENT(delay_start);
2473
2474 pgstat_report_wait_start(WAIT_EVENT_VACUUM_DELAY);
2475 pg_usleep(msec * 1000);
2477
2479 {
2480 instr_time delay_end;
2481 instr_time delay;
2482
2483 INSTR_TIME_SET_CURRENT(delay_end);
2484 INSTR_TIME_SET_ZERO(delay);
2485 INSTR_TIME_ACCUM_DIFF(delay, delay_end, delay_start);
2486
2487 /*
2488 * For parallel workers, we only report the delay time every once
2489 * in a while to avoid overloading the leader with messages and
2490 * interrupts.
2491 */
2492 if (IsParallelWorker())
2493 {
2494 static instr_time last_report_time;
2495 instr_time time_since_last_report;
2496
2497 Assert(!is_analyze);
2498
2499 /* Accumulate the delay time */
2501
2502 /* Calculate interval since last report */
2503 INSTR_TIME_SET_ZERO(time_since_last_report);
2504 INSTR_TIME_ACCUM_DIFF(time_since_last_report, delay_end, last_report_time);
2505
2506 /* If we haven't reported in a while, do so now */
2507 if (INSTR_TIME_GET_NANOSEC(time_since_last_report) >=
2509 {
2512
2513 /* Reset variables */
2514 last_report_time = delay_end;
2516 }
2517 }
2518 else if (is_analyze)
2520 INSTR_TIME_GET_NANOSEC(delay));
2521 else
2523 INSTR_TIME_GET_NANOSEC(delay));
2524 }
2525
2526 /*
2527 * We don't want to ignore postmaster death during very long vacuums
2528 * with vacuum_cost_delay configured. We can't use the usual
2529 * WaitLatch() approach here because we want microsecond-based sleep
2530 * durations above.
2531 */
2533 exit(1);
2534
2536
2537 /*
2538 * Balance and update limit values for autovacuum workers. We must do
2539 * this periodically, as the number of workers across which we are
2540 * balancing the limit may have changed.
2541 *
2542 * TODO: There may be better criteria for determining when to do this
2543 * besides "check after napping".
2544 */
2546
2547 /* Might have gotten an interrupt while sleeping */
2549 }
2550}
void AutoVacuumUpdateCostLimit(void)
Definition: autovacuum.c:1736
void pgstat_progress_incr_param(int index, int64 incr)
volatile sig_atomic_t InterruptPending
Definition: globals.c:32
bool IsUnderPostmaster
Definition: globals.c:120
void ProcessConfigFile(GucContext context)
Definition: guc-file.l:120
@ PGC_SIGHUP
Definition: guc.h:75
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_GET_NANOSEC(t)
Definition: instr_time.h:125
#define INSTR_TIME_SET_ZERO(t)
Definition: instr_time.h:172
#define INSTR_TIME_ACCUM_DIFF(x, y, z)
Definition: instr_time.h:184
volatile sig_atomic_t ConfigReloadPending
Definition: interrupt.c:27
#define PostmasterIsAlive()
Definition: pmsignal.h:107
#define PROGRESS_ANALYZE_DELAY_TIME
Definition: progress.h:50
void pg_usleep(long microsec)
Definition: signal.c:53
double vacuum_cost_delay
Definition: vacuum.c:90
static double compute_parallel_delay(void)
Definition: vacuum.c:2575
#define PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS
Definition: vacuum.c:69
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:69
static void pgstat_report_wait_end(void)
Definition: wait_event.h:85

References AmAutoVacuumWorkerProcess, Assert(), AutoVacuumUpdateCostLimit(), CHECK_FOR_INTERRUPTS, compute_parallel_delay(), ConfigReloadPending, INSTR_TIME_ACCUM_DIFF, INSTR_TIME_GET_NANOSEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SET_ZERO, InterruptPending, IsParallelWorker, IsUnderPostmaster, PARALLEL_VACUUM_DELAY_REPORT_INTERVAL_NS, parallel_vacuum_worker_delay_ns, pg_usleep(), PGC_SIGHUP, pgstat_progress_incr_param(), pgstat_progress_parallel_incr_param(), pgstat_report_wait_end(), pgstat_report_wait_start(), PostmasterIsAlive, ProcessConfigFile(), PROGRESS_ANALYZE_DELAY_TIME, PROGRESS_VACUUM_DELAY_TIME, track_cost_delay_timing, vacuum_cost_delay, vacuum_cost_limit, VacuumCostActive, VacuumCostBalance, VacuumSharedCostBalance, and VacuumUpdateCosts().

Referenced by acquire_sample_rows(), blbulkdelete(), blvacuumcleanup(), btvacuumpage(), btvacuumscan(), 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(), gistvacuumscan(), hashbucketcleanup(), lazy_scan_heap(), lazy_vacuum_heap_rel(), spgprocesspending(), and spgvacuumscan().

◆ vacuum_get_cutoffs()

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

Definition at line 1103 of file vacuum.c.

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

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

Referenced by copy_table_data(), and heap_vacuum_rel().

◆ vacuum_is_permitted_for_relation()

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

Definition at line 722 of file vacuum.c.

724{
725 char *relname;
726
728
729 /*----------
730 * A role has privileges to vacuum or analyze the relation if any of the
731 * following are true:
732 * - the role owns the current database and the relation is not shared
733 * - the role has the MAINTAIN privilege on the relation
734 *----------
735 */
736 if ((object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) &&
737 !reltuple->relisshared) ||
739 return true;
740
741 relname = NameStr(reltuple->relname);
742
743 if ((options & VACOPT_VACUUM) != 0)
744 {
746 (errmsg("permission denied to vacuum \"%s\", skipping it",
747 relname)));
748
749 /*
750 * For VACUUM ANALYZE, both logs could show up, but just generate
751 * information for VACUUM as that would be the first one to be
752 * processed.
753 */
754 return false;
755 }
756
757 if ((options & VACOPT_ANALYZE) != 0)
759 (errmsg("permission denied to analyze \"%s\", skipping it",
760 relname)));
761
762 return false;
763}
@ ACLCHECK_OK
Definition: acl.h:183
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4088
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4037
#define NameStr(name)
Definition: c.h:751
Oid GetUserId(void)
Definition: miscinit.c:469
#define ACL_MAINTAIN
Definition: parsenodes.h:90
NameData relname
Definition: pg_class.h:38

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

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

◆ vacuum_open_relation()

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

Definition at line 774 of file vacuum.c.

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

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

Referenced by analyze_rel(), and vacuum_rel().

◆ vacuum_xid_failsafe_check()

bool vacuum_xid_failsafe_check ( const struct VacuumCutoffs cutoffs)

Definition at line 1271 of file vacuum.c.

1272{
1273 TransactionId relfrozenxid = cutoffs->relfrozenxid;
1274 MultiXactId relminmxid = cutoffs->relminmxid;
1275 TransactionId xid_skip_limit;
1276 MultiXactId multi_skip_limit;
1277 int skip_index_vacuum;
1278
1279 Assert(TransactionIdIsNormal(relfrozenxid));
1280 Assert(MultiXactIdIsValid(relminmxid));
1281
1282 /*
1283 * Determine the index skipping age to use. In any case no less than
1284 * autovacuum_freeze_max_age * 1.05.
1285 */
1286 skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
1287
1288 xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
1289 if (!TransactionIdIsNormal(xid_skip_limit))
1290 xid_skip_limit = FirstNormalTransactionId;
1291
1292 if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
1293 {
1294 /* The table's relfrozenxid is too old */
1295 return true;
1296 }
1297
1298 /*
1299 * Similar to above, determine the index skipping age to use for
1300 * multixact. In any case no less than autovacuum_multixact_freeze_max_age *
1301 * 1.05.
1302 */
1303 skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
1305
1306 multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
1307 if (multi_skip_limit < FirstMultiXactId)
1308 multi_skip_limit = FirstMultiXactId;
1309
1310 if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
1311 {
1312 /* The table's relminmxid is too old */
1313 return true;
1314 }
1315
1316 return false;
1317}
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:130
int vacuum_multixact_failsafe_age
Definition: vacuum.c:79
int vacuum_failsafe_age
Definition: vacuum.c:78

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

1668{
1669 if (MyWorkerInfo)
1670 {
1673 else if (autovacuum_vac_cost_delay >= 0)
1675 else
1676 /* fall back to VacuumCostDelay */
1678
1680 }
1681 else
1682 {
1683 /* Must be explicit VACUUM or ANALYZE */
1686 }
1687
1688 /*
1689 * If configuration changes are allowed to impact VacuumCostActive, make
1690 * sure it is updated.
1691 */
1694 else if (vacuum_cost_delay > 0)
1695 VacuumCostActive = true;
1696 else
1697 {
1698 VacuumCostActive = false;
1700 }
1701
1702 /*
1703 * Since the cost logging requires a lock, avoid rendering the log message
1704 * in case we are using a message level where the log wouldn't be emitted.
1705 */
1707 {
1708 Oid dboid,
1709 tableoid;
1710
1711 Assert(!LWLockHeldByMe(AutovacuumLock));
1712
1713 LWLockAcquire(AutovacuumLock, LW_SHARED);
1714 dboid = MyWorkerInfo->wi_dboid;
1715 tableoid = MyWorkerInfo->wi_tableoid;
1716 LWLockRelease(AutovacuumLock);
1717
1718 elog(DEBUG2,
1719 "Autovacuum VacuumUpdateCosts(db=%u, rel=%u, dobalance=%s, cost_limit=%d, cost_delay=%g active=%s failsafe=%s)",
1720 dboid, tableoid, pg_atomic_unlocked_test_flag(&MyWorkerInfo->wi_dobalance) ? "no" : "yes",
1722 vacuum_cost_delay > 0 ? "yes" : "no",
1723 VacuumFailsafeActive ? "yes" : "no");
1724 }
1725}
static double av_storage_param_cost_delay
Definition: autovacuum.c:150
double autovacuum_vac_cost_delay
Definition: autovacuum.c:132
bool message_level_is_interesting(int elevel)
Definition: elog.c:273
double VacuumCostDelay
Definition: globals.c:155
bool LWLockHeldByMe(LWLock *lock)
Definition: lwlock.c:1977
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1174
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1894
@ LW_SHARED
Definition: lwlock.h:113

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

◆ parallel_vacuum_worker_delay_ns

PGDLLIMPORT int64 parallel_vacuum_worker_delay_ns
extern

Definition at line 94 of file vacuum.c.

Referenced by parallel_vacuum_main(), and vacuum_delay_point().

◆ track_cost_delay_timing

PGDLLIMPORT bool track_cost_delay_timing
extern

Definition at line 81 of file vacuum.c.

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

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

Referenced by vacuum_xid_failsafe_check().

◆ vacuum_freeze_min_age

PGDLLIMPORT int vacuum_freeze_min_age
extern

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

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_max_eager_freeze_failure_rate

PGDLLIMPORT double vacuum_max_eager_freeze_failure_rate
extern

Definition at line 80 of file vacuum.c.

Referenced by default_reloptions(), ExecVacuum(), and table_recheck_autovac().

◆ vacuum_multixact_failsafe_age

PGDLLIMPORT int vacuum_multixact_failsafe_age
extern

Definition at line 79 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 76 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 77 of file vacuum.c.

Referenced by do_autovacuum(), and vacuum_get_cutoffs().

◆ vacuum_truncate

PGDLLIMPORT bool vacuum_truncate
extern

Definition at line 82 of file vacuum.c.

Referenced by default_reloptions(), and vacuum_rel().

◆ VacuumActiveNWorkers

◆ VacuumCostBalanceLocal

PGDLLIMPORT int VacuumCostBalanceLocal
extern

◆ VacuumFailsafeActive

◆ VacuumSharedCostBalance