v1-0001-fix-analyze-extended-stats-virtual-gen-col.patch

application/octet-stream

Filename: v1-0001-fix-analyze-extended-stats-virtual-gen-col.patch
Type: application/octet-stream
Part: 0
Message: Infinite Autovacuum loop caused by failing virtual generated column expression

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v1-0001
File+
src/backend/statistics/extended_stats.c 98 27
diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c
index 2b83355d..ba25dd62 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -33,6 +33,7 @@
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "rewrite/rewriteHandler.h"
+#include "utils/resowner.h"
 #include "statistics/extended_stats_internal.h"
 #include "statistics/statistics.h"
 #include "utils/acl.h"
@@ -194,42 +195,112 @@ BuildRelationExtStatistics(Relation onerel, bool inh, double totalrows,
 		if (stattarget == 0)
 			continue;
 
-		/* evaluate expressions (if the statistics object has any) */
-		data = make_build_data(onerel, stat, numrows, rows, stats, stattarget);
-
-		/* compute statistic of each requested type */
-		foreach(lc2, stat->types)
+		/*
+		 * Wrap expression evaluation and stats computation in PG_TRY so
+		 * that errors from evaluating virtual generated column expressions
+		 * (e.g. division by zero) don't cause ANALYZE to fail entirely.
+		 * Skip the statistics object and issue a WARNING instead.
+		 *
+		 * Used child ResourceOwner so that any resources allocated
+		 * during expression evaluation are properly released on 
+		 * error without leaking.
+		 */
 		{
-			char		t = (char) lfirst_int(lc2);
-
-			if (t == STATS_EXT_NDISTINCT)
-				ndistinct = statext_ndistinct_build(totalrows, data);
-			else if (t == STATS_EXT_DEPENDENCIES)
-				dependencies = statext_dependencies_build(data);
-			else if (t == STATS_EXT_MCV)
-				mcv = statext_mcv_build(data, totalrows, stattarget);
-			else if (t == STATS_EXT_EXPRESSIONS)
+			ResourceOwner oldowner = CurrentResourceOwner;
+			ResourceOwner child;
+
+			child = ResourceOwnerCreate(oldowner, "BuildExtStatistics");
+			CurrentResourceOwner = child;
+
+			PG_TRY();
 			{
-				AnlExprData *exprdata;
-				int			nexprs;
+				/* evaluate expressions (if the statistics has any) */
+				data = make_build_data(onerel, stat, numrows, rows,
+									   stats, stattarget);
 
-				/* should not happen, thanks to checks when defining stats */
-				if (!stat->exprs)
-					elog(ERROR, "requested expression stats, but there are no expressions");
+				/* compute statistic of each requested type */
+				foreach(lc2, stat->types)
+				{
+					char		t = (char) lfirst_int(lc2);
+
+					if (t == STATS_EXT_NDISTINCT)
+						ndistinct = statext_ndistinct_build(totalrows, data);
+					else if (t == STATS_EXT_DEPENDENCIES)
+						dependencies = statext_dependencies_build(data);
+					else if (t == STATS_EXT_MCV)
+						mcv = statext_mcv_build(data, totalrows, stattarget);
+					else if (t == STATS_EXT_EXPRESSIONS)
+					{
+						AnlExprData *exprdata;
+						int			nexprs;
+
+						/* should not happen, thanks to checks */
+						if (!stat->exprs)
+							elog(ERROR, "requested expression stats, but there are no expressions");
+
+						exprdata = build_expr_data(stat->exprs, stattarget);
+						nexprs = list_length(stat->exprs);
+
+						compute_expr_stats(onerel, exprdata, nexprs,
+										   rows, numrows);
+
+						exprstats = serialize_expr_stats(exprdata, nexprs);
+					}
+				}
 
-				exprdata = build_expr_data(stat->exprs, stattarget);
-				nexprs = list_length(stat->exprs);
+				/* store the statistics in the catalog */
+				statext_store(stat->statOid, inh,
+							  ndistinct, dependencies, mcv, exprstats, stats);
+
+				/* Success: release child ResourceOwner normally */
+				CurrentResourceOwner = oldowner;
+				ResourceOwnerRelease(child,
+									 RESOURCE_RELEASE_BEFORE_LOCKS,
+									 false, false);
+				ResourceOwnerRelease(child,
+									 RESOURCE_RELEASE_LOCKS,
+									 false, false);
+				ResourceOwnerRelease(child,
+									 RESOURCE_RELEASE_AFTER_LOCKS,
+									 false, false);
+				ResourceOwnerDelete(child);
+			}
+			PG_CATCH();
+			{
+				ErrorData  *edata;
+
+				/* Release leaked resources from the child ResourceOwner */
+				CurrentResourceOwner = child;
+				ResourceOwnerRelease(child,
+									 RESOURCE_RELEASE_BEFORE_LOCKS,
+									 false, false);
+				ResourceOwnerRelease(child,
+									 RESOURCE_RELEASE_LOCKS,
+									 false, false);
+				ResourceOwnerRelease(child,
+									 RESOURCE_RELEASE_AFTER_LOCKS,
+									 false, false);
+				CurrentResourceOwner = oldowner;
+				ResourceOwnerDelete(child);
+
+				/* Save the error, issue a WARNING and continue */
+				MemoryContextSwitchTo(cxt);
+				edata = CopyErrorData();
+				FlushErrorState();
 
-				compute_expr_stats(onerel, exprdata, nexprs, rows, numrows);
+				ereport(WARNING,
+						(errcode(ERRCODE_WARNING),
+						 errmsg("skipping statistics object \"%s.%s\" for relation \"%s.%s\"",
+								stat->schema, stat->name,
+								get_namespace_name(onerel->rd_rel->relnamespace),
+								RelationGetRelationName(onerel)),
+						 errdetail("%s", edata->message)));
 
-				exprstats = serialize_expr_stats(exprdata, nexprs);
+				FreeErrorData(edata);
 			}
+			PG_END_TRY();
 		}
 
-		/* store the statistics in the catalog */
-		statext_store(stat->statOid, inh,
-					  ndistinct, dependencies, mcv, exprstats, stats);
-
 		/* for reporting progress */
 		pgstat_progress_update_param(PROGRESS_ANALYZE_EXT_STATS_COMPUTED,
 									 ++ext_cnt);