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