agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v1 1/1] Use C99-designated initializer syntax for dispatch_table array
798+ messages / 2 participants
[nested] [flat]

* [PATCH v3 1/1] Use C99-designated initializer syntax for dispatch_table array
@ 2024-03-05 13:32 Japin Li <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Japin Li @ 2024-03-05 13:32 UTC (permalink / raw)

---
 src/backend/executor/execExprInterp.c | 203 ++++++++++++--------------
 src/include/executor/execExpr.h       |   2 +-
 2 files changed, 97 insertions(+), 108 deletions(-)

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 7c1f51e2e0..df9fe79058 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -400,110 +400,106 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 	TupleTableSlot *outerslot;
 	TupleTableSlot *scanslot;
 
-	/*
-	 * This array has to be in the same order as enum ExprEvalOp.
-	 */
 #if defined(EEO_USE_COMPUTED_GOTO)
 	static const void *const dispatch_table[] = {
-		&&CASE_EEOP_DONE,
-		&&CASE_EEOP_INNER_FETCHSOME,
-		&&CASE_EEOP_OUTER_FETCHSOME,
-		&&CASE_EEOP_SCAN_FETCHSOME,
-		&&CASE_EEOP_INNER_VAR,
-		&&CASE_EEOP_OUTER_VAR,
-		&&CASE_EEOP_SCAN_VAR,
-		&&CASE_EEOP_INNER_SYSVAR,
-		&&CASE_EEOP_OUTER_SYSVAR,
-		&&CASE_EEOP_SCAN_SYSVAR,
-		&&CASE_EEOP_WHOLEROW,
-		&&CASE_EEOP_ASSIGN_INNER_VAR,
-		&&CASE_EEOP_ASSIGN_OUTER_VAR,
-		&&CASE_EEOP_ASSIGN_SCAN_VAR,
-		&&CASE_EEOP_ASSIGN_TMP,
-		&&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
-		&&CASE_EEOP_CONST,
-		&&CASE_EEOP_FUNCEXPR,
-		&&CASE_EEOP_FUNCEXPR_STRICT,
-		&&CASE_EEOP_FUNCEXPR_FUSAGE,
-		&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
-		&&CASE_EEOP_BOOL_AND_STEP_FIRST,
-		&&CASE_EEOP_BOOL_AND_STEP,
-		&&CASE_EEOP_BOOL_AND_STEP_LAST,
-		&&CASE_EEOP_BOOL_OR_STEP_FIRST,
-		&&CASE_EEOP_BOOL_OR_STEP,
-		&&CASE_EEOP_BOOL_OR_STEP_LAST,
-		&&CASE_EEOP_BOOL_NOT_STEP,
-		&&CASE_EEOP_QUAL,
-		&&CASE_EEOP_JUMP,
-		&&CASE_EEOP_JUMP_IF_NULL,
-		&&CASE_EEOP_JUMP_IF_NOT_NULL,
-		&&CASE_EEOP_JUMP_IF_NOT_TRUE,
-		&&CASE_EEOP_NULLTEST_ISNULL,
-		&&CASE_EEOP_NULLTEST_ISNOTNULL,
-		&&CASE_EEOP_NULLTEST_ROWISNULL,
-		&&CASE_EEOP_NULLTEST_ROWISNOTNULL,
-		&&CASE_EEOP_BOOLTEST_IS_TRUE,
-		&&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
-		&&CASE_EEOP_BOOLTEST_IS_FALSE,
-		&&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
-		&&CASE_EEOP_PARAM_EXEC,
-		&&CASE_EEOP_PARAM_EXTERN,
-		&&CASE_EEOP_PARAM_CALLBACK,
-		&&CASE_EEOP_CASE_TESTVAL,
-		&&CASE_EEOP_MAKE_READONLY,
-		&&CASE_EEOP_IOCOERCE,
-		&&CASE_EEOP_IOCOERCE_SAFE,
-		&&CASE_EEOP_DISTINCT,
-		&&CASE_EEOP_NOT_DISTINCT,
-		&&CASE_EEOP_NULLIF,
-		&&CASE_EEOP_SQLVALUEFUNCTION,
-		&&CASE_EEOP_CURRENTOFEXPR,
-		&&CASE_EEOP_NEXTVALUEEXPR,
-		&&CASE_EEOP_ARRAYEXPR,
-		&&CASE_EEOP_ARRAYCOERCE,
-		&&CASE_EEOP_ROW,
-		&&CASE_EEOP_ROWCOMPARE_STEP,
-		&&CASE_EEOP_ROWCOMPARE_FINAL,
-		&&CASE_EEOP_MINMAX,
-		&&CASE_EEOP_FIELDSELECT,
-		&&CASE_EEOP_FIELDSTORE_DEFORM,
-		&&CASE_EEOP_FIELDSTORE_FORM,
-		&&CASE_EEOP_SBSREF_SUBSCRIPTS,
-		&&CASE_EEOP_SBSREF_OLD,
-		&&CASE_EEOP_SBSREF_ASSIGN,
-		&&CASE_EEOP_SBSREF_FETCH,
-		&&CASE_EEOP_DOMAIN_TESTVAL,
-		&&CASE_EEOP_DOMAIN_NOTNULL,
-		&&CASE_EEOP_DOMAIN_CHECK,
-		&&CASE_EEOP_CONVERT_ROWTYPE,
-		&&CASE_EEOP_SCALARARRAYOP,
-		&&CASE_EEOP_HASHED_SCALARARRAYOP,
-		&&CASE_EEOP_XMLEXPR,
-		&&CASE_EEOP_JSON_CONSTRUCTOR,
-		&&CASE_EEOP_IS_JSON,
-		&&CASE_EEOP_AGGREF,
-		&&CASE_EEOP_GROUPING_FUNC,
-		&&CASE_EEOP_WINDOW_FUNC,
-		&&CASE_EEOP_SUBPLAN,
-		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
-		&&CASE_EEOP_AGG_DESERIALIZE,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
-		&&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
-		&&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
-		&&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
-		&&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
-		&&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
-		&&CASE_EEOP_LAST
+		[EEOP_DONE] = &&CASE_EEOP_DONE,
+		[EEOP_INNER_FETCHSOME] = &&CASE_EEOP_INNER_FETCHSOME,
+		[EEOP_OUTER_FETCHSOME] = &&CASE_EEOP_OUTER_FETCHSOME,
+		[EEOP_SCAN_FETCHSOME] = &&CASE_EEOP_SCAN_FETCHSOME,
+		[EEOP_INNER_VAR] = &&CASE_EEOP_INNER_VAR,
+		[EEOP_OUTER_VAR] = &&CASE_EEOP_OUTER_VAR,
+		[EEOP_SCAN_VAR] = &&CASE_EEOP_SCAN_VAR,
+		[EEOP_INNER_SYSVAR] = &&CASE_EEOP_INNER_SYSVAR,
+		[EEOP_OUTER_SYSVAR] = &&CASE_EEOP_OUTER_SYSVAR,
+		[EEOP_SCAN_SYSVAR] = &&CASE_EEOP_SCAN_SYSVAR,
+		[EEOP_WHOLEROW] = &&CASE_EEOP_WHOLEROW,
+		[EEOP_ASSIGN_INNER_VAR] = &&CASE_EEOP_ASSIGN_INNER_VAR,
+		[EEOP_ASSIGN_OUTER_VAR] = &&CASE_EEOP_ASSIGN_OUTER_VAR,
+		[EEOP_ASSIGN_SCAN_VAR] = &&CASE_EEOP_ASSIGN_SCAN_VAR,
+		[EEOP_ASSIGN_TMP] = &&CASE_EEOP_ASSIGN_TMP,
+		[EEOP_ASSIGN_TMP_MAKE_RO] = &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
+		[EEOP_CONST] = &&CASE_EEOP_CONST,
+		[EEOP_FUNCEXPR] = &&CASE_EEOP_FUNCEXPR,
+		[EEOP_FUNCEXPR_STRICT] = &&CASE_EEOP_FUNCEXPR_STRICT,
+		[EEOP_FUNCEXPR_FUSAGE] = &&CASE_EEOP_FUNCEXPR_FUSAGE,
+		[EEOP_FUNCEXPR_STRICT_FUSAGE] = &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+		[EEOP_BOOL_AND_STEP_FIRST] = &&CASE_EEOP_BOOL_AND_STEP_FIRST,
+		[EEOP_BOOL_AND_STEP] = &&CASE_EEOP_BOOL_AND_STEP,
+		[EEOP_BOOL_AND_STEP_LAST] = &&CASE_EEOP_BOOL_AND_STEP_LAST,
+		[EEOP_BOOL_OR_STEP_FIRST] = &&CASE_EEOP_BOOL_OR_STEP_FIRST,
+		[EEOP_BOOL_OR_STEP] = &&CASE_EEOP_BOOL_OR_STEP,
+		[EEOP_BOOL_OR_STEP_LAST] = &&CASE_EEOP_BOOL_OR_STEP_LAST,
+		[EEOP_BOOL_NOT_STEP] = &&CASE_EEOP_BOOL_NOT_STEP,
+		[EEOP_QUAL] = &&CASE_EEOP_QUAL,
+		[EEOP_JUMP] = &&CASE_EEOP_JUMP,
+		[EEOP_JUMP_IF_NULL] = &&CASE_EEOP_JUMP_IF_NULL,
+		[EEOP_JUMP_IF_NOT_NULL] = &&CASE_EEOP_JUMP_IF_NOT_NULL,
+		[EEOP_JUMP_IF_NOT_TRUE] = &&CASE_EEOP_JUMP_IF_NOT_TRUE,
+		[EEOP_NULLTEST_ISNULL] = &&CASE_EEOP_NULLTEST_ISNULL,
+		[EEOP_NULLTEST_ISNOTNULL] = &&CASE_EEOP_NULLTEST_ISNOTNULL,
+		[EEOP_NULLTEST_ROWISNULL] = &&CASE_EEOP_NULLTEST_ROWISNULL,
+		[EEOP_NULLTEST_ROWISNOTNULL] = &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
+		[EEOP_BOOLTEST_IS_TRUE] = &&CASE_EEOP_BOOLTEST_IS_TRUE,
+		[EEOP_BOOLTEST_IS_NOT_TRUE] = &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
+		[EEOP_BOOLTEST_IS_FALSE] = &&CASE_EEOP_BOOLTEST_IS_FALSE,
+		[EEOP_BOOLTEST_IS_NOT_FALSE] = &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
+		[EEOP_PARAM_EXEC] = &&CASE_EEOP_PARAM_EXEC,
+		[EEOP_PARAM_EXTERN] = &&CASE_EEOP_PARAM_EXTERN,
+		[EEOP_PARAM_CALLBACK] = &&CASE_EEOP_PARAM_CALLBACK,
+		[EEOP_CASE_TESTVAL] = &&CASE_EEOP_CASE_TESTVAL,
+		[EEOP_MAKE_READONLY] = &&CASE_EEOP_MAKE_READONLY,
+		[EEOP_IOCOERCE] = &&CASE_EEOP_IOCOERCE,
+		[EEOP_IOCOERCE_SAFE] = &&CASE_EEOP_IOCOERCE_SAFE,
+		[EEOP_DISTINCT] = &&CASE_EEOP_DISTINCT,
+		[EEOP_NOT_DISTINCT] = &&CASE_EEOP_NOT_DISTINCT,
+		[EEOP_NULLIF] = &&CASE_EEOP_NULLIF,
+		[EEOP_SQLVALUEFUNCTION] = &&CASE_EEOP_SQLVALUEFUNCTION,
+		[EEOP_CURRENTOFEXPR] = &&CASE_EEOP_CURRENTOFEXPR,
+		[EEOP_NEXTVALUEEXPR] = &&CASE_EEOP_NEXTVALUEEXPR,
+		[EEOP_ARRAYEXPR] = &&CASE_EEOP_ARRAYEXPR,
+		[EEOP_ARRAYCOERCE] = &&CASE_EEOP_ARRAYCOERCE,
+		[EEOP_ROW] = &&CASE_EEOP_ROW,
+		[EEOP_ROWCOMPARE_STEP] = &&CASE_EEOP_ROWCOMPARE_STEP,
+		[EEOP_ROWCOMPARE_FINAL] = &&CASE_EEOP_ROWCOMPARE_FINAL,
+		[EEOP_MINMAX] = &&CASE_EEOP_MINMAX,
+		[EEOP_FIELDSELECT] = &&CASE_EEOP_FIELDSELECT,
+		[EEOP_FIELDSTORE_DEFORM] = &&CASE_EEOP_FIELDSTORE_DEFORM,
+		[EEOP_FIELDSTORE_FORM] = &&CASE_EEOP_FIELDSTORE_FORM,
+		[EEOP_SBSREF_SUBSCRIPTS] = &&CASE_EEOP_SBSREF_SUBSCRIPTS,
+		[EEOP_SBSREF_OLD] = &&CASE_EEOP_SBSREF_OLD,
+		[EEOP_SBSREF_ASSIGN] = &&CASE_EEOP_SBSREF_ASSIGN,
+		[EEOP_SBSREF_FETCH] = &&CASE_EEOP_SBSREF_FETCH,
+		[EEOP_DOMAIN_TESTVAL] = &&CASE_EEOP_DOMAIN_TESTVAL,
+		[EEOP_DOMAIN_NOTNULL] = &&CASE_EEOP_DOMAIN_NOTNULL,
+		[EEOP_DOMAIN_CHECK] = &&CASE_EEOP_DOMAIN_CHECK,
+		[EEOP_CONVERT_ROWTYPE] = &&CASE_EEOP_CONVERT_ROWTYPE,
+		[EEOP_SCALARARRAYOP] = &&CASE_EEOP_SCALARARRAYOP,
+		[EEOP_HASHED_SCALARARRAYOP] = &&CASE_EEOP_HASHED_SCALARARRAYOP,
+		[EEOP_XMLEXPR] = &&CASE_EEOP_XMLEXPR,
+		[EEOP_JSON_CONSTRUCTOR] = &&CASE_EEOP_JSON_CONSTRUCTOR,
+		[EEOP_IS_JSON] = &&CASE_EEOP_IS_JSON,
+		[EEOP_AGGREF] = &&CASE_EEOP_AGGREF,
+		[EEOP_GROUPING_FUNC] = &&CASE_EEOP_GROUPING_FUNC,
+		[EEOP_WINDOW_FUNC] = &&CASE_EEOP_WINDOW_FUNC,
+		[EEOP_SUBPLAN] = &&CASE_EEOP_SUBPLAN,
+		[EEOP_AGG_STRICT_DESERIALIZE] = &&CASE_EEOP_AGG_STRICT_DESERIALIZE,
+		[EEOP_AGG_DESERIALIZE] = &&CASE_EEOP_AGG_DESERIALIZE,
+		[EEOP_AGG_STRICT_INPUT_CHECK_ARGS] = &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		[EEOP_AGG_STRICT_INPUT_CHECK_NULLS] = &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
+		[EEOP_AGG_PLAIN_PERGROUP_NULLCHECK] = &&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
+		[EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
+		[EEOP_AGG_PLAIN_TRANS_STRICT_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
+		[EEOP_AGG_PLAIN_TRANS_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
+		[EEOP_AGG_PRESORTED_DISTINCT_SINGLE] = &&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
+		[EEOP_AGG_PRESORTED_DISTINCT_MULTI] = &&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
+		[EEOP_AGG_ORDERED_TRANS_DATUM] = &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
+		[EEOP_AGG_ORDERED_TRANS_TUPLE] = &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
 	};
 
-	StaticAssertDecl(lengthof(dispatch_table) == EEOP_LAST + 1,
+	StaticAssertDecl(lengthof(dispatch_table) == EEOP_LAST,
 					 "dispatch_table out of whack with ExprEvalOp");
 
 	if (unlikely(state == NULL))
@@ -1845,13 +1841,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 			EEO_NEXT();
 		}
-
-		EEO_CASE(EEOP_LAST)
-		{
-			/* unreachable */
-			Assert(false);
-			goto out;
-		}
 	}
 
 out:
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a28ddcdd77..00d3db7a74 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -60,7 +60,7 @@ typedef struct ExprEvalRowtypeCache
  * Identifies the operation to be executed and which member in the
  * ExprEvalStep->d union is valid.
  *
- * The order of entries needs to be kept in sync with the dispatch_table[]
+ * If you add some operation don't forget to update the dispatch_table[]
  * array in execExprInterp.c:ExecInterpExpr().
  */
 typedef enum ExprEvalOp
-- 
2.34.1


--=-=-=--





^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/1] Use C99-designated initializer syntax for dispatch_table array
@ 2024-03-05 13:32 Japin Li <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Japin Li @ 2024-03-05 13:32 UTC (permalink / raw)

---
 src/backend/executor/execExprInterp.c | 195 +++++++++++++-------------
 src/include/executor/execExpr.h       |   3 -
 2 files changed, 96 insertions(+), 102 deletions(-)

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 7c1f51e2e0..e4ad522312 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -400,107 +400,104 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 	TupleTableSlot *outerslot;
 	TupleTableSlot *scanslot;
 
-	/*
-	 * This array has to be in the same order as enum ExprEvalOp.
-	 */
 #if defined(EEO_USE_COMPUTED_GOTO)
 	static const void *const dispatch_table[] = {
-		&&CASE_EEOP_DONE,
-		&&CASE_EEOP_INNER_FETCHSOME,
-		&&CASE_EEOP_OUTER_FETCHSOME,
-		&&CASE_EEOP_SCAN_FETCHSOME,
-		&&CASE_EEOP_INNER_VAR,
-		&&CASE_EEOP_OUTER_VAR,
-		&&CASE_EEOP_SCAN_VAR,
-		&&CASE_EEOP_INNER_SYSVAR,
-		&&CASE_EEOP_OUTER_SYSVAR,
-		&&CASE_EEOP_SCAN_SYSVAR,
-		&&CASE_EEOP_WHOLEROW,
-		&&CASE_EEOP_ASSIGN_INNER_VAR,
-		&&CASE_EEOP_ASSIGN_OUTER_VAR,
-		&&CASE_EEOP_ASSIGN_SCAN_VAR,
-		&&CASE_EEOP_ASSIGN_TMP,
-		&&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
-		&&CASE_EEOP_CONST,
-		&&CASE_EEOP_FUNCEXPR,
-		&&CASE_EEOP_FUNCEXPR_STRICT,
-		&&CASE_EEOP_FUNCEXPR_FUSAGE,
-		&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
-		&&CASE_EEOP_BOOL_AND_STEP_FIRST,
-		&&CASE_EEOP_BOOL_AND_STEP,
-		&&CASE_EEOP_BOOL_AND_STEP_LAST,
-		&&CASE_EEOP_BOOL_OR_STEP_FIRST,
-		&&CASE_EEOP_BOOL_OR_STEP,
-		&&CASE_EEOP_BOOL_OR_STEP_LAST,
-		&&CASE_EEOP_BOOL_NOT_STEP,
-		&&CASE_EEOP_QUAL,
-		&&CASE_EEOP_JUMP,
-		&&CASE_EEOP_JUMP_IF_NULL,
-		&&CASE_EEOP_JUMP_IF_NOT_NULL,
-		&&CASE_EEOP_JUMP_IF_NOT_TRUE,
-		&&CASE_EEOP_NULLTEST_ISNULL,
-		&&CASE_EEOP_NULLTEST_ISNOTNULL,
-		&&CASE_EEOP_NULLTEST_ROWISNULL,
-		&&CASE_EEOP_NULLTEST_ROWISNOTNULL,
-		&&CASE_EEOP_BOOLTEST_IS_TRUE,
-		&&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
-		&&CASE_EEOP_BOOLTEST_IS_FALSE,
-		&&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
-		&&CASE_EEOP_PARAM_EXEC,
-		&&CASE_EEOP_PARAM_EXTERN,
-		&&CASE_EEOP_PARAM_CALLBACK,
-		&&CASE_EEOP_CASE_TESTVAL,
-		&&CASE_EEOP_MAKE_READONLY,
-		&&CASE_EEOP_IOCOERCE,
-		&&CASE_EEOP_IOCOERCE_SAFE,
-		&&CASE_EEOP_DISTINCT,
-		&&CASE_EEOP_NOT_DISTINCT,
-		&&CASE_EEOP_NULLIF,
-		&&CASE_EEOP_SQLVALUEFUNCTION,
-		&&CASE_EEOP_CURRENTOFEXPR,
-		&&CASE_EEOP_NEXTVALUEEXPR,
-		&&CASE_EEOP_ARRAYEXPR,
-		&&CASE_EEOP_ARRAYCOERCE,
-		&&CASE_EEOP_ROW,
-		&&CASE_EEOP_ROWCOMPARE_STEP,
-		&&CASE_EEOP_ROWCOMPARE_FINAL,
-		&&CASE_EEOP_MINMAX,
-		&&CASE_EEOP_FIELDSELECT,
-		&&CASE_EEOP_FIELDSTORE_DEFORM,
-		&&CASE_EEOP_FIELDSTORE_FORM,
-		&&CASE_EEOP_SBSREF_SUBSCRIPTS,
-		&&CASE_EEOP_SBSREF_OLD,
-		&&CASE_EEOP_SBSREF_ASSIGN,
-		&&CASE_EEOP_SBSREF_FETCH,
-		&&CASE_EEOP_DOMAIN_TESTVAL,
-		&&CASE_EEOP_DOMAIN_NOTNULL,
-		&&CASE_EEOP_DOMAIN_CHECK,
-		&&CASE_EEOP_CONVERT_ROWTYPE,
-		&&CASE_EEOP_SCALARARRAYOP,
-		&&CASE_EEOP_HASHED_SCALARARRAYOP,
-		&&CASE_EEOP_XMLEXPR,
-		&&CASE_EEOP_JSON_CONSTRUCTOR,
-		&&CASE_EEOP_IS_JSON,
-		&&CASE_EEOP_AGGREF,
-		&&CASE_EEOP_GROUPING_FUNC,
-		&&CASE_EEOP_WINDOW_FUNC,
-		&&CASE_EEOP_SUBPLAN,
-		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
-		&&CASE_EEOP_AGG_DESERIALIZE,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
-		&&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
-		&&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
-		&&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
-		&&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
-		&&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
-		&&CASE_EEOP_LAST
+		[EEOP_DONE] = &&CASE_EEOP_DONE,
+		[EEOP_INNER_FETCHSOME] = &&CASE_EEOP_INNER_FETCHSOME,
+		[EEOP_OUTER_FETCHSOME] = &&CASE_EEOP_OUTER_FETCHSOME,
+		[EEOP_SCAN_FETCHSOME] = &&CASE_EEOP_SCAN_FETCHSOME,
+		[EEOP_INNER_VAR] = &&CASE_EEOP_INNER_VAR,
+		[EEOP_OUTER_VAR] = &&CASE_EEOP_OUTER_VAR,
+		[EEOP_SCAN_VAR] = &&CASE_EEOP_SCAN_VAR,
+		[EEOP_INNER_SYSVAR] = &&CASE_EEOP_INNER_SYSVAR,
+		[EEOP_OUTER_SYSVAR] = &&CASE_EEOP_OUTER_SYSVAR,
+		[EEOP_SCAN_SYSVAR] = &&CASE_EEOP_SCAN_SYSVAR,
+		[EEOP_WHOLEROW] = &&CASE_EEOP_WHOLEROW,
+		[EEOP_ASSIGN_INNER_VAR] = &&CASE_EEOP_ASSIGN_INNER_VAR,
+		[EEOP_ASSIGN_OUTER_VAR] = &&CASE_EEOP_ASSIGN_OUTER_VAR,
+		[EEOP_ASSIGN_SCAN_VAR] = &&CASE_EEOP_ASSIGN_SCAN_VAR,
+		[EEOP_ASSIGN_TMP] = &&CASE_EEOP_ASSIGN_TMP,
+		[EEOP_ASSIGN_TMP_MAKE_RO] = &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
+		[EEOP_CONST] = &&CASE_EEOP_CONST,
+		[EEOP_FUNCEXPR] = &&CASE_EEOP_FUNCEXPR,
+		[EEOP_FUNCEXPR_STRICT] = &&CASE_EEOP_FUNCEXPR_STRICT,
+		[EEOP_FUNCEXPR_FUSAGE] = &&CASE_EEOP_FUNCEXPR_FUSAGE,
+		[EEOP_FUNCEXPR_STRICT_FUSAGE] = &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+		[EEOP_BOOL_AND_STEP_FIRST] = &&CASE_EEOP_BOOL_AND_STEP_FIRST,
+		[EEOP_BOOL_AND_STEP] = &&CASE_EEOP_BOOL_AND_STEP,
+		[EEOP_BOOL_AND_STEP_LAST] = &&CASE_EEOP_BOOL_AND_STEP_LAST,
+		[EEOP_BOOL_OR_STEP_FIRST] = &&CASE_EEOP_BOOL_OR_STEP_FIRST,
+		[EEOP_BOOL_OR_STEP] = &&CASE_EEOP_BOOL_OR_STEP,
+		[EEOP_BOOL_OR_STEP_LAST] = &&CASE_EEOP_BOOL_OR_STEP_LAST,
+		[EEOP_BOOL_NOT_STEP] = &&CASE_EEOP_BOOL_NOT_STEP,
+		[EEOP_QUAL] = &&CASE_EEOP_QUAL,
+		[EEOP_JUMP] = &&CASE_EEOP_JUMP,
+		[EEOP_JUMP_IF_NULL] = &&CASE_EEOP_JUMP_IF_NULL,
+		[EEOP_JUMP_IF_NOT_NULL] = &&CASE_EEOP_JUMP_IF_NOT_NULL,
+		[EEOP_JUMP_IF_NOT_TRUE] = &&CASE_EEOP_JUMP_IF_NOT_TRUE,
+		[EEOP_NULLTEST_ISNULL] = &&CASE_EEOP_NULLTEST_ISNULL,
+		[EEOP_NULLTEST_ISNOTNULL] = &&CASE_EEOP_NULLTEST_ISNOTNULL,
+		[EEOP_NULLTEST_ROWISNULL] = &&CASE_EEOP_NULLTEST_ROWISNULL,
+		[EEOP_NULLTEST_ROWISNOTNULL] = &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
+		[EEOP_BOOLTEST_IS_TRUE] = &&CASE_EEOP_BOOLTEST_IS_TRUE,
+		[EEOP_BOOLTEST_IS_NOT_TRUE] = &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
+		[EEOP_BOOLTEST_IS_FALSE] = &&CASE_EEOP_BOOLTEST_IS_FALSE,
+		[EEOP_BOOLTEST_IS_NOT_FALSE] = &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
+		[EEOP_PARAM_EXEC] = &&CASE_EEOP_PARAM_EXEC,
+		[EEOP_PARAM_EXTERN] = &&CASE_EEOP_PARAM_EXTERN,
+		[EEOP_PARAM_CALLBACK] = &&CASE_EEOP_PARAM_CALLBACK,
+		[EEOP_CASE_TESTVAL] = &&CASE_EEOP_CASE_TESTVAL,
+		[EEOP_MAKE_READONLY] = &&CASE_EEOP_MAKE_READONLY,
+		[EEOP_IOCOERCE] = &&CASE_EEOP_IOCOERCE,
+		[EEOP_IOCOERCE_SAFE] = &&CASE_EEOP_IOCOERCE_SAFE,
+		[EEOP_DISTINCT] = &&CASE_EEOP_DISTINCT,
+		[EEOP_NOT_DISTINCT] = &&CASE_EEOP_NOT_DISTINCT,
+		[EEOP_NULLIF] = &&CASE_EEOP_NULLIF,
+		[EEOP_SQLVALUEFUNCTION] = &&CASE_EEOP_SQLVALUEFUNCTION,
+		[EEOP_CURRENTOFEXPR] = &&CASE_EEOP_CURRENTOFEXPR,
+		[EEOP_NEXTVALUEEXPR] = &&CASE_EEOP_NEXTVALUEEXPR,
+		[EEOP_ARRAYEXPR] = &&CASE_EEOP_ARRAYEXPR,
+		[EEOP_ARRAYCOERCE] = &&CASE_EEOP_ARRAYCOERCE,
+		[EEOP_ROW] = &&CASE_EEOP_ROW,
+		[EEOP_ROWCOMPARE_STEP] = &&CASE_EEOP_ROWCOMPARE_STEP,
+		[EEOP_ROWCOMPARE_FINAL] = &&CASE_EEOP_ROWCOMPARE_FINAL,
+		[EEOP_MINMAX] = &&CASE_EEOP_MINMAX,
+		[EEOP_FIELDSELECT] = &&CASE_EEOP_FIELDSELECT,
+		[EEOP_FIELDSTORE_DEFORM] = &&CASE_EEOP_FIELDSTORE_DEFORM,
+		[EEOP_FIELDSTORE_FORM] = &&CASE_EEOP_FIELDSTORE_FORM,
+		[EEOP_SBSREF_SUBSCRIPTS] = &&CASE_EEOP_SBSREF_SUBSCRIPTS,
+		[EEOP_SBSREF_OLD] = &&CASE_EEOP_SBSREF_OLD,
+		[EEOP_SBSREF_ASSIGN] = &&CASE_EEOP_SBSREF_ASSIGN,
+		[EEOP_SBSREF_FETCH] = &&CASE_EEOP_SBSREF_FETCH,
+		[EEOP_DOMAIN_TESTVAL] = &&CASE_EEOP_DOMAIN_TESTVAL,
+		[EEOP_DOMAIN_NOTNULL] = &&CASE_EEOP_DOMAIN_NOTNULL,
+		[EEOP_DOMAIN_CHECK] = &&CASE_EEOP_DOMAIN_CHECK,
+		[EEOP_CONVERT_ROWTYPE] = &&CASE_EEOP_CONVERT_ROWTYPE,
+		[EEOP_SCALARARRAYOP] = &&CASE_EEOP_SCALARARRAYOP,
+		[EEOP_HASHED_SCALARARRAYOP] = &&CASE_EEOP_HASHED_SCALARARRAYOP,
+		[EEOP_XMLEXPR] = &&CASE_EEOP_XMLEXPR,
+		[EEOP_JSON_CONSTRUCTOR] = &&CASE_EEOP_JSON_CONSTRUCTOR,
+		[EEOP_IS_JSON] = &&CASE_EEOP_IS_JSON,
+		[EEOP_AGGREF] = &&CASE_EEOP_AGGREF,
+		[EEOP_GROUPING_FUNC] = &&CASE_EEOP_GROUPING_FUNC,
+		[EEOP_WINDOW_FUNC] = &&CASE_EEOP_WINDOW_FUNC,
+		[EEOP_SUBPLAN] = &&CASE_EEOP_SUBPLAN,
+		[EEOP_AGG_STRICT_DESERIALIZE] = &&CASE_EEOP_AGG_STRICT_DESERIALIZE,
+		[EEOP_AGG_DESERIALIZE] = &&CASE_EEOP_AGG_DESERIALIZE,
+		[EEOP_AGG_STRICT_INPUT_CHECK_ARGS] = &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		[EEOP_AGG_STRICT_INPUT_CHECK_NULLS] = &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
+		[EEOP_AGG_PLAIN_PERGROUP_NULLCHECK] = &&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
+		[EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
+		[EEOP_AGG_PLAIN_TRANS_STRICT_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
+		[EEOP_AGG_PLAIN_TRANS_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
+		[EEOP_AGG_PRESORTED_DISTINCT_SINGLE] = &&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
+		[EEOP_AGG_PRESORTED_DISTINCT_MULTI] = &&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
+		[EEOP_AGG_ORDERED_TRANS_DATUM] = &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
+		[EEOP_AGG_ORDERED_TRANS_TUPLE] = &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
+		[EEOP_LAST] = &&CASE_EEOP_LAST,
 	};
 
 	StaticAssertDecl(lengthof(dispatch_table) == EEOP_LAST + 1,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a28ddcdd77..0a2f389ed7 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -59,9 +59,6 @@ typedef struct ExprEvalRowtypeCache
  *
  * Identifies the operation to be executed and which member in the
  * ExprEvalStep->d union is valid.
- *
- * The order of entries needs to be kept in sync with the dispatch_table[]
- * array in execExprInterp.c:ExecInterpExpr().
  */
 typedef enum ExprEvalOp
 {
-- 
2.34.1


--=-=-=--





^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v2 1/1] Use C99-designated initializer syntax for dispatch_table array
@ 2024-03-05 13:32 Japin Li <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Japin Li @ 2024-03-05 13:32 UTC (permalink / raw)

---
 src/backend/executor/execExprInterp.c | 195 +++++++++++++-------------
 src/include/executor/execExpr.h       |   2 +-
 2 files changed, 97 insertions(+), 100 deletions(-)

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 7c1f51e2e0..e4ad522312 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -400,107 +400,104 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 	TupleTableSlot *outerslot;
 	TupleTableSlot *scanslot;
 
-	/*
-	 * This array has to be in the same order as enum ExprEvalOp.
-	 */
 #if defined(EEO_USE_COMPUTED_GOTO)
 	static const void *const dispatch_table[] = {
-		&&CASE_EEOP_DONE,
-		&&CASE_EEOP_INNER_FETCHSOME,
-		&&CASE_EEOP_OUTER_FETCHSOME,
-		&&CASE_EEOP_SCAN_FETCHSOME,
-		&&CASE_EEOP_INNER_VAR,
-		&&CASE_EEOP_OUTER_VAR,
-		&&CASE_EEOP_SCAN_VAR,
-		&&CASE_EEOP_INNER_SYSVAR,
-		&&CASE_EEOP_OUTER_SYSVAR,
-		&&CASE_EEOP_SCAN_SYSVAR,
-		&&CASE_EEOP_WHOLEROW,
-		&&CASE_EEOP_ASSIGN_INNER_VAR,
-		&&CASE_EEOP_ASSIGN_OUTER_VAR,
-		&&CASE_EEOP_ASSIGN_SCAN_VAR,
-		&&CASE_EEOP_ASSIGN_TMP,
-		&&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
-		&&CASE_EEOP_CONST,
-		&&CASE_EEOP_FUNCEXPR,
-		&&CASE_EEOP_FUNCEXPR_STRICT,
-		&&CASE_EEOP_FUNCEXPR_FUSAGE,
-		&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
-		&&CASE_EEOP_BOOL_AND_STEP_FIRST,
-		&&CASE_EEOP_BOOL_AND_STEP,
-		&&CASE_EEOP_BOOL_AND_STEP_LAST,
-		&&CASE_EEOP_BOOL_OR_STEP_FIRST,
-		&&CASE_EEOP_BOOL_OR_STEP,
-		&&CASE_EEOP_BOOL_OR_STEP_LAST,
-		&&CASE_EEOP_BOOL_NOT_STEP,
-		&&CASE_EEOP_QUAL,
-		&&CASE_EEOP_JUMP,
-		&&CASE_EEOP_JUMP_IF_NULL,
-		&&CASE_EEOP_JUMP_IF_NOT_NULL,
-		&&CASE_EEOP_JUMP_IF_NOT_TRUE,
-		&&CASE_EEOP_NULLTEST_ISNULL,
-		&&CASE_EEOP_NULLTEST_ISNOTNULL,
-		&&CASE_EEOP_NULLTEST_ROWISNULL,
-		&&CASE_EEOP_NULLTEST_ROWISNOTNULL,
-		&&CASE_EEOP_BOOLTEST_IS_TRUE,
-		&&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
-		&&CASE_EEOP_BOOLTEST_IS_FALSE,
-		&&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
-		&&CASE_EEOP_PARAM_EXEC,
-		&&CASE_EEOP_PARAM_EXTERN,
-		&&CASE_EEOP_PARAM_CALLBACK,
-		&&CASE_EEOP_CASE_TESTVAL,
-		&&CASE_EEOP_MAKE_READONLY,
-		&&CASE_EEOP_IOCOERCE,
-		&&CASE_EEOP_IOCOERCE_SAFE,
-		&&CASE_EEOP_DISTINCT,
-		&&CASE_EEOP_NOT_DISTINCT,
-		&&CASE_EEOP_NULLIF,
-		&&CASE_EEOP_SQLVALUEFUNCTION,
-		&&CASE_EEOP_CURRENTOFEXPR,
-		&&CASE_EEOP_NEXTVALUEEXPR,
-		&&CASE_EEOP_ARRAYEXPR,
-		&&CASE_EEOP_ARRAYCOERCE,
-		&&CASE_EEOP_ROW,
-		&&CASE_EEOP_ROWCOMPARE_STEP,
-		&&CASE_EEOP_ROWCOMPARE_FINAL,
-		&&CASE_EEOP_MINMAX,
-		&&CASE_EEOP_FIELDSELECT,
-		&&CASE_EEOP_FIELDSTORE_DEFORM,
-		&&CASE_EEOP_FIELDSTORE_FORM,
-		&&CASE_EEOP_SBSREF_SUBSCRIPTS,
-		&&CASE_EEOP_SBSREF_OLD,
-		&&CASE_EEOP_SBSREF_ASSIGN,
-		&&CASE_EEOP_SBSREF_FETCH,
-		&&CASE_EEOP_DOMAIN_TESTVAL,
-		&&CASE_EEOP_DOMAIN_NOTNULL,
-		&&CASE_EEOP_DOMAIN_CHECK,
-		&&CASE_EEOP_CONVERT_ROWTYPE,
-		&&CASE_EEOP_SCALARARRAYOP,
-		&&CASE_EEOP_HASHED_SCALARARRAYOP,
-		&&CASE_EEOP_XMLEXPR,
-		&&CASE_EEOP_JSON_CONSTRUCTOR,
-		&&CASE_EEOP_IS_JSON,
-		&&CASE_EEOP_AGGREF,
-		&&CASE_EEOP_GROUPING_FUNC,
-		&&CASE_EEOP_WINDOW_FUNC,
-		&&CASE_EEOP_SUBPLAN,
-		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
-		&&CASE_EEOP_AGG_DESERIALIZE,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
-		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
-		&&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
-		&&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
-		&&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
-		&&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
-		&&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
-		&&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
-		&&CASE_EEOP_LAST
+		[EEOP_DONE] = &&CASE_EEOP_DONE,
+		[EEOP_INNER_FETCHSOME] = &&CASE_EEOP_INNER_FETCHSOME,
+		[EEOP_OUTER_FETCHSOME] = &&CASE_EEOP_OUTER_FETCHSOME,
+		[EEOP_SCAN_FETCHSOME] = &&CASE_EEOP_SCAN_FETCHSOME,
+		[EEOP_INNER_VAR] = &&CASE_EEOP_INNER_VAR,
+		[EEOP_OUTER_VAR] = &&CASE_EEOP_OUTER_VAR,
+		[EEOP_SCAN_VAR] = &&CASE_EEOP_SCAN_VAR,
+		[EEOP_INNER_SYSVAR] = &&CASE_EEOP_INNER_SYSVAR,
+		[EEOP_OUTER_SYSVAR] = &&CASE_EEOP_OUTER_SYSVAR,
+		[EEOP_SCAN_SYSVAR] = &&CASE_EEOP_SCAN_SYSVAR,
+		[EEOP_WHOLEROW] = &&CASE_EEOP_WHOLEROW,
+		[EEOP_ASSIGN_INNER_VAR] = &&CASE_EEOP_ASSIGN_INNER_VAR,
+		[EEOP_ASSIGN_OUTER_VAR] = &&CASE_EEOP_ASSIGN_OUTER_VAR,
+		[EEOP_ASSIGN_SCAN_VAR] = &&CASE_EEOP_ASSIGN_SCAN_VAR,
+		[EEOP_ASSIGN_TMP] = &&CASE_EEOP_ASSIGN_TMP,
+		[EEOP_ASSIGN_TMP_MAKE_RO] = &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
+		[EEOP_CONST] = &&CASE_EEOP_CONST,
+		[EEOP_FUNCEXPR] = &&CASE_EEOP_FUNCEXPR,
+		[EEOP_FUNCEXPR_STRICT] = &&CASE_EEOP_FUNCEXPR_STRICT,
+		[EEOP_FUNCEXPR_FUSAGE] = &&CASE_EEOP_FUNCEXPR_FUSAGE,
+		[EEOP_FUNCEXPR_STRICT_FUSAGE] = &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+		[EEOP_BOOL_AND_STEP_FIRST] = &&CASE_EEOP_BOOL_AND_STEP_FIRST,
+		[EEOP_BOOL_AND_STEP] = &&CASE_EEOP_BOOL_AND_STEP,
+		[EEOP_BOOL_AND_STEP_LAST] = &&CASE_EEOP_BOOL_AND_STEP_LAST,
+		[EEOP_BOOL_OR_STEP_FIRST] = &&CASE_EEOP_BOOL_OR_STEP_FIRST,
+		[EEOP_BOOL_OR_STEP] = &&CASE_EEOP_BOOL_OR_STEP,
+		[EEOP_BOOL_OR_STEP_LAST] = &&CASE_EEOP_BOOL_OR_STEP_LAST,
+		[EEOP_BOOL_NOT_STEP] = &&CASE_EEOP_BOOL_NOT_STEP,
+		[EEOP_QUAL] = &&CASE_EEOP_QUAL,
+		[EEOP_JUMP] = &&CASE_EEOP_JUMP,
+		[EEOP_JUMP_IF_NULL] = &&CASE_EEOP_JUMP_IF_NULL,
+		[EEOP_JUMP_IF_NOT_NULL] = &&CASE_EEOP_JUMP_IF_NOT_NULL,
+		[EEOP_JUMP_IF_NOT_TRUE] = &&CASE_EEOP_JUMP_IF_NOT_TRUE,
+		[EEOP_NULLTEST_ISNULL] = &&CASE_EEOP_NULLTEST_ISNULL,
+		[EEOP_NULLTEST_ISNOTNULL] = &&CASE_EEOP_NULLTEST_ISNOTNULL,
+		[EEOP_NULLTEST_ROWISNULL] = &&CASE_EEOP_NULLTEST_ROWISNULL,
+		[EEOP_NULLTEST_ROWISNOTNULL] = &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
+		[EEOP_BOOLTEST_IS_TRUE] = &&CASE_EEOP_BOOLTEST_IS_TRUE,
+		[EEOP_BOOLTEST_IS_NOT_TRUE] = &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
+		[EEOP_BOOLTEST_IS_FALSE] = &&CASE_EEOP_BOOLTEST_IS_FALSE,
+		[EEOP_BOOLTEST_IS_NOT_FALSE] = &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
+		[EEOP_PARAM_EXEC] = &&CASE_EEOP_PARAM_EXEC,
+		[EEOP_PARAM_EXTERN] = &&CASE_EEOP_PARAM_EXTERN,
+		[EEOP_PARAM_CALLBACK] = &&CASE_EEOP_PARAM_CALLBACK,
+		[EEOP_CASE_TESTVAL] = &&CASE_EEOP_CASE_TESTVAL,
+		[EEOP_MAKE_READONLY] = &&CASE_EEOP_MAKE_READONLY,
+		[EEOP_IOCOERCE] = &&CASE_EEOP_IOCOERCE,
+		[EEOP_IOCOERCE_SAFE] = &&CASE_EEOP_IOCOERCE_SAFE,
+		[EEOP_DISTINCT] = &&CASE_EEOP_DISTINCT,
+		[EEOP_NOT_DISTINCT] = &&CASE_EEOP_NOT_DISTINCT,
+		[EEOP_NULLIF] = &&CASE_EEOP_NULLIF,
+		[EEOP_SQLVALUEFUNCTION] = &&CASE_EEOP_SQLVALUEFUNCTION,
+		[EEOP_CURRENTOFEXPR] = &&CASE_EEOP_CURRENTOFEXPR,
+		[EEOP_NEXTVALUEEXPR] = &&CASE_EEOP_NEXTVALUEEXPR,
+		[EEOP_ARRAYEXPR] = &&CASE_EEOP_ARRAYEXPR,
+		[EEOP_ARRAYCOERCE] = &&CASE_EEOP_ARRAYCOERCE,
+		[EEOP_ROW] = &&CASE_EEOP_ROW,
+		[EEOP_ROWCOMPARE_STEP] = &&CASE_EEOP_ROWCOMPARE_STEP,
+		[EEOP_ROWCOMPARE_FINAL] = &&CASE_EEOP_ROWCOMPARE_FINAL,
+		[EEOP_MINMAX] = &&CASE_EEOP_MINMAX,
+		[EEOP_FIELDSELECT] = &&CASE_EEOP_FIELDSELECT,
+		[EEOP_FIELDSTORE_DEFORM] = &&CASE_EEOP_FIELDSTORE_DEFORM,
+		[EEOP_FIELDSTORE_FORM] = &&CASE_EEOP_FIELDSTORE_FORM,
+		[EEOP_SBSREF_SUBSCRIPTS] = &&CASE_EEOP_SBSREF_SUBSCRIPTS,
+		[EEOP_SBSREF_OLD] = &&CASE_EEOP_SBSREF_OLD,
+		[EEOP_SBSREF_ASSIGN] = &&CASE_EEOP_SBSREF_ASSIGN,
+		[EEOP_SBSREF_FETCH] = &&CASE_EEOP_SBSREF_FETCH,
+		[EEOP_DOMAIN_TESTVAL] = &&CASE_EEOP_DOMAIN_TESTVAL,
+		[EEOP_DOMAIN_NOTNULL] = &&CASE_EEOP_DOMAIN_NOTNULL,
+		[EEOP_DOMAIN_CHECK] = &&CASE_EEOP_DOMAIN_CHECK,
+		[EEOP_CONVERT_ROWTYPE] = &&CASE_EEOP_CONVERT_ROWTYPE,
+		[EEOP_SCALARARRAYOP] = &&CASE_EEOP_SCALARARRAYOP,
+		[EEOP_HASHED_SCALARARRAYOP] = &&CASE_EEOP_HASHED_SCALARARRAYOP,
+		[EEOP_XMLEXPR] = &&CASE_EEOP_XMLEXPR,
+		[EEOP_JSON_CONSTRUCTOR] = &&CASE_EEOP_JSON_CONSTRUCTOR,
+		[EEOP_IS_JSON] = &&CASE_EEOP_IS_JSON,
+		[EEOP_AGGREF] = &&CASE_EEOP_AGGREF,
+		[EEOP_GROUPING_FUNC] = &&CASE_EEOP_GROUPING_FUNC,
+		[EEOP_WINDOW_FUNC] = &&CASE_EEOP_WINDOW_FUNC,
+		[EEOP_SUBPLAN] = &&CASE_EEOP_SUBPLAN,
+		[EEOP_AGG_STRICT_DESERIALIZE] = &&CASE_EEOP_AGG_STRICT_DESERIALIZE,
+		[EEOP_AGG_DESERIALIZE] = &&CASE_EEOP_AGG_DESERIALIZE,
+		[EEOP_AGG_STRICT_INPUT_CHECK_ARGS] = &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
+		[EEOP_AGG_STRICT_INPUT_CHECK_NULLS] = &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
+		[EEOP_AGG_PLAIN_PERGROUP_NULLCHECK] = &&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
+		[EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_BYVAL] = &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
+		[EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
+		[EEOP_AGG_PLAIN_TRANS_STRICT_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
+		[EEOP_AGG_PLAIN_TRANS_BYREF] = &&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
+		[EEOP_AGG_PRESORTED_DISTINCT_SINGLE] = &&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
+		[EEOP_AGG_PRESORTED_DISTINCT_MULTI] = &&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
+		[EEOP_AGG_ORDERED_TRANS_DATUM] = &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
+		[EEOP_AGG_ORDERED_TRANS_TUPLE] = &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
+		[EEOP_LAST] = &&CASE_EEOP_LAST,
 	};
 
 	StaticAssertDecl(lengthof(dispatch_table) == EEOP_LAST + 1,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a28ddcdd77..00d3db7a74 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -60,7 +60,7 @@ typedef struct ExprEvalRowtypeCache
  * Identifies the operation to be executed and which member in the
  * ExprEvalStep->d union is valid.
  *
- * The order of entries needs to be kept in sync with the dispatch_table[]
+ * If you add some operation don't forget to update the dispatch_table[]
  * array in execExprInterp.c:ExecInterpExpr().
  */
 typedef enum ExprEvalOp
-- 
2.34.1


--=-=-=--





^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread

* [PATCH v1 1/4] autovac: save all relopts instead of just avopts
@ 2025-06-23 19:07 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 798+ messages in thread

From: Nathan Bossart @ 2025-06-23 19:07 UTC (permalink / raw)

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


--QWBYbd5Ar5k8NvSv
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0002-autovac-resolve-relopts-before-vacuuming.patch



^ permalink  raw  reply  [nested|flat] 798+ messages in thread


end of thread, other threads:[~2025-06-23 19:07 UTC | newest]

Thread overview: 798+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2024-03-05 13:32 [PATCH v3 1/1] Use C99-designated initializer syntax for dispatch_table array Japin Li <[email protected]>
2024-03-05 13:32 [PATCH v1 1/1] Use C99-designated initializer syntax for dispatch_table array Japin Li <[email protected]>
2024-03-05 13:32 [PATCH v2 1/1] Use C99-designated initializer syntax for dispatch_table array Japin Li <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>
2025-06-23 19:07 [PATCH v1 1/4] autovac: save all relopts instead of just avopts Nathan Bossart <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox