On Fri, Jun 20, 2025 at 11:05:37AM +0900, Michael Paquier wrote: > On Thu, Jun 19, 2025 at 03:20:27PM -0500, Nathan Bossart wrote: >> I think we need to do something like the following to fix this: >> >> * Teach autovacuum to combine the TOAST reloptions with the main relation's >> when processing TOAST tables (with the toast.* ones winning if both are >> set). >> >> * Teach autovacuum to resolve reloptions for parameters like >> vacuum_truncate instead of relying on vacuum_rel() to fill it in. > > These two points make sense here, yes. > >> * Have vacuum_rel() send the main relation's reloptions when recursing to >> the TOAST table so that we can combine them there, too. > > For the case of a manual VACUUM on the main table, where the TOAST > table is treated as a secondary citizen, that makes sense as well, > yes.
Here is a very rough proof-of-concept patch set for this. AFAICT there are a few options we cannot fix on the back-branches because there is no way to tell whether it is set or has just picked up the default. On v18 and newer, we could use isset_offset, but that doesn't exist on older versions. (I haven't looked closely, but I'm assuming that back-patching isset_offset isn't an option.) I would like to explore the "option 2" from upthread [0] for v19. I think that is a better long-term solution, and it may allow us to remove the table_toast_map in autovacuum. [0] https://postgr.es/m/aFl598epAdUrrv0y%40nathan -- nathan
>From a1930aa769a96c38621d7caa727c404d553b7ecc Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nat...@postgresql.org> Date: Mon, 23 Jun 2025 14:07:30 -0500 Subject: [PATCH v1 1/4] autovac: save all relopts instead of just avopts --- src/backend/postmaster/autovacuum.c | 119 ++++++++++------------------ 1 file changed, 43 insertions(+), 76 deletions(-) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 451fb90a610..f86c9fed853 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -192,8 +192,8 @@ typedef struct av_relation Oid ar_toastrelid; /* hash key - must be first */ Oid ar_relid; bool ar_hasrelopts; - AutoVacOpts ar_reloptions; /* copy of AutoVacOpts from the main table's - * reloptions, or NULL if none */ + StdRdOptions ar_reloptions; /* copy of main table's reloptions, or NULL if + * none */ } av_relation; /* struct to keep track of tables to vacuum and/or analyze, after rechecking */ @@ -333,11 +333,11 @@ static void FreeWorkerInfo(int code, Datum arg); static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map, TupleDesc pg_class_desc, int effective_multixact_freeze_max_age); -static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts, +static void recheck_relation_needs_vacanalyze(Oid relid, StdRdOptions *relopts, Form_pg_class classForm, int effective_multixact_freeze_max_age, bool *dovacuum, bool *doanalyze, bool *wraparound); -static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts, +static void relation_needs_vacanalyze(Oid relid, StdRdOptions *relopts, Form_pg_class classForm, PgStat_StatTabEntry *tabentry, int effective_multixact_freeze_max_age, @@ -345,8 +345,6 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts, static void autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy); -static AutoVacOpts *extract_autovac_opts(HeapTuple tup, - TupleDesc pg_class_desc); static void perform_work_item(AutoVacuumWorkItem *workitem); static void autovac_report_activity(autovac_table *tab); static void autovac_report_workitem(AutoVacuumWorkItem *workitem, @@ -1995,7 +1993,7 @@ do_autovacuum(void) { Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); PgStat_StatTabEntry *tabentry; - AutoVacOpts *relopts; + StdRdOptions *relopts; Oid relid; bool dovacuum; bool doanalyze; @@ -2033,7 +2031,7 @@ do_autovacuum(void) } /* Fetch reloptions and the pgstat entry for this table */ - relopts = extract_autovac_opts(tuple, pg_class_desc); + relopts = (StdRdOptions *) extractRelOptions(tuple, pg_class_desc, NULL); tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared, relid); @@ -2069,7 +2067,7 @@ do_autovacuum(void) { hentry->ar_hasrelopts = true; memcpy(&hentry->ar_reloptions, relopts, - sizeof(AutoVacOpts)); + sizeof(StdRdOptions)); } } } @@ -2095,7 +2093,7 @@ do_autovacuum(void) Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); PgStat_StatTabEntry *tabentry; Oid relid; - AutoVacOpts *relopts; + StdRdOptions *relopts; bool free_relopts = false; bool dovacuum; bool doanalyze; @@ -2113,7 +2111,7 @@ do_autovacuum(void) * fetch reloptions -- if this toast table does not have them, try the * main rel */ - relopts = extract_autovac_opts(tuple, pg_class_desc); + relopts = (StdRdOptions *) extractRelOptions(tuple, pg_class_desc, NULL); if (relopts) free_relopts = true; else @@ -2701,39 +2699,6 @@ deleted2: pfree(cur_relname); } -/* - * extract_autovac_opts - * - * Given a relation's pg_class tuple, return a palloc'd copy of the - * AutoVacOpts portion of reloptions, if set; otherwise, return NULL. - * - * Note: callers do not have a relation lock on the table at this point, - * so the table could have been dropped, and its catalog rows gone, after - * we acquired the pg_class row. If pg_class had a TOAST table, this would - * be a risk; fortunately, it doesn't. - */ -static AutoVacOpts * -extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) -{ - bytea *relopts; - AutoVacOpts *av; - - Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION || - ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW || - ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE); - - relopts = extractRelOptions(tup, pg_class_desc, NULL); - if (relopts == NULL) - return NULL; - - av = palloc(sizeof(AutoVacOpts)); - memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts)); - pfree(relopts); - - return av; -} - - /* * table_recheck_autovac * @@ -2753,8 +2718,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, bool doanalyze; autovac_table *tab = NULL; bool wraparound; - AutoVacOpts *avopts; - bool free_avopts = false; + StdRdOptions *relopts; + bool free_relopts = false; /* fetch the relation's relcache entry */ classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid)); @@ -2766,9 +2731,9 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, * Get the applicable reloptions. If it is a TOAST table, try to get the * main table reloptions if the toast table itself doesn't have. */ - avopts = extract_autovac_opts(classTup, pg_class_desc); - if (avopts) - free_avopts = true; + relopts = (StdRdOptions *) extractRelOptions(classTup, pg_class_desc, NULL); + if (relopts) + free_relopts = true; else if (classForm->relkind == RELKIND_TOASTVALUE && table_toast_map != NULL) { @@ -2777,10 +2742,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); if (found && hentry->ar_hasrelopts) - avopts = &hentry->ar_reloptions; + relopts = &hentry->ar_reloptions; } - recheck_relation_needs_vacanalyze(relid, avopts, classForm, + recheck_relation_needs_vacanalyze(relid, relopts, classForm, effective_multixact_freeze_max_age, &dovacuum, &doanalyze, &wraparound); @@ -2792,6 +2757,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, int multixact_freeze_min_age; int multixact_freeze_table_age; int log_min_duration; + AutoVacOpts *avopts = (relopts ? &relopts->autovacuum : NULL); /* * Calculate the vacuum cost parameters and the freeze ages. If there @@ -2879,8 +2845,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, avopts->vacuum_cost_delay >= 0)); } - if (free_avopts) - pfree(avopts); + if (free_relopts) + pfree(relopts); heap_freetuple(classTup); return tab; } @@ -2895,7 +2861,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, */ static void recheck_relation_needs_vacanalyze(Oid relid, - AutoVacOpts *avopts, + StdRdOptions *relopts, Form_pg_class classForm, int effective_multixact_freeze_max_age, bool *dovacuum, @@ -2908,7 +2874,7 @@ recheck_relation_needs_vacanalyze(Oid relid, tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared, relid); - relation_needs_vacanalyze(relid, avopts, classForm, tabentry, + relation_needs_vacanalyze(relid, relopts, classForm, tabentry, effective_multixact_freeze_max_age, dovacuum, doanalyze, wraparound); @@ -2928,7 +2894,7 @@ recheck_relation_needs_vacanalyze(Oid relid, * "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is * being forced because of Xid or multixact wraparound. * - * relopts is a pointer to the AutoVacOpts options (either for itself in the + * relopts is a pointer to the StdRdOptions options (either for itself in the * case of a plain table, or for either itself or its parent table in the case * of a TOAST table), NULL if none; tabentry is the pgstats entry, which can be * NULL. @@ -2962,7 +2928,7 @@ recheck_relation_needs_vacanalyze(Oid relid, */ static void relation_needs_vacanalyze(Oid relid, - AutoVacOpts *relopts, + StdRdOptions *relopts, Form_pg_class classForm, PgStat_StatTabEntry *tabentry, int effective_multixact_freeze_max_age, @@ -2973,6 +2939,7 @@ relation_needs_vacanalyze(Oid relid, { bool force_vacuum; bool av_enabled; + AutoVacOpts *avopts = (relopts ? &relopts->autovacuum : NULL); /* constants from reloptions or GUC variables */ int vac_base_thresh, @@ -3010,45 +2977,45 @@ relation_needs_vacanalyze(Oid relid, */ /* -1 in autovac setting means use plain vacuum_scale_factor */ - vac_scale_factor = (relopts && relopts->vacuum_scale_factor >= 0) - ? relopts->vacuum_scale_factor + vac_scale_factor = (avopts && avopts->vacuum_scale_factor >= 0) + ? avopts->vacuum_scale_factor : autovacuum_vac_scale; - vac_base_thresh = (relopts && relopts->vacuum_threshold >= 0) - ? relopts->vacuum_threshold + vac_base_thresh = (avopts && avopts->vacuum_threshold >= 0) + ? avopts->vacuum_threshold : autovacuum_vac_thresh; /* -1 is used to disable max threshold */ - vac_max_thresh = (relopts && relopts->vacuum_max_threshold >= -1) - ? relopts->vacuum_max_threshold + vac_max_thresh = (avopts && avopts->vacuum_max_threshold >= -1) + ? avopts->vacuum_max_threshold : autovacuum_vac_max_thresh; - vac_ins_scale_factor = (relopts && relopts->vacuum_ins_scale_factor >= 0) - ? relopts->vacuum_ins_scale_factor + vac_ins_scale_factor = (avopts && avopts->vacuum_ins_scale_factor >= 0) + ? avopts->vacuum_ins_scale_factor : autovacuum_vac_ins_scale; /* -1 is used to disable insert vacuums */ - vac_ins_base_thresh = (relopts && relopts->vacuum_ins_threshold >= -1) - ? relopts->vacuum_ins_threshold + vac_ins_base_thresh = (avopts && avopts->vacuum_ins_threshold >= -1) + ? avopts->vacuum_ins_threshold : autovacuum_vac_ins_thresh; - anl_scale_factor = (relopts && relopts->analyze_scale_factor >= 0) - ? relopts->analyze_scale_factor + anl_scale_factor = (avopts && avopts->analyze_scale_factor >= 0) + ? avopts->analyze_scale_factor : autovacuum_anl_scale; - anl_base_thresh = (relopts && relopts->analyze_threshold >= 0) - ? relopts->analyze_threshold + anl_base_thresh = (avopts && avopts->analyze_threshold >= 0) + ? avopts->analyze_threshold : autovacuum_anl_thresh; - freeze_max_age = (relopts && relopts->freeze_max_age >= 0) - ? Min(relopts->freeze_max_age, autovacuum_freeze_max_age) + freeze_max_age = (avopts && avopts->freeze_max_age >= 0) + ? Min(avopts->freeze_max_age, autovacuum_freeze_max_age) : autovacuum_freeze_max_age; - multixact_freeze_max_age = (relopts && relopts->multixact_freeze_max_age >= 0) - ? Min(relopts->multixact_freeze_max_age, effective_multixact_freeze_max_age) + multixact_freeze_max_age = (avopts && avopts->multixact_freeze_max_age >= 0) + ? Min(avopts->multixact_freeze_max_age, effective_multixact_freeze_max_age) : effective_multixact_freeze_max_age; - av_enabled = (relopts ? relopts->enabled : true); + av_enabled = (avopts ? avopts->enabled : true); /* Force vacuum if table is at risk of wraparound */ xidForceLimit = recentXid - freeze_max_age; -- 2.39.5 (Apple Git-154)
>From a3bbb1fdcf6a66719af84b763861e187cc5588bd Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nat...@postgresql.org> Date: Mon, 23 Jun 2025 14:42:39 -0500 Subject: [PATCH v1 2/4] autovac: resolve relopts before vacuuming --- src/backend/commands/vacuum.c | 7 ++-- src/backend/postmaster/autovacuum.c | 52 +++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 33a33bf6b1c..0015b9ef4b0 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -421,7 +421,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) /* * Later, in vacuum_rel(), we check if a reloption override was specified. */ - params.max_eager_freeze_failure_rate = vacuum_max_eager_freeze_failure_rate; + params.max_eager_freeze_failure_rate = -1.0; /* * Create special memory context for cross-transaction storage. @@ -2195,10 +2195,13 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, * Check if the vacuum_max_eager_freeze_failure_rate table storage * parameter was specified. This overrides the GUC value. */ - if (rel->rd_options != NULL && + if (params->max_eager_freeze_failure_rate < 0 && + rel->rd_options != NULL && ((StdRdOptions *) rel->rd_options)->vacuum_max_eager_freeze_failure_rate >= 0) params->max_eager_freeze_failure_rate = ((StdRdOptions *) rel->rd_options)->vacuum_max_eager_freeze_failure_rate; + else + params->max_eager_freeze_failure_rate = vacuum_max_eager_freeze_failure_rate; /* * Set truncate option based on truncate reloption or GUC if it wasn't diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index f86c9fed853..3bedca971ff 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2758,6 +2758,9 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, int multixact_freeze_table_age; int log_min_duration; AutoVacOpts *avopts = (relopts ? &relopts->autovacuum : NULL); + VacOptValue index_cleanup; + VacOptValue truncate; + double max_eager_freeze_failure_rate; /* * Calculate the vacuum cost parameters and the freeze ages. If there @@ -2790,6 +2793,39 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, ? avopts->multixact_freeze_table_age : default_multixact_freeze_table_age; + if (relopts) + { + switch (relopts->vacuum_index_cleanup) + { + case STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO: + index_cleanup = VACOPTVALUE_AUTO; + break; + case STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON: + index_cleanup = VACOPTVALUE_ENABLED; + break; + case STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF: + index_cleanup = VACOPTVALUE_DISABLED; + break; + } + } + else + index_cleanup = VACOPTVALUE_UNSPECIFIED; + + if (relopts && relopts->vacuum_truncate_set) + { + if (relopts->vacuum_truncate) + truncate = VACOPTVALUE_ENABLED; + else + truncate = VACOPTVALUE_DISABLED; + } + else + truncate = VACOPTVALUE_UNSPECIFIED; + + if (relopts && relopts->vacuum_max_eager_freeze_failure_rate >= 0) + max_eager_freeze_failure_rate = relopts->vacuum_max_eager_freeze_failure_rate; + else + max_eager_freeze_failure_rate = -1.0; + tab = palloc(sizeof(autovac_table)); tab->at_relid = relid; tab->at_sharedrel = classForm->relisshared; @@ -2806,13 +2842,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, (doanalyze ? VACOPT_ANALYZE : 0) | (!wraparound ? VACOPT_SKIP_LOCKED : 0); - /* - * index_cleanup and truncate are unspecified at first in autovacuum. - * They will be filled in with usable values using their reloptions - * (or reloption defaults) later. - */ - tab->at_params.index_cleanup = VACOPTVALUE_UNSPECIFIED; - tab->at_params.truncate = VACOPTVALUE_UNSPECIFIED; + tab->at_params.index_cleanup = index_cleanup; + tab->at_params.truncate = truncate; /* As of now, we don't support parallel vacuum for autovacuum */ tab->at_params.nworkers = -1; tab->at_params.freeze_min_age = freeze_min_age; @@ -2822,12 +2853,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, tab->at_params.is_wraparound = wraparound; tab->at_params.log_min_duration = log_min_duration; tab->at_params.toast_parent = InvalidOid; - - /* - * Later, in vacuum_rel(), we check reloptions for any - * vacuum_max_eager_freeze_failure_rate override. - */ - tab->at_params.max_eager_freeze_failure_rate = vacuum_max_eager_freeze_failure_rate; + tab->at_params.max_eager_freeze_failure_rate = max_eager_freeze_failure_rate; tab->at_storage_param_vac_cost_limit = avopts ? avopts->vacuum_cost_limit : 0; tab->at_storage_param_vac_cost_delay = avopts ? -- 2.39.5 (Apple Git-154)
>From 12b6404c906837c9244110095ca980ad3545bc0b Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nat...@postgresql.org> Date: Mon, 23 Jun 2025 15:16:38 -0500 Subject: [PATCH v1 3/4] autovac: combine reloptions correctly --- src/backend/postmaster/autovacuum.c | 85 +++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 3bedca971ff..0d1f4e38b58 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -1873,6 +1873,73 @@ get_database_list(void) return dblist; } +static void +combine_relopts(StdRdOptions *toast_opts, const StdRdOptions *main_opts) +{ + AutoVacOpts *toast_avopts = &toast_opts->autovacuum; + const AutoVacOpts *main_avopts = &main_opts->autovacuum; + + /* XXX: need isset_offset to combine "enabled" */ + + if (toast_avopts->vacuum_threshold == -1) + toast_avopts->vacuum_threshold = main_avopts->vacuum_threshold; + + if (toast_avopts->vacuum_max_threshold == -2) + toast_avopts->vacuum_max_threshold = main_avopts->vacuum_max_threshold; + + if (toast_avopts->vacuum_ins_threshold == -2) + toast_avopts->vacuum_ins_threshold = main_avopts->vacuum_ins_threshold; + + if (toast_avopts->analyze_threshold == -1) + toast_avopts->analyze_threshold = main_avopts->analyze_threshold; + + if (toast_avopts->vacuum_cost_limit == -1) + toast_avopts->vacuum_cost_limit = main_avopts->vacuum_cost_limit; + + if (toast_avopts->freeze_min_age == -1) + toast_avopts->freeze_min_age = main_avopts->freeze_min_age; + + if (toast_avopts->freeze_max_age == -1) + toast_avopts->freeze_max_age = main_avopts->freeze_max_age; + + if (toast_avopts->freeze_table_age == -1) + toast_avopts->freeze_table_age = main_avopts->freeze_table_age; + + if (toast_avopts->multixact_freeze_min_age == -1) + toast_avopts->multixact_freeze_min_age = main_avopts->multixact_freeze_min_age; + + if (toast_avopts->multixact_freeze_max_age == -1) + toast_avopts->multixact_freeze_max_age = main_avopts->multixact_freeze_max_age; + + if (toast_avopts->multixact_freeze_table_age == -1) + toast_avopts->multixact_freeze_table_age = main_avopts->multixact_freeze_table_age; + + /* XXX: need isset_offset for log_min_duration */ + + if (toast_avopts->vacuum_cost_delay == -1) + toast_avopts->vacuum_cost_delay = main_avopts->vacuum_cost_delay; + + if (toast_avopts->vacuum_scale_factor == -1) + toast_avopts->vacuum_scale_factor = main_avopts->vacuum_scale_factor; + + if (toast_avopts->vacuum_ins_scale_factor == -1) + toast_avopts->vacuum_ins_scale_factor = main_avopts->vacuum_ins_scale_factor; + + if (toast_avopts->analyze_scale_factor == -1) + toast_avopts->analyze_scale_factor = main_avopts->analyze_scale_factor; + + /* XXX: need isset_offset for vacuum_index_cleanup */ + + if (!toast_opts->vacuum_truncate_set && main_opts->vacuum_truncate_set) + { + toast_opts->vacuum_truncate = main_opts->vacuum_truncate; + toast_opts->vacuum_truncate_set = true; + } + + if (toast_opts->vacuum_max_eager_freeze_failure_rate == -1) + toast_opts->vacuum_max_eager_freeze_failure_rate = main_opts->vacuum_max_eager_freeze_failure_rate; +} + /* * Process a database table-by-table * @@ -2113,7 +2180,16 @@ do_autovacuum(void) */ relopts = (StdRdOptions *) extractRelOptions(tuple, pg_class_desc, NULL); if (relopts) + { + av_relation *hentry; + bool found; + free_relopts = true; + + hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); + if (found && hentry->ar_hasrelopts) + combine_relopts(relopts, &hentry->ar_reloptions); + } else { av_relation *hentry; @@ -2733,7 +2809,16 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, */ relopts = (StdRdOptions *) extractRelOptions(classTup, pg_class_desc, NULL); if (relopts) + { + av_relation *hentry; + bool found; + free_relopts = true; + + hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); + if (found && hentry->ar_hasrelopts) + combine_relopts(relopts, &hentry->ar_reloptions); + } else if (classForm->relkind == RELKIND_TOASTVALUE && table_toast_map != NULL) { -- 2.39.5 (Apple Git-154)
>From 724f90f4d19a2c92026adffcde8bf47967378f1e Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nat...@postgresql.org> Date: Mon, 23 Jun 2025 15:40:28 -0500 Subject: [PATCH v1 4/4] combine relopts correctly for VACUUM commands --- src/backend/commands/vacuum.c | 30 +++++++++++++++++++---------- src/backend/postmaster/autovacuum.c | 2 +- src/include/commands/vacuum.h | 2 ++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 0015b9ef4b0..a74c3a9e2a2 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -124,7 +124,7 @@ static void vac_truncate_clog(TransactionId frozenXID, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti); static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, - BufferAccessStrategy bstrategy); + BufferAccessStrategy bstrategy, StdRdOptions *main_opts); static double compute_parallel_delay(void); static VacOptValue get_vacoptval_from_boolean(DefElem *def); static bool vac_tid_reaped(ItemPointer itemptr, void *state); @@ -634,7 +634,7 @@ vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy, if (params->options & VACOPT_VACUUM) { - if (!vacuum_rel(vrel->oid, vrel->relation, params, bstrategy)) + if (!vacuum_rel(vrel->oid, vrel->relation, params, bstrategy, NULL)) continue; } @@ -1998,7 +1998,7 @@ vac_truncate_clog(TransactionId frozenXID, */ static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, - BufferAccessStrategy bstrategy) + BufferAccessStrategy bstrategy, StdRdOptions *main_opts) { LOCKMODE lmode; Relation rel; @@ -2008,6 +2008,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, Oid save_userid; int save_sec_context; int save_nestlevel; + StdRdOptions saved_opts; + StdRdOptions combined_opts; Assert(params != NULL); @@ -2165,6 +2167,15 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, lockrelid = rel->rd_lockInfo.lockRelId; LockRelationIdForSession(&lockrelid, lmode); + if (rel->rd_options) + { + memcpy(&saved_opts, rel->rd_options, sizeof(StdRdOptions)); + memcpy(&combined_opts, rel->rd_options, sizeof(StdRdOptions)); + + if (main_opts) + combine_relopts(&combined_opts, main_opts); + } + /* * Set index_cleanup option based on index_cleanup reloption if it wasn't * specified in VACUUM command, or when running in an autovacuum worker @@ -2176,8 +2187,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, if (rel->rd_options == NULL) vacuum_index_cleanup = STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO; else - vacuum_index_cleanup = - ((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup; + vacuum_index_cleanup = combined_opts.vacuum_index_cleanup; if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO) params->index_cleanup = VACOPTVALUE_AUTO; @@ -2197,9 +2207,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, */ if (params->max_eager_freeze_failure_rate < 0 && rel->rd_options != NULL && - ((StdRdOptions *) rel->rd_options)->vacuum_max_eager_freeze_failure_rate >= 0) + combined_opts.vacuum_max_eager_freeze_failure_rate >= 0) params->max_eager_freeze_failure_rate = - ((StdRdOptions *) rel->rd_options)->vacuum_max_eager_freeze_failure_rate; + combined_opts.vacuum_max_eager_freeze_failure_rate; else params->max_eager_freeze_failure_rate = vacuum_max_eager_freeze_failure_rate; @@ -2211,9 +2221,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, { StdRdOptions *opts = (StdRdOptions *) rel->rd_options; - if (opts && opts->vacuum_truncate_set) + if (opts && combined_opts.vacuum_truncate_set) { - if (opts->vacuum_truncate) + if (combined_opts.vacuum_truncate) params->truncate = VACOPTVALUE_ENABLED; else params->truncate = VACOPTVALUE_DISABLED; @@ -2314,7 +2324,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, toast_vacuum_params.options |= VACOPT_PROCESS_MAIN; toast_vacuum_params.toast_parent = relid; - vacuum_rel(toast_relid, NULL, &toast_vacuum_params, bstrategy); + vacuum_rel(toast_relid, NULL, &toast_vacuum_params, bstrategy, &saved_opts); } /* diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 0d1f4e38b58..ca399b6a92e 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -1873,7 +1873,7 @@ get_database_list(void) return dblist; } -static void +void combine_relopts(StdRdOptions *toast_opts, const StdRdOptions *main_opts) { AutoVacOpts *toast_avopts = &toast_opts->autovacuum; diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index bc37a80dc74..928f864581b 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -24,6 +24,7 @@ #include "parser/parse_node.h" #include "storage/buf.h" #include "storage/lock.h" +#include "utils/rel.h" #include "utils/relcache.h" /* @@ -377,6 +378,7 @@ extern IndexBulkDeleteResult *vac_cleanup_one_index(IndexVacuumInfo *ivinfo, /* In postmaster/autovacuum.c */ extern void AutoVacuumUpdateCostLimit(void); extern void VacuumUpdateCosts(void); +extern void combine_relopts(StdRdOptions *toast_opts, const StdRdOptions *main_opts); /* in commands/vacuumparallel.c */ extern ParallelVacuumState *parallel_vacuum_init(Relation rel, Relation *indrels, -- 2.39.5 (Apple Git-154)