alternative to PG_CATCH

Started by Peter Eisentrautabout 7 years ago18 messages
#1Peter Eisentraut
peter.eisentraut@2ndquadrant.com
1 attachment(s)

This is a common pattern:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_CATCH();
{
cleanup();
PG_RE_THROW();
}
PG_END_TRY();
cleanup(); /* the same as above */

I've played with a way to express this more compactly:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_FINALLY({
cleanup();
});

See attached patch for how this works out in practice.

Thoughts? Other ideas?

One problem is that this currently makes pgindent crash. That's
probably worth fixing anyway. Or perhaps find a way to write this
differently.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-PG_FINALLY.patchtext/plain; charset=UTF-8; name=0001-PG_FINALLY.patch; x-mac-creator=0; x-mac-type=0Download
From e4d05fba0b2e97f7344c77b17a3b8aa6378ded8f Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Thu, 13 Dec 2018 11:19:07 +0100
Subject: [PATCH] PG_FINALLY

This gives an alternative way of catching exceptions, for the common
case where the cleanup code is the same in the error and non-error
cases.  So instead of

    PG_TRY();
    {
        ... code that might throw ereport(ERROR) ...
    }
    PG_CATCH();
    {
        cleanup();
	PG_RE_THROW();
    }
    PG_END_TRY();
    cleanup();

one can write

    PG_TRY();
    {
        ... code that might throw ereport(ERROR) ...
    }
    PG_FINALLY({
        cleanup();
    });
---
 contrib/auto_explain/auto_explain.c           | 16 ++-----
 contrib/dblink/dblink.c                       | 31 +++-----------
 contrib/hstore_plpython/hstore_plpython.c     |  8 +---
 .../pg_stat_statements/pg_stat_statements.c   | 24 +++--------
 contrib/pg_trgm/trgm_regexp.c                 |  9 +---
 contrib/postgres_fdw/connection.c             |  9 +---
 contrib/sepgsql/hooks.c                       |  8 +---
 contrib/sepgsql/label.c                       | 42 +++++--------------
 contrib/sepgsql/selinux.c                     |  8 +---
 contrib/sepgsql/uavc.c                        |  9 +---
 src/backend/catalog/index.c                   | 16 ++-----
 src/backend/commands/async.c                  | 18 +-------
 src/backend/commands/copy.c                   |  8 +---
 src/backend/commands/event_trigger.c          | 18 ++------
 src/backend/commands/extension.c              | 10 +----
 src/backend/commands/matview.c                |  8 +---
 src/backend/commands/subscriptioncmds.c       | 21 ++--------
 src/backend/commands/trigger.c                |  8 +---
 src/backend/commands/vacuum.c                 | 10 +----
 src/backend/libpq/be-fsstubs.c                |  8 +---
 src/backend/tcop/utility.c                    | 18 ++------
 src/backend/utils/adt/xml.c                   | 25 +++--------
 src/include/utils/elog.h                      | 30 +++++++++++++
 src/pl/plperl/plperl.c                        | 24 ++---------
 src/pl/plpgsql/src/pl_handler.c               | 13 ++----
 src/pl/plpython/plpy_cursorobject.c           |  8 +---
 src/pl/plpython/plpy_elog.c                   | 16 +------
 src/pl/plpython/plpy_exec.c                   | 20 ++-------
 src/pl/plpython/plpy_spi.c                    |  7 +---
 src/pl/plpython/plpy_typeio.c                 |  8 +---
 src/pl/tcl/pltcl.c                            | 17 ++------
 31 files changed, 126 insertions(+), 349 deletions(-)

diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 646cd0d42c..54d9a8c5e5 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -295,14 +295,10 @@ explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction,
 			prev_ExecutorRun(queryDesc, direction, count, execute_once);
 		else
 			standard_ExecutorRun(queryDesc, direction, count, execute_once);
-		nesting_level--;
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		nesting_level--;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 }
 
 /*
@@ -318,14 +314,10 @@ explain_ExecutorFinish(QueryDesc *queryDesc)
 			prev_ExecutorFinish(queryDesc);
 		else
 			standard_ExecutorFinish(queryDesc);
-		nesting_level--;
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		nesting_level--;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 }
 
 /*
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 3b73ff13f1..068c2c602c 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -776,18 +776,11 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
 			}
 		}
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		/* if needed, close the connection to the database */
 		if (freeconn)
 			PQfinish(conn);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	/* if needed, close the connection to the database */
-	if (freeconn)
-		PQfinish(conn);
+	});
 
 	return (Datum) 0;
 }
@@ -952,16 +945,11 @@ materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
 			/* clean up and return the tuplestore */
 			tuplestore_donestoring(tupstore);
 		}
-
-		PQclear(res);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		/* be sure to release the libpq result */
 		PQclear(res);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 }
 
 /*
@@ -1466,18 +1454,11 @@ dblink_exec(PG_FUNCTION_ARGS)
 					 errmsg("statement returning results not allowed")));
 		}
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		/* if needed, close the connection to the database */
 		if (freeconn)
 			PQfinish(conn);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	/* if needed, close the connection to the database */
-	if (freeconn)
-		PQfinish(conn);
+	});
 
 	PG_RETURN_TEXT_P(sql_cmd_status);
 }
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index 2f24090ff3..9c5acd0ea1 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -177,17 +177,13 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
 				pairs[i].isnull = false;
 			}
 		}
-		Py_DECREF(items_v);
 
 		pcount = hstoreUniquePairs(pairs, pcount, &buflen);
 		out = hstorePairs(pairs, pcount, buflen);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		Py_DECREF(items_v);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 
 	PG_RETURN_POINTER(out);
 }
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 33f9a79f54..a6b19e6403 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -890,14 +890,10 @@ pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count,
 			prev_ExecutorRun(queryDesc, direction, count, execute_once);
 		else
 			standard_ExecutorRun(queryDesc, direction, count, execute_once);
-		nested_level--;
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		nested_level--;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 }
 
 /*
@@ -913,14 +909,10 @@ pgss_ExecutorFinish(QueryDesc *queryDesc)
 			prev_ExecutorFinish(queryDesc);
 		else
 			standard_ExecutorFinish(queryDesc);
-		nested_level--;
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		nested_level--;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 }
 
 /*
@@ -1005,14 +997,10 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				standard_ProcessUtility(pstmt, queryString,
 										context, params, queryEnv,
 										dest, completionTag);
-			nested_level--;
 		}
-		PG_CATCH();
-		{
+		PG_FINALLY({
 			nested_level--;
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
+		});
 
 		INSTR_TIME_SET_CURRENT(duration);
 		INSTR_TIME_SUBTRACT(duration, start);
diff --git a/contrib/pg_trgm/trgm_regexp.c b/contrib/pg_trgm/trgm_regexp.c
index 547e7c094f..17743f5a8e 100644
--- a/contrib/pg_trgm/trgm_regexp.c
+++ b/contrib/pg_trgm/trgm_regexp.c
@@ -557,14 +557,9 @@ createTrgmNFA(text *text_re, Oid collation,
 	{
 		trg = createTrgmNFAInternal(&regex, graph, rcontext);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		pg_regfree(&regex);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	pg_regfree(&regex);
+	});
 
 	/* Clean up all the cruft we created */
 	MemoryContextSwitchTo(oldcontext);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index a6509932dc..067fc53d59 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -633,15 +633,10 @@ pgfdw_report_error(int elevel, PGresult *res, PGconn *conn,
 				 message_context ? errcontext("%s", message_context) : 0,
 				 sql ? errcontext("remote SQL command: %s", sql) : 0));
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		if (clear)
 			PQclear(res);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	if (clear)
-		PQclear(res);
+	});
 }
 
 /*
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 4249ed552c..430d179a2c 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -373,13 +373,9 @@ sepgsql_utility_command(PlannedStmt *pstmt,
 									context, params, queryEnv,
 									dest, completionTag);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		sepgsql_context_info = saved_context_info;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	sepgsql_context_info = saved_context_info;
+	});
 }
 
 /*
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index acffc468d2..938eb72001 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -474,14 +474,9 @@ sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
 		{
 			label = pstrdup(unlabeled);
 		}
-		PG_CATCH();
-		{
+		PG_FINALLY({
 			freecon(unlabeled);
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
-
-		freecon(unlabeled);
+		});
 	}
 	return label;
 }
@@ -609,13 +604,9 @@ sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
 	{
 		result = pstrdup(raw_label);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		freecon(raw_label);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	freecon(raw_label);
+	});
 
 	PG_RETURN_TEXT_P(cstring_to_text(result));
 }
@@ -649,13 +640,9 @@ sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
 	{
 		result = pstrdup(qual_label);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		freecon(qual_label);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	freecon(qual_label);
+	});
 
 	PG_RETURN_TEXT_P(cstring_to_text(result));
 }
@@ -860,13 +847,9 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
 
 				SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
 			}
-			PG_CATCH();
-			{
+			PG_FINALLY({
 				freecon(context);
-				PG_RE_THROW();
-			}
-			PG_END_TRY();
-			freecon(context);
+			});
 		}
 		else if (errno == ENOENT)
 			ereport(WARNING,
@@ -946,14 +929,9 @@ sepgsql_restorecon(PG_FUNCTION_ARGS)
 		exec_object_restorecon(sehnd, AttributeRelationId);
 		exec_object_restorecon(sehnd, ProcedureRelationId);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		selabel_close(sehnd);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	selabel_close(sehnd);
+	});
 
 	PG_RETURN_BOOL(true);
 }
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
index 47def00a46..db70d77bc2 100644
--- a/contrib/sepgsql/selinux.c
+++ b/contrib/sepgsql/selinux.c
@@ -873,13 +873,9 @@ sepgsql_compute_create(const char *scontext,
 	{
 		result = pstrdup(ncontext);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		freecon(ncontext);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	freecon(ncontext);
+	});
 
 	return result;
 }
diff --git a/contrib/sepgsql/uavc.c b/contrib/sepgsql/uavc.c
index ea276ee0cc..37ceed8340 100644
--- a/contrib/sepgsql/uavc.c
+++ b/contrib/sepgsql/uavc.c
@@ -187,14 +187,9 @@ sepgsql_avc_unlabeled(void)
 		{
 			avc_unlabeled = MemoryContextStrdup(avc_mem_cxt, unlabeled);
 		}
-		PG_CATCH();
-		{
+		PG_FINALLY({
 			freecon(unlabeled);
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
-
-		freecon(unlabeled);
+		});
 	}
 	return avc_unlabeled;
 }
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8709e8c22c..62e1b4dc73 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -3705,14 +3705,10 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 		/* Note: we do not need to re-establish pkey setting */
 		index_build(heapRelation, iRel, indexInfo, false, true, true);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		/* Make sure flag gets cleared on error exit */
 		ResetReindexProcessing();
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	ResetReindexProcessing();
+	});
 
 	/*
 	 * If the index is marked invalid/not-ready/dead (ie, it's from a failed
@@ -3954,14 +3950,10 @@ reindex_relation(Oid relid, int flags, int options)
 				doneIndexes = lappend_oid(doneIndexes, indexOid);
 		}
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		/* Make sure list gets cleared on error exit */
 		ResetReindexPending();
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	ResetReindexPending();
+	});
 
 	if (is_pg_class)
 		RelationSetIndexList(rel, indexIds);
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index ee7c6d41b4..247cbf98a8 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1867,8 +1867,7 @@ asyncQueueReadAllNotifications(void)
 													   snapshot);
 		} while (!reachedStop);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		/* Update shared state */
 		LWLockAcquire(AsyncQueueLock, LW_SHARED);
 		QUEUE_BACKEND_POS(MyBackendId) = pos;
@@ -1878,20 +1877,7 @@ asyncQueueReadAllNotifications(void)
 		/* If we were the laziest backend, try to advance the tail pointer */
 		if (advanceTail)
 			asyncQueueAdvanceTail();
-
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	/* Update shared state */
-	LWLockAcquire(AsyncQueueLock, LW_SHARED);
-	QUEUE_BACKEND_POS(MyBackendId) = pos;
-	advanceTail = QUEUE_POS_EQUAL(oldpos, QUEUE_TAIL);
-	LWLockRelease(AsyncQueueLock);
-
-	/* If we were the laziest backend, try to advance the tail pointer */
-	if (advanceTail)
-		asyncQueueAdvanceTail();
+	});
 
 	/* Done with snapshot */
 	UnregisterSnapshot(snapshot);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 4aa8890fe8..de10453b4a 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1850,13 +1850,9 @@ BeginCopyTo(ParseState *pstate,
 			{
 				cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W);
 			}
-			PG_CATCH();
-			{
+			PG_FINALLY({
 				umask(oumask);
-				PG_RE_THROW();
-			}
-			PG_END_TRY();
-			umask(oumask);
+			});
 			if (cstate->copy_file == NULL)
 			{
 				/* copy errno because ereport subfunctions might change it */
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3e7c1067d8..0e5bfc5f5b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -935,13 +935,9 @@ EventTriggerSQLDrop(Node *parsetree)
 	{
 		EventTriggerInvoke(runlist, &trigdata);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		currentEventTriggerState->in_sql_drop = false;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	currentEventTriggerState->in_sql_drop = false;
+	});
 
 	/* Cleanup. */
 	list_free(runlist);
@@ -1008,16 +1004,10 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
 	{
 		EventTriggerInvoke(runlist, &trigdata);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		currentEventTriggerState->table_rewrite_oid = InvalidOid;
 		currentEventTriggerState->table_rewrite_reason = 0;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	currentEventTriggerState->table_rewrite_oid = InvalidOid;
-	currentEventTriggerState->table_rewrite_reason = 0;
+	});
 
 	/* Cleanup. */
 	list_free(runlist);
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 87e4dd8245..a9d64dc2f3 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -922,16 +922,10 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 
 		execute_sql_string(c_sql);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		creating_extension = false;
 		CurrentExtensionObject = InvalidOid;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	creating_extension = false;
-	CurrentExtensionObject = InvalidOid;
+	});
 
 	/*
 	 * Restore the GUC variables we set above.
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index a171ebabf8..bc67f9932c 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -320,13 +320,9 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
 			refresh_by_match_merge(matviewOid, OIDNewHeap, relowner,
 								   save_sec_context);
 		}
-		PG_CATCH();
-		{
+		PG_FINALLY({
 			matview_maintenance_depth = old_depth;
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
-		Assert(matview_maintenance_depth == old_depth);
+		});
 	}
 	else
 	{
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 9021463a4c..49fe891e48 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -474,16 +474,9 @@ CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
 								slotname)));
 			}
 		}
-		PG_CATCH();
-		{
-			/* Close the connection in case of failure. */
+		PG_FINALLY({
 			walrcv_disconnect(wrconn);
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
-
-		/* And we are done with the remote side. */
-		walrcv_disconnect(wrconn);
+		});
 	}
 	else
 		ereport(WARNING,
@@ -1002,15 +995,9 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
 
 		walrcv_clear_result(res);
 	}
-	PG_CATCH();
-	{
-		/* Close the connection in case of failure */
+	PG_FINALLY({
 		walrcv_disconnect(wrconn);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	walrcv_disconnect(wrconn);
+	});
 
 	pfree(cmd.data);
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index bcdd86ce92..eb3a5fb48d 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2413,13 +2413,9 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	{
 		result = FunctionCallInvoke(&fcinfo);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		MyTriggerDepth--;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	MyTriggerDepth--;
+	});
 
 	pgstat_end_function_usage(&fcusage, true);
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 25b3b0312c..897967a7ec 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -365,16 +365,10 @@ vacuum(int options, List *relations, VacuumParams *params,
 			}
 		}
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		in_vacuum = false;
 		VacuumCostActive = false;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	in_vacuum = false;
-	VacuumCostActive = false;
+	});
 
 	/*
 	 * Finish up processing.
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index 0b802b54e4..fa5e4c6596 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -498,13 +498,9 @@ be_lo_export(PG_FUNCTION_ARGS)
 		fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
 								   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		umask(oumask);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-	umask(oumask);
+	});
 	if (fd < 0)
 		ereport(ERROR,
 				(errcode_for_file_access(),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 970c94ee80..6b9e138414 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1496,13 +1496,9 @@ ProcessUtilitySlow(ParseState *pstate,
 					address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
 												 queryString, params, completionTag);
 				}
-				PG_CATCH();
-				{
+				PG_FINALLY({
 					EventTriggerUndoInhibitCommandCollection();
-					PG_RE_THROW();
-				}
-				PG_END_TRY();
-				EventTriggerUndoInhibitCommandCollection();
+				});
 				break;
 
 			case T_CreateTrigStmt:
@@ -1694,16 +1690,10 @@ ProcessUtilitySlow(ParseState *pstate,
 			EventTriggerDDLCommandEnd(parsetree);
 		}
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		if (needCleanup)
 			EventTriggerEndCompleteQuery();
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	if (needCleanup)
-		EventTriggerEndCompleteQuery();
+	});
 }
 
 /*
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 37d85f71f3..3fe86d2e78 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -3705,15 +3705,10 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 			xmlNodeDump(buf, NULL, cur_copy, 0, 1);
 			result = xmlBuffer_to_xmltype(buf);
 		}
-		PG_CATCH();
-		{
+		PG_FINALLY({
 			xmlFreeNode(cur_copy);
 			xmlBufferFree(buf);
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
-		xmlFreeNode(cur_copy);
-		xmlBufferFree(buf);
+		});
 	}
 	else
 	{
@@ -3728,13 +3723,9 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 			result = (xmltype *) cstring_to_text(escaped);
 			pfree(escaped);
 		}
-		PG_CATCH();
-		{
+		PG_FINALLY({
 			xmlFree(str);
-			PG_RE_THROW();
-		}
-		PG_END_TRY();
-		xmlFree(str);
+		});
 	}
 
 	return result;
@@ -4523,13 +4514,9 @@ XmlTableGetValue(TableFuncScanState *state, int colnum,
 					{
 						cstr = pstrdup((char *) str);
 					}
-					PG_CATCH();
-					{
+					PG_FINALLY({
 						xmlFree(str);
-						PG_RE_THROW();
-					}
-					PG_END_TRY();
-					xmlFree(str);
+					});
 				}
 				else
 				{
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 33c6b53e27..9cf2bcd8a4 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -318,6 +318,36 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
 		error_context_stack = save_context_stack; \
 	} while (0)
 
+/*----------
+ * Variant API for the common case that the error recovery code and the
+ * cleanup in the normal code path are identical.  Use like so:
+ *
+ *		PG_TRY();
+ *		{
+ *			... code that might throw ereport(ERROR) ...
+ *		}
+ *		PG_FINALLY(
+ *		{
+ *			... cleanup code ...
+ *		});
+ *
+ * The cleanup code will be run in either case, and any error will be rethrown
+ * afterwards.  Note the slightly different bracketing.
+ */
+#define PG_FINALLY(code) \
+		} \
+		else \
+		{ \
+			PG_exception_stack = save_exception_stack; \
+			error_context_stack = save_context_stack; \
+			do { code } while(0); \
+			PG_RE_THROW(); \
+		} \
+		PG_exception_stack = save_exception_stack; \
+		error_context_stack = save_context_stack; \
+		do { code } while(0); \
+	} while (0)
+
 /*
  * Some compilers understand pg_attribute_noreturn(); for other compilers,
  * insert pg_unreachable() so that the compiler gets the point.
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index fe54b20903..4172a399cd 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1851,20 +1851,13 @@ plperl_call_handler(PG_FUNCTION_ARGS)
 		else
 			retval = plperl_func_handler(fcinfo);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		current_call_data = save_call_data;
 		activate_interpreter(oldinterp);
 		if (this_call_data.prodesc)
 			decrement_prodesc_refcount(this_call_data.prodesc);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
+	});
 
-	current_call_data = save_call_data;
-	activate_interpreter(oldinterp);
-	if (this_call_data.prodesc)
-		decrement_prodesc_refcount(this_call_data.prodesc);
 	return retval;
 }
 
@@ -1947,21 +1940,12 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		if (SPI_finish() != SPI_OK_FINISH)
 			elog(ERROR, "SPI_finish() failed");
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		if (desc.reference)
 			SvREFCNT_dec_current(desc.reference);
 		current_call_data = save_call_data;
 		activate_interpreter(oldinterp);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	if (desc.reference)
-		SvREFCNT_dec_current(desc.reference);
-
-	current_call_data = save_call_data;
-	activate_interpreter(oldinterp);
+	});
 
 	error_context_stack = pl_error_context.previous;
 
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 7d3647a12d..9a40111258 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -266,18 +266,11 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
 		else
 			retval = plpgsql_exec_function(func, fcinfo, NULL, !nonatomic);
 	}
-	PG_CATCH();
-	{
-		/* Decrement use-count, restore cur_estate, and propagate error */
+	PG_FINALLY({
+		/* Decrement use-count, restore cur_estate */
 		func->use_count--;
 		func->cur_estate = save_cur_estate;
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	func->use_count--;
-
-	func->cur_estate = save_cur_estate;
+	});
 
 	/*
 	 * Disconnect from SPI manager
diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 45ac25b2ae..0378d1f049 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -228,13 +228,9 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 				plan->values[j] = PLy_output_convert(arg, elem, &isnull);
 				nulls[j] = isnull ? 'n' : ' ';
 			}
-			PG_CATCH();
-			{
+			PG_FINALLY({
 				Py_DECREF(elem);
-				PG_RE_THROW();
-			}
-			PG_END_TRY();
-			Py_DECREF(elem);
+			});
 		}
 
 		portal = SPI_cursor_open(NULL, plan->plan, plan->values, nulls,
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 3814a6c32d..b897c83ba6 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -141,7 +141,7 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 				 (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME,
 														constraint_name) : 0));
 	}
-	PG_CATCH();
+	PG_FINALLY(
 	{
 		if (fmt)
 			pfree(emsg.data);
@@ -151,19 +151,7 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 			pfree(tbmsg);
 		Py_XDECREF(exc);
 		Py_XDECREF(val);
-
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	if (fmt)
-		pfree(emsg.data);
-	if (xmsg)
-		pfree(xmsg);
-	if (tbmsg)
-		pfree(tbmsg);
-	Py_XDECREF(exc);
-	Py_XDECREF(val);
+	});
 }
 
 /*
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 47ed95dcc6..9f601daec7 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -402,17 +402,10 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
 			}
 		}
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		Py_XDECREF(plargs);
 		Py_XDECREF(plrv);
-
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	Py_DECREF(plargs);
-	Py_DECREF(plrv);
+	});
 
 	return rv;
 }
@@ -1037,14 +1030,9 @@ PLy_procedure_call(PLyProcedure *proc, const char *kargs, PyObject *vargs)
 		 */
 		Assert(list_length(explicit_subtransactions) >= save_subxact_level);
 	}
-	PG_CATCH();
-	{
+	PG_FINALLY({
 		PLy_abort_open_subtransactions(save_subxact_level);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	PLy_abort_open_subtransactions(save_subxact_level);
+	});
 
 	/* If the Python code returned an error, propagate it */
 	if (rv == NULL)
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 41155fc81e..9dc0e7c3f1 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -249,13 +249,10 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 				plan->values[j] = PLy_output_convert(arg, elem, &isnull);
 				nulls[j] = isnull ? 'n' : ' ';
 			}
-			PG_CATCH();
+			PG_FINALLY(
 			{
 				Py_DECREF(elem);
-				PG_RE_THROW();
-			}
-			PG_END_TRY();
-			Py_DECREF(elem);
+			});
 		}
 
 		rv = SPI_execute_plan(plan->plan, plan->values, nulls,
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index d6a6a849c3..99e8fc0602 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -918,14 +918,10 @@ PLyObject_ToBytea(PLyObToDatum *arg, PyObject *plrv,
 		memcpy(VARDATA(result), plrv_sc, len);
 		rv = PointerGetDatum(result);
 	}
-	PG_CATCH();
+	PG_FINALLY(
 	{
 		Py_XDECREF(plrv_so);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	Py_XDECREF(plrv_so);
+	});
 
 	return rv;
 }
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 3b1454f833..e7f0a39014 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -765,9 +765,10 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
 			retval = pltcl_func_handler(fcinfo, &current_call_state, pltrusted);
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY(
 	{
 		/* Restore static pointer, then clean up the prodesc refcount if any */
+		/* (We're being paranoid in case an error is thrown in context deletion) */
 		pltcl_current_call_state = save_call_state;
 		if (current_call_state.prodesc != NULL)
 		{
@@ -775,19 +776,7 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
 			if (--current_call_state.prodesc->fn_refcount == 0)
 				MemoryContextDelete(current_call_state.prodesc->fn_cxt);
 		}
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	/* Restore static pointer, then clean up the prodesc refcount if any */
-	/* (We're being paranoid in case an error is thrown in context deletion) */
-	pltcl_current_call_state = save_call_state;
-	if (current_call_state.prodesc != NULL)
-	{
-		Assert(current_call_state.prodesc->fn_refcount > 0);
-		if (--current_call_state.prodesc->fn_refcount == 0)
-			MemoryContextDelete(current_call_state.prodesc->fn_cxt);
-	}
+	});
 
 	return retval;
 }
-- 
2.20.0

#2Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Peter Eisentraut (#1)
Re: alternative to PG_CATCH

At Thu, 13 Dec 2018 11:33:39 +0100, Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote in <c170919d-c78b-3dac-5ff6-9bd12f7a38bc@2ndquadrant.com>

This is a common pattern:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_CATCH();
{
cleanup();
PG_RE_THROW();
}
PG_END_TRY();
cleanup(); /* the same as above */

I've played with a way to express this more compactly:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_FINALLY({
cleanup();
});

See attached patch for how this works out in practice.

Thoughts? Other ideas?

One problem is that this currently makes pgindent crash. That's
probably worth fixing anyway. Or perhaps find a way to write this
differently.

Though I didn't look into individual change, this kind of
refactoring looks good to me. But the syntax looks
somewhat.. uh..

I'm not sure it is actually workable, but I suspect that what we
need here is just a shortcut of 'PG_CATCH();{PG_RE_THROW();}'.
Something like this:

#define PG_FINALLY() \
} \
else \
{ \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
PG_RE_THROW(); \
} \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
{

Which can be used as:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_FINALLY();
{
cleanup();
}
PG_TRY_END();

Or

#define PG_END_TRY_CATCHING_ALL() \
PG_FINALLY(); \
PG_END_TRY();

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_END_TRY_CATCHING_ALL();

cleanup();

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#3Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Peter Eisentraut (#1)
Re: alternative to PG_CATCH

On 12/13/18 5:33 AM, Peter Eisentraut wrote:

This is a common pattern:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_CATCH();
{
cleanup();
PG_RE_THROW();
}
PG_END_TRY();
cleanup(); /* the same as above */

I've played with a way to express this more compactly:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_FINALLY({
cleanup();
});

See attached patch for how this works out in practice.

Thoughts? Other ideas?

One problem is that this currently makes pgindent crash. That's
probably worth fixing anyway. Or perhaps find a way to write this
differently.

The pgindent issue isn't at all surprising. If we wanted to fix it the
way would be to get the perl script to rewrite it with something indent
would accept (e.g. move the block outside the parentheses) and then undo
that after the indent had run.

But the block inside parentheses feels kinda funny, doesn't it?

cheers

andrew

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#4David Steele
david@pgmasters.net
In reply to: Kyotaro HORIGUCHI (#2)
1 attachment(s)
Re: alternative to PG_CATCH

On 12/13/18 7:26 AM, Kyotaro HORIGUCHI wrote:

At Thu, 13 Dec 2018 11:33:39 +0100, Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote in <c170919d-c78b-3dac-5ff6-9bd12f7a38bc@2ndquadrant.com>

I've played with a way to express this more compactly:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_FINALLY({
cleanup();
});

See attached patch for how this works out in practice.

+1

Though I didn't look into individual change, this kind of
refactoring looks good to me. But the syntax looks
somewhat.. uh..

I'm not sure it is actually workable, but I suspect that what we
need here is just a shortcut of 'PG_CATCH();{PG_RE_THROW();}'.
Something like this:

#define PG_FINALLY() \
} \
else \
{ \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
PG_RE_THROW(); \
} \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
{

Which can be used as:

PG_TRY();
{
... code that might throw ereport(ERROR) ...
}
PG_FINALLY();
{
cleanup();
}
PG_TRY_END();

I like this syntax better. We use something very similar in the
pgBackRest project:

TRY_BEGIN()
{
<Do something that might throw an error>
}
CATCH(Error1)
{
<Handle Error1>
}
CATCH(Error2)
{
<Handle Error2>
}
CATCH_ANY()
{
<Handle errors that are not Error1 or Error2>
}
FINALLY()
{
<Cleanup code that runs in all cases>
}
TRY_END();

The syntax works out simpler if the FINALLY is part of the TRY block.

See attached for the implementation.

Regards,
--
-David
david@pgmasters.net

Attachments:

error.htext/plain; charset=UTF-8; name=error.h; x-mac-creator=0; x-mac-type=0Download
/***********************************************************************************************************************************
Error Handler

Implement a try ... catch ... finally error handler.

TRY_BEGIN()
{
    <Do something that might throw an error>
}
CATCH(Error1)
{
    <Handle Error1>
}
CATCH(Error2)
{
    <Handle Error2>
}
CATCH_ANY()
{
    <Handle errors that are not Error1 or Error2>
}
FINALLY()
{
    <Cleanup code that runs in all cases>
}
TRY_END();

The CATCH() and FINALLY() blocks are optional but at least one must be specified.  There is no need for a TRY block by itself
because errors will automatically be propagated to the nearest try block in the call stack.

IMPORTANT: If a local variable of the function containing a TRY block is modified in the TRY_BEGIN() block and used later in the
function after an error is thrown, that variable must be declared "volatile" if the preserving the value is important.  Beware that
gcc's -Wclobbered warnings are almost entirely useless for catching such issues.

IMPORTANT: Never call return from within any of the error-handling blocks.
***********************************************************************************************************************************/
#ifndef COMMON_ERROR_H
#define COMMON_ERROR_H

#include <errno.h>
#include <setjmp.h>
#include <stdbool.h>

/***********************************************************************************************************************************
Error type object
***********************************************************************************************************************************/
typedef struct ErrorType ErrorType;

// Macro for declaring new error types
#define ERROR_DECLARE(name)                                                                                                        \
    extern const ErrorType name

// Include error type declarations
#include "common/error.auto.h"

/***********************************************************************************************************************************
Functions to get information about a generic error type
***********************************************************************************************************************************/
int errorTypeCode(const ErrorType *errorType);
const ErrorType *errorTypeFromCode(int code);
const char *errorTypeName(const ErrorType *errorType);
const ErrorType *errorTypeParent(const ErrorType *errorType);
bool errorTypeExtends(const ErrorType *child, const ErrorType *parent);

/***********************************************************************************************************************************
Functions to get information about the current error
***********************************************************************************************************************************/
const ErrorType *errorType(void);
int errorCode(void);
const char *errorFileName(void);
const char *errorFunctionName(void);
int errorFileLine(void);
bool errorInstanceOf(const ErrorType *errorTypeTest);
const char *errorMessage(void);
const char *errorName(void);
const char *errorStackTrace(void);

/***********************************************************************************************************************************
Functions to get information about the try stack
***********************************************************************************************************************************/
unsigned int errorTryDepth(void);

/***********************************************************************************************************************************
Begin a block where errors can be thrown
***********************************************************************************************************************************/
#define TRY_BEGIN()                                                                                                                \
do                                                                                                                                 \
{                                                                                                                                  \
    if (errorInternalTry(__FILE__, __func__, __LINE__) && setjmp(*errorInternalJump()) >= 0)                                       \
    {                                                                                                                              \
        while (errorInternalProcess(false))                                                                                        \
            if (errorInternalStateTry())

/***********************************************************************************************************************************
Catch a specific error thrown in the try block
***********************************************************************************************************************************/
#define CATCH(errorTypeCatch)                                                                                                      \
    else if (errorInternalStateCatch(&errorTypeCatch))

/***********************************************************************************************************************************
Catch any error thrown in the try block
***********************************************************************************************************************************/
#define CATCH_ANY()                                                                                                                \
    else if (errorInternalStateCatch(&RuntimeError))

/***********************************************************************************************************************************
Code to run whether the try block was successful or not
***********************************************************************************************************************************/
#define FINALLY()                                                                                                                  \
    else if (errorInternalStateFinal())

/***********************************************************************************************************************************
End the try block
***********************************************************************************************************************************/
#define TRY_END()                                                                                                                  \
    }                                                                                                                              \
} while (0)

/***********************************************************************************************************************************
Throw an error

Errors can be thrown any time, but if there is no TRY block somewhere in the call stack then the program will exit and print the
error information to stderr.

The seldom used "THROWP" variants allow an error to be thrown with a pointer to the error type.
***********************************************************************************************************************************/
#define THROW(errorType, message)                                                                                                  \
    errorInternalThrow(&errorType, __FILE__, __func__, __LINE__, message)
#define THROW_FMT(errorType, ...)                                                                                                  \
    errorInternalThrowFmt(&errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP(errorType, message)                                                                                                 \
    errorInternalThrow(errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_FMT(errorType, ...)                                                                                                 \
    errorInternalThrowFmt(errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)

#define THROW_CODE(errorCode, message)                                                                                             \
    errorInternalThrow(errorTypeFromCode(errorCode), __FILE__, __func__, __LINE__, message)
#define THROW_CODE_FMT(errorCode, ...)                                                                                             \
    errorInternalThrowFmt(errorTypeFromCode(errorCode), __FILE__, __func__, __LINE__, __VA_ARGS__)

/***********************************************************************************************************************************
Throw an error when a system call fails
***********************************************************************************************************************************/
#define THROW_SYS_ERROR(errorType, message)                                                                                        \
    errorInternalThrowSys(errno, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_FMT(errorType, ...)                                                                                        \
    errorInternalThrowSysFmt(errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR(errorType, message)                                                                                       \
    errorInternalThrowSys(errno, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_FMT(errorType, ...)                                                                                       \
    errorInternalThrowSysFmt(errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)

#define THROW_ON_SYS_ERROR(error, errorType, message)                                                                              \
    do                                                                                                                             \
    {                                                                                                                              \
        if (error)                                                                                                                 \
            errorInternalThrowSys(errno, &errorType, __FILE__, __func__, __LINE__, message);                                       \
    } while(0)

#define THROW_ON_SYS_ERROR_FMT(error, errorType, ...)                                                                              \
    do                                                                                                                             \
    {                                                                                                                              \
        if (error)                                                                                                                 \
            errorInternalThrowSysFmt(errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__);                                \
    } while(0)

#define THROWP_ON_SYS_ERROR(error, errorType, message)                                                                             \
    do                                                                                                                             \
    {                                                                                                                              \
        if (error)                                                                                                                 \
            errorInternalThrowSys(errno, errorType, __FILE__, __func__, __LINE__, message);                                        \
    } while(0)

#define THROWP_ON_SYS_ERROR_FMT(error, errorType, ...)                                                                             \
    do                                                                                                                             \
    {                                                                                                                              \
        if (error)                                                                                                                 \
            errorInternalThrowSysFmt(errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__);                                 \
    } while(0)

#define THROW_SYS_ERROR_CODE(errNo, errorType, message)                                                                            \
    errorInternalThrowSys(errNo, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_CODE_FMT(errNo, errorType, ...)                                                                            \
    errorInternalThrowSysFmt(errNo, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR_CODE(errNo, errorType, message)                                                                           \
    errorInternalThrowSys(errNo, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_CODE_FMT(errNo, errorType, ...)                                                                           \
    errorInternalThrowSysFmt(errNo, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)

/***********************************************************************************************************************************
Rethrow the current error
***********************************************************************************************************************************/
#define RETHROW()                                                                                                                  \
    errorInternalPropagate()

/***********************************************************************************************************************************
Internal functions

These functions are used by the macros to implement the error handler and should never be called independently.
***********************************************************************************************************************************/
bool errorInternalTry(const char *fileName, const char *functionName, int fileLine);
jmp_buf *errorInternalJump(void);
bool errorInternalStateTry(void);
bool errorInternalStateCatch(const ErrorType *errorTypeCatch);
bool errorInternalStateFinal(void);
bool errorInternalProcess(bool catch);
void errorInternalPropagate(void) __attribute__((__noreturn__));
void errorInternalThrow(
    const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *message)
    __attribute__((__noreturn__));
void errorInternalThrowFmt(
    const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *format, ...)
    __attribute__((format(printf, 5, 6))) __attribute__((__noreturn__));
void errorInternalThrowSys(
    int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *message)
    __attribute__((__noreturn__));
void errorInternalThrowSysFmt(
    int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *format, ...)
    __attribute__((format(printf, 6, 7))) __attribute__((__noreturn__));

/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_DEBUG_ERROR_TYPE_TYPE                                                                                             \
    ErrorType *
#define FUNCTION_DEBUG_ERROR_TYPE_FORMAT(value, buffer, bufferSize)                                                                \
    objToLog(value, "ErrorType", buffer, bufferSize)

#endif
#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#3)
Re: alternative to PG_CATCH

Andrew Dunstan <andrew.dunstan@2ndquadrant.com> writes:

But the block inside parentheses feels kinda funny, doesn't it?

+1. I think this is a good concept, but let's put in enough effort to
not require weird syntax.

regards, tom lane

#6Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Kyotaro HORIGUCHI (#2)
Re: alternative to PG_CATCH

On 13/12/2018 13:26, Kyotaro HORIGUCHI wrote:

Though I didn't look into individual change, this kind of
refactoring looks good to me. But the syntax looks
somewhat.. uh..

I'm not sure it is actually workable, but I suspect that what we
need here is just a shortcut of 'PG_CATCH();{PG_RE_THROW();}'.
Something like this:

#define PG_FINALLY() \
} \
else \
{ \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
PG_RE_THROW(); \
} \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
{

I don't think this works, because the "finally" code needs to be run in
the else branch before the rethrow.

The fundamental problem, as I see it, is that the macro expansion needs
to produce the "finally" code twice: Once in the else (error) branch of
the setjmp, and once in the non-error code path, either after the
if-setjmp, or in the try block. But to be able to do that, we need to
capture the block as a macro argument.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#6)
Re: alternative to PG_CATCH

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

The fundamental problem, as I see it, is that the macro expansion needs
to produce the "finally" code twice: Once in the else (error) branch of
the setjmp, and once in the non-error code path, either after the
if-setjmp, or in the try block. But to be able to do that, we need to
capture the block as a macro argument.

I don't especially like the MACRO({...}) proposal, because it looks way
too much like gcc's special syntax for "statement expressions". If we
have to go this way, I'd rather see if MACRO((...)) can be made to work.
But I question your assumption that we have to have two physical copies
of the "finally" code. There's nothing wrong with implementing this
sort of infrastructure with some goto's, or whatever else we have to
have to make it work. Also, it'd look more natural as an extension
to the existing PG_TRY infrastructure if the source code were like

PG_TRY();
{
...
}
PG_FINALLY();
{
...
}
PG_END_TRY();

So even if Kyotaro-san's initial sketch isn't right in detail,
I think he has the right idea about where we want to go.

regards, tom lane

#8Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#7)
1 attachment(s)
Re: alternative to PG_CATCH

On 2018-12-14 16:49, Tom Lane wrote:

I don't especially like the MACRO({...}) proposal, because it looks way
too much like gcc's special syntax for "statement expressions". If we
have to go this way, I'd rather see if MACRO((...)) can be made to work.
But I question your assumption that we have to have two physical copies
of the "finally" code. There's nothing wrong with implementing this
sort of infrastructure with some goto's, or whatever else we have to
have to make it work. Also, it'd look more natural as an extension
to the existing PG_TRY infrastructure if the source code were like

PG_TRY();
{
...
}
PG_FINALLY();
{
...
}
PG_END_TRY();

Here is a new implementation that works just like that.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

v2-0001-PG_FINALLY.patchtext/plain; charset=UTF-8; name=v2-0001-PG_FINALLY.patch; x-mac-creator=0; x-mac-type=0Download
From be2246f8e239e57498928d55eb7813e59d2df719 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 28 Oct 2019 09:34:12 +0100
Subject: [PATCH v2] PG_FINALLY

This gives an alternative way of catching exceptions, for the common
case where the cleanup code is the same in the error and non-error
cases.  So instead of

    PG_TRY();
    {
        ... code that might throw ereport(ERROR) ...
    }
    PG_CATCH();
    {
        cleanup();
	PG_RE_THROW();
    }
    PG_END_TRY();
    cleanup();

one can write

    PG_TRY();
    {
        ... code that might throw ereport(ERROR) ...
    }
    PG_FINALLY();
    {
        cleanup();
    }
    PG_END_TRY();

Discussion: https://www.postgresql.org/message-id/flat/c170919d-c78b-3dac-5ff6-9bd12f7a38bc%402ndquadrant.com
---
 src/include/utils/elog.h                      | 28 +++++++++++++++++++
 contrib/auto_explain/auto_explain.c           |  8 ++----
 contrib/dblink/dblink.c                       | 19 ++-----------
 contrib/hstore_plpython/hstore_plpython.c     |  5 +---
 contrib/jsonb_plpython/jsonb_plpython.c       |  5 +---
 .../pg_stat_statements/pg_stat_statements.c   | 12 ++------
 contrib/pg_trgm/trgm_regexp.c                 |  5 +---
 contrib/postgres_fdw/connection.c             |  5 +---
 contrib/postgres_fdw/postgres_fdw.c           | 25 +++--------------
 contrib/sepgsql/hooks.c                       |  4 +--
 contrib/sepgsql/label.c                       | 22 ++++-----------
 contrib/sepgsql/selinux.c                     |  4 +--
 contrib/sepgsql/uavc.c                        |  5 +---
 src/backend/catalog/index.c                   |  8 ++----
 src/backend/commands/async.c                  |  9 +-----
 src/backend/commands/copy.c                   |  4 +--
 src/backend/commands/event_trigger.c          | 10 ++-----
 src/backend/commands/extension.c              |  6 +---
 src/backend/commands/subscriptioncmds.c       | 13 ++-------
 src/backend/commands/trigger.c                |  4 +--
 src/backend/commands/vacuum.c                 |  6 +---
 src/backend/libpq/be-fsstubs.c                |  4 +--
 src/backend/tcop/utility.c                    | 10 ++-----
 src/backend/utils/adt/xml.c                   | 20 +++----------
 src/pl/plperl/plperl.c                        | 16 ++---------
 src/pl/plpgsql/src/pl_handler.c               |  9 ++----
 src/pl/plpython/plpy_cursorobject.c           |  4 +--
 src/pl/plpython/plpy_elog.c                   | 13 +--------
 src/pl/plpython/plpy_exec.c                   | 12 ++------
 src/pl/plpython/plpy_spi.c                    |  4 +--
 src/pl/plpython/plpy_typeio.c                 |  5 +---
 src/pl/tcl/pltcl.c                            | 14 ++--------
 32 files changed, 82 insertions(+), 236 deletions(-)

diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index ba0b7f6f79..36406b1cf9 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -277,6 +277,25 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
  * (sub)transaction abort. Failure to do so may leave the system in an
  * inconsistent state for further processing.
  *
+ * For the common case that the error recovery code and the cleanup in the
+ * normal code path are identical, the following can be used instead:
+ *
+ *		PG_TRY();
+ *		{
+ *			... code that might throw ereport(ERROR) ...
+ *		}
+ *		PG_FINALLY();
+ *		{
+ *			... cleanup code ...
+ *		}
+ *      PG_END_TRY();
+ *
+ * The cleanup code will be run in either case, and any error will be rethrown
+ * afterwards.
+ *
+ * You cannot use both PG_CATCH() and PG_FINALLY() in the same
+ * PG_TRY()/PG_END_TRY() block.
+ *
  * Note: while the system will correctly propagate any new ereport(ERROR)
  * occurring in the recovery section, there is a small limit on the number
  * of levels this will work for.  It's best to keep the error recovery
@@ -303,6 +322,7 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
 		sigjmp_buf *save_exception_stack = PG_exception_stack; \
 		ErrorContextCallback *save_context_stack = error_context_stack; \
 		sigjmp_buf local_sigjmp_buf; \
+		bool do_rethrow = false; \
 		if (sigsetjmp(local_sigjmp_buf, 0) == 0) \
 		{ \
 			PG_exception_stack = &local_sigjmp_buf
@@ -314,10 +334,18 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
 			PG_exception_stack = save_exception_stack; \
 			error_context_stack = save_context_stack
 
+#define PG_FINALLY() \
+		} \
+		else \
+			do_rethrow = true; \
+		{
+
 #define PG_END_TRY()  \
 		} \
 		PG_exception_stack = save_exception_stack; \
 		error_context_stack = save_context_stack; \
+		if (do_rethrow) \
+				PG_RE_THROW(); \
 	} while (0)
 
 /*
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index a9536c2de0..f118dbaedd 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -320,12 +320,10 @@ explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction,
 			prev_ExecutorRun(queryDesc, direction, count, execute_once);
 		else
 			standard_ExecutorRun(queryDesc, direction, count, execute_once);
-		nesting_level--;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		nesting_level--;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 }
@@ -343,12 +341,10 @@ explain_ExecutorFinish(QueryDesc *queryDesc)
 			prev_ExecutorFinish(queryDesc);
 		else
 			standard_ExecutorFinish(queryDesc);
-		nesting_level--;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		nesting_level--;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 }
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 54b7bf952f..7e225589a9 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -776,19 +776,14 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
 			}
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* if needed, close the connection to the database */
 		if (freeconn)
 			PQfinish(conn);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	/* if needed, close the connection to the database */
-	if (freeconn)
-		PQfinish(conn);
-
 	return (Datum) 0;
 }
 
@@ -952,14 +947,11 @@ materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
 			/* clean up and return the tuplestore */
 			tuplestore_donestoring(tupstore);
 		}
-
-		PQclear(res);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* be sure to release the libpq result */
 		PQclear(res);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 }
@@ -1464,19 +1456,14 @@ dblink_exec(PG_FUNCTION_ARGS)
 					 errmsg("statement returning results not allowed")));
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* if needed, close the connection to the database */
 		if (freeconn)
 			PQfinish(conn);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	/* if needed, close the connection to the database */
-	if (freeconn)
-		PQfinish(conn);
-
 	PG_RETURN_TEXT_P(sql_cmd_status);
 }
 
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index 4dee569740..39bad55802 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -180,14 +180,11 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
 		pcount = hstoreUniquePairs(pairs, pcount, &buflen);
 		out = hstorePairs(pairs, pcount, buflen);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		Py_DECREF(items);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	Py_DECREF(items);
-
 	PG_RETURN_POINTER(out);
 }
diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c
index ecaa4c6f92..b41c738ad6 100644
--- a/contrib/jsonb_plpython/jsonb_plpython.c
+++ b/contrib/jsonb_plpython/jsonb_plpython.c
@@ -307,15 +307,12 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
 
 		out = pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		Py_DECREF(items);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	Py_DECREF(items);
-
 	return out;
 }
 
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 221b47298c..63b5888ebb 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -892,12 +892,10 @@ pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count,
 			prev_ExecutorRun(queryDesc, direction, count, execute_once);
 		else
 			standard_ExecutorRun(queryDesc, direction, count, execute_once);
-		nested_level--;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		nested_level--;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 }
@@ -915,12 +913,10 @@ pgss_ExecutorFinish(QueryDesc *queryDesc)
 			prev_ExecutorFinish(queryDesc);
 		else
 			standard_ExecutorFinish(queryDesc);
-		nested_level--;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		nested_level--;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 }
@@ -1007,12 +1003,10 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 				standard_ProcessUtility(pstmt, queryString,
 										context, params, queryEnv,
 										dest, completionTag);
-			nested_level--;
 		}
-		PG_CATCH();
+		PG_FINALLY();
 		{
 			nested_level--;
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
 
diff --git a/contrib/pg_trgm/trgm_regexp.c b/contrib/pg_trgm/trgm_regexp.c
index 7965a29c9f..e65e683823 100644
--- a/contrib/pg_trgm/trgm_regexp.c
+++ b/contrib/pg_trgm/trgm_regexp.c
@@ -555,15 +555,12 @@ createTrgmNFA(text *text_re, Oid collation,
 	{
 		trg = createTrgmNFAInternal(&regex, graph, rcontext);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		pg_regfree(&regex);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	pg_regfree(&regex);
-
 	/* Clean up all the cruft we created */
 	MemoryContextSwitchTo(oldcontext);
 	MemoryContextDelete(tmpcontext);
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 12f9dd35be..7cd69cc709 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -631,15 +631,12 @@ pgfdw_report_error(int elevel, PGresult *res, PGconn *conn,
 				 message_context ? errcontext("%s", message_context) : 0,
 				 sql ? errcontext("remote SQL command: %s", sql) : 0));
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (clear)
 			PQclear(res);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	if (clear)
-		PQclear(res);
 }
 
 /*
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 32366641a3..fa142960eb 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3155,15 +3155,11 @@ get_remote_estimate(const char *sql, PGconn *conn,
 				   startup_cost, total_cost, rows, width);
 		if (n != 4)
 			elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
-
-		PQclear(res);
-		res = NULL;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (res)
 			PQclear(res);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 }
@@ -3383,15 +3379,11 @@ fetch_more_data(ForeignScanState *node)
 
 		/* Must be EOF if we didn't get as many tuples as we asked for. */
 		fsstate->eof_reached = (numrows < fsstate->fetch_size);
-
-		PQclear(res);
-		res = NULL;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (res)
 			PQclear(res);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
@@ -4404,15 +4396,11 @@ postgresAnalyzeForeignTable(Relation relation,
 		if (PQntuples(res) != 1 || PQnfields(res) != 1)
 			elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
 		*totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
-
-		PQclear(res);
-		res = NULL;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (res)
 			PQclear(res);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
@@ -4925,16 +4913,11 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
 
 			commands = lappend(commands, pstrdup(buf.data));
 		}
-
-		/* Clean up */
-		PQclear(res);
-		res = NULL;
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (res)
 			PQclear(res);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 992c70e8a0..49f32ac4d3 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -372,13 +372,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
 									context, params, queryEnv,
 									dest, completionTag);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		sepgsql_context_info = saved_context_info;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	sepgsql_context_info = saved_context_info;
 }
 
 /*
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index d2505f7f34..d8a1d129d2 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -465,14 +465,11 @@ sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
 		{
 			label = pstrdup(unlabeled);
 		}
-		PG_CATCH();
+		PG_FINALLY();
 		{
 			freecon(unlabeled);
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
-
-		freecon(unlabeled);
 	}
 	return label;
 }
@@ -600,13 +597,11 @@ sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
 	{
 		result = pstrdup(raw_label);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		freecon(raw_label);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	freecon(raw_label);
 
 	PG_RETURN_TEXT_P(cstring_to_text(result));
 }
@@ -640,13 +635,11 @@ sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
 	{
 		result = pstrdup(qual_label);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		freecon(qual_label);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	freecon(qual_label);
 
 	PG_RETURN_TEXT_P(cstring_to_text(result));
 }
@@ -851,13 +844,11 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
 
 				SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
 			}
-			PG_CATCH();
+			PG_FINALLY();
 			{
 				freecon(context);
-				PG_RE_THROW();
 			}
 			PG_END_TRY();
-			freecon(context);
 		}
 		else if (errno == ENOENT)
 			ereport(WARNING,
@@ -937,14 +928,11 @@ sepgsql_restorecon(PG_FUNCTION_ARGS)
 		exec_object_restorecon(sehnd, AttributeRelationId);
 		exec_object_restorecon(sehnd, ProcedureRelationId);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		selabel_close(sehnd);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	selabel_close(sehnd);
-
 	PG_RETURN_BOOL(true);
 }
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
index 192aabea0b..b7c489cc33 100644
--- a/contrib/sepgsql/selinux.c
+++ b/contrib/sepgsql/selinux.c
@@ -871,13 +871,11 @@ sepgsql_compute_create(const char *scontext,
 	{
 		result = pstrdup(ncontext);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		freecon(ncontext);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	freecon(ncontext);
 
 	return result;
 }
diff --git a/contrib/sepgsql/uavc.c b/contrib/sepgsql/uavc.c
index 8ce0bc631b..f5279cc9b6 100644
--- a/contrib/sepgsql/uavc.c
+++ b/contrib/sepgsql/uavc.c
@@ -181,14 +181,11 @@ sepgsql_avc_unlabeled(void)
 		{
 			avc_unlabeled = MemoryContextStrdup(avc_mem_cxt, unlabeled);
 		}
-		PG_CATCH();
+		PG_FINALLY();
 		{
 			freecon(unlabeled);
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
-
-		freecon(unlabeled);
 	}
 	return avc_unlabeled;
 }
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 78896da391..7ab6d7d983 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -3446,14 +3446,12 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 		/* Note: we do not need to re-establish pkey setting */
 		index_build(heapRelation, iRel, indexInfo, true, true);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* Make sure flag gets cleared on error exit */
 		ResetReindexProcessing();
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	ResetReindexProcessing();
 
 	/*
 	 * If the index is marked invalid/not-ready/dead (ie, it's from a failed
@@ -3673,14 +3671,12 @@ reindex_relation(Oid relid, int flags, int options)
 			i++;
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* Make sure list gets cleared on error exit */
 		ResetReindexPending();
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	ResetReindexPending();
 
 	/*
 	 * Close rel, but continue to hold the lock.
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index d0649d2e3e..a3209d076b 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -2028,22 +2028,15 @@ asyncQueueReadAllNotifications(void)
 													   snapshot);
 		} while (!reachedStop);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* Update shared state */
 		LWLockAcquire(AsyncQueueLock, LW_SHARED);
 		QUEUE_BACKEND_POS(MyBackendId) = pos;
 		LWLockRelease(AsyncQueueLock);
-
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	/* Update shared state */
-	LWLockAcquire(AsyncQueueLock, LW_SHARED);
-	QUEUE_BACKEND_POS(MyBackendId) = pos;
-	LWLockRelease(AsyncQueueLock);
-
 	/* Done with snapshot */
 	UnregisterSnapshot(snapshot);
 }
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3aeef30b28..e17d8c760f 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1916,13 +1916,11 @@ BeginCopyTo(ParseState *pstate,
 			{
 				cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W);
 			}
-			PG_CATCH();
+			PG_FINALLY();
 			{
 				umask(oumask);
-				PG_RE_THROW();
 			}
 			PG_END_TRY();
-			umask(oumask);
 			if (cstate->copy_file == NULL)
 			{
 				/* copy errno because ereport subfunctions might change it */
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index f7ee9838f7..0301ae1ddd 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -934,13 +934,11 @@ EventTriggerSQLDrop(Node *parsetree)
 	{
 		EventTriggerInvoke(runlist, &trigdata);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		currentEventTriggerState->in_sql_drop = false;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	currentEventTriggerState->in_sql_drop = false;
 
 	/* Cleanup. */
 	list_free(runlist);
@@ -1007,17 +1005,13 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
 	{
 		EventTriggerInvoke(runlist, &trigdata);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		currentEventTriggerState->table_rewrite_oid = InvalidOid;
 		currentEventTriggerState->table_rewrite_reason = 0;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	currentEventTriggerState->table_rewrite_oid = InvalidOid;
-	currentEventTriggerState->table_rewrite_reason = 0;
-
 	/* Cleanup. */
 	list_free(runlist);
 
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index f7202cc9e7..a04b0c9e57 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -942,17 +942,13 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 
 		execute_sql_string(c_sql);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		creating_extension = false;
 		CurrentExtensionObject = InvalidOid;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	creating_extension = false;
-	CurrentExtensionObject = InvalidOid;
-
 	/*
 	 * Restore the GUC variables we set above.
 	 */
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 2e67a5889e..1419195766 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -493,16 +493,11 @@ CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
 								slotname)));
 			}
 		}
-		PG_CATCH();
+		PG_FINALLY();
 		{
-			/* Close the connection in case of failure. */
 			walrcv_disconnect(wrconn);
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
-
-		/* And we are done with the remote side. */
-		walrcv_disconnect(wrconn);
 	}
 	else
 		ereport(WARNING,
@@ -1023,16 +1018,12 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
 
 		walrcv_clear_result(res);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
-		/* Close the connection in case of failure */
 		walrcv_disconnect(wrconn);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	walrcv_disconnect(wrconn);
-
 	pfree(cmd.data);
 
 	table_close(rel, NoLock);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 7ba859d446..0b84de5943 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2431,13 +2431,11 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 	{
 		result = FunctionCallInvoke(fcinfo);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		MyTriggerDepth--;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	MyTriggerDepth--;
 
 	pgstat_end_function_usage(&fcusage, true);
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 4b67b40b28..da1da23400 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -430,17 +430,13 @@ vacuum(List *relations, VacuumParams *params,
 			}
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		in_vacuum = false;
 		VacuumCostActive = false;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	in_vacuum = false;
-	VacuumCostActive = false;
-
 	/*
 	 * Finish up processing.
 	 */
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index b0ece7ec25..1ee96c0b9f 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -503,13 +503,11 @@ be_lo_export(PG_FUNCTION_ARGS)
 		fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
 								   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		umask(oumask);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-	umask(oumask);
 	if (fd < 0)
 		ereport(ERROR,
 				(errcode_for_file_access(),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index c6faa6619d..f2269ad35c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1514,13 +1514,11 @@ ProcessUtilitySlow(ParseState *pstate,
 					address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
 												 queryString, params, completionTag);
 				}
-				PG_CATCH();
+				PG_FINALLY();
 				{
 					EventTriggerUndoInhibitCommandCollection();
-					PG_RE_THROW();
 				}
 				PG_END_TRY();
-				EventTriggerUndoInhibitCommandCollection();
 				break;
 
 			case T_CreateTrigStmt:
@@ -1716,16 +1714,12 @@ ProcessUtilitySlow(ParseState *pstate,
 			EventTriggerDDLCommandEnd(parsetree);
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (needCleanup)
 			EventTriggerEndCompleteQuery();
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-
-	if (needCleanup)
-		EventTriggerEndCompleteQuery();
 }
 
 /*
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 0280c2625c..c397461ad5 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -1193,13 +1193,11 @@ xml_pstrdup_and_free(xmlChar *str)
 		{
 			result = pstrdup((char *) str);
 		}
-		PG_CATCH();
+		PG_FINALLY();
 		{
 			xmlFree(str);
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
-		xmlFree(str);
 	}
 	else
 		result = NULL;
@@ -3866,19 +3864,14 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 
 			result = xmlBuffer_to_xmltype(buf);
 		}
-		PG_CATCH();
+		PG_FINALLY()
 		{
 			if (nodefree)
 				nodefree(cur_copy);
 			if (buf)
 				xmlBufferFree(buf);
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
-
-		if (nodefree)
-			nodefree(cur_copy);
-		xmlBufferFree(buf);
 	}
 	else
 	{
@@ -3893,13 +3886,11 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 			result = (xmltype *) cstring_to_text(escaped);
 			pfree(escaped);
 		}
-		PG_CATCH();
+		PG_FINALLY();
 		{
 			xmlFree(str);
-			PG_RE_THROW();
 		}
 		PG_END_TRY();
-		xmlFree(str);
 	}
 
 	return result;
@@ -4734,16 +4725,13 @@ XmlTableGetValue(TableFuncScanState *state, int colnum,
 									   state->typioparams[colnum],
 									   typmod);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (xpathobj != NULL)
 			xmlXPathFreeObject(xpathobj);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	xmlXPathFreeObject(xpathobj);
-
 	return result;
 #else
 	NO_XML_SUPPORT();
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index c480999c51..f0fb308552 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1862,20 +1862,15 @@ plperl_call_handler(PG_FUNCTION_ARGS)
 		else
 			retval = plperl_func_handler(fcinfo);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		current_call_data = save_call_data;
 		activate_interpreter(oldinterp);
 		if (this_call_data.prodesc)
 			decrement_prodesc_refcount(this_call_data.prodesc);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	current_call_data = save_call_data;
-	activate_interpreter(oldinterp);
-	if (this_call_data.prodesc)
-		decrement_prodesc_refcount(this_call_data.prodesc);
 	return retval;
 }
 
@@ -1958,22 +1953,15 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		if (SPI_finish() != SPI_OK_FINISH)
 			elog(ERROR, "SPI_finish() failed");
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (desc.reference)
 			SvREFCNT_dec_current(desc.reference);
 		current_call_data = save_call_data;
 		activate_interpreter(oldinterp);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	if (desc.reference)
-		SvREFCNT_dec_current(desc.reference);
-
-	current_call_data = save_call_data;
-	activate_interpreter(oldinterp);
-
 	error_context_stack = pl_error_context.previous;
 
 	PG_RETURN_VOID();
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index e92deb32ca..1b592c8a52 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -264,19 +264,14 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
 		else
 			retval = plpgsql_exec_function(func, fcinfo, NULL, !nonatomic);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
-		/* Decrement use-count, restore cur_estate, and propagate error */
+		/* Decrement use-count, restore cur_estate */
 		func->use_count--;
 		func->cur_estate = save_cur_estate;
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	func->use_count--;
-
-	func->cur_estate = save_cur_estate;
-
 	/*
 	 * Disconnect from SPI manager
 	 */
diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index e4d543a4d4..b44ce7e225 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -228,13 +228,11 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 				plan->values[j] = PLy_output_convert(arg, elem, &isnull);
 				nulls[j] = isnull ? 'n' : ' ';
 			}
-			PG_CATCH();
+			PG_FINALLY();
 			{
 				Py_DECREF(elem);
-				PG_RE_THROW();
 			}
 			PG_END_TRY();
-			Py_DECREF(elem);
 		}
 
 		portal = SPI_cursor_open(NULL, plan->plan, plan->values, nulls,
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 25930f99d7..15cc444af8 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -141,7 +141,7 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 				 (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME,
 														constraint_name) : 0));
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		if (fmt)
 			pfree(emsg.data);
@@ -151,19 +151,8 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 			pfree(tbmsg);
 		Py_XDECREF(exc);
 		Py_XDECREF(val);
-
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
-
-	if (fmt)
-		pfree(emsg.data);
-	if (xmsg)
-		pfree(xmsg);
-	if (tbmsg)
-		pfree(tbmsg);
-	Py_XDECREF(exc);
-	Py_XDECREF(val);
 }
 
 /*
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 920322e912..6994d7c10b 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -403,18 +403,13 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
 			}
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		Py_XDECREF(plargs);
 		Py_XDECREF(plrv);
-
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	Py_DECREF(plargs);
-	Py_DECREF(plrv);
-
 	return rv;
 }
 
@@ -1052,15 +1047,12 @@ PLy_procedure_call(PLyProcedure *proc, const char *kargs, PyObject *vargs)
 		 */
 		Assert(list_length(explicit_subtransactions) >= save_subxact_level);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		PLy_abort_open_subtransactions(save_subxact_level);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	PLy_abort_open_subtransactions(save_subxact_level);
-
 	/* If the Python code returned an error, propagate it */
 	if (rv == NULL)
 		PLy_elog(ERROR, NULL);
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 2fe435d42b..ed2eee0603 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -249,13 +249,11 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 				plan->values[j] = PLy_output_convert(arg, elem, &isnull);
 				nulls[j] = isnull ? 'n' : ' ';
 			}
-			PG_CATCH();
+			PG_FINALLY();
 			{
 				Py_DECREF(elem);
-				PG_RE_THROW();
 			}
 			PG_END_TRY();
-			Py_DECREF(elem);
 		}
 
 		rv = SPI_execute_plan(plan->plan, plan->values, nulls,
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 371e534fd2..589c76e7a7 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -925,15 +925,12 @@ PLyObject_ToBytea(PLyObToDatum *arg, PyObject *plrv,
 		memcpy(VARDATA(result), plrv_sc, len);
 		rv = PointerGetDatum(result);
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		Py_XDECREF(plrv_so);
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	Py_XDECREF(plrv_so);
-
 	return rv;
 }
 
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 8277d1ea85..fccd22b4f5 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -765,9 +765,10 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
 			retval = pltcl_func_handler(fcinfo, &current_call_state, pltrusted);
 		}
 	}
-	PG_CATCH();
+	PG_FINALLY();
 	{
 		/* Restore static pointer, then clean up the prodesc refcount if any */
+		/* (We're being paranoid in case an error is thrown in context deletion) */
 		pltcl_current_call_state = save_call_state;
 		if (current_call_state.prodesc != NULL)
 		{
@@ -775,20 +776,9 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
 			if (--current_call_state.prodesc->fn_refcount == 0)
 				MemoryContextDelete(current_call_state.prodesc->fn_cxt);
 		}
-		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	/* Restore static pointer, then clean up the prodesc refcount if any */
-	/* (We're being paranoid in case an error is thrown in context deletion) */
-	pltcl_current_call_state = save_call_state;
-	if (current_call_state.prodesc != NULL)
-	{
-		Assert(current_call_state.prodesc->fn_refcount > 0);
-		if (--current_call_state.prodesc->fn_refcount == 0)
-			MemoryContextDelete(current_call_state.prodesc->fn_cxt);
-	}
-
 	return retval;
 }
 

base-commit: 61ecea45e50bcd3b87d4e905719e63e41d6321ce
-- 
2.23.0

#9Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#8)
Re: alternative to PG_CATCH

On Mon, Oct 28, 2019 at 4:43 AM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

Here is a new implementation that works just like that.

This looks like a marked notational improvement.

With the patch:

[rhaas pgsql]$ git grep PG_CATCH | wc -l
102
[rhaas pgsql]$ git grep PG_FINALLY | wc -l
55

I'm actually a bit surprised that the percentage of cases that could
be converted to use PG_FINALLY wasn't even higher than that.

In theory, the do_rethrow variable could conflict with a symbol
declared in the surrounding scope, but that doesn't seem like it's a
problem worth getting worked up about.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#10Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Robert Haas (#9)
Re: alternative to PG_CATCH

On 2019-10-28 13:45, Robert Haas wrote:

In theory, the do_rethrow variable could conflict with a symbol
declared in the surrounding scope, but that doesn't seem like it's a
problem worth getting worked up about.

Right. A PG_TRY block also declares other local variables for internal
use without much care about namespacing. If it becomes a problem, it's
easy to address.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#10)
Re: alternative to PG_CATCH

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 2019-10-28 13:45, Robert Haas wrote:

In theory, the do_rethrow variable could conflict with a symbol
declared in the surrounding scope, but that doesn't seem like it's a
problem worth getting worked up about.

Right. A PG_TRY block also declares other local variables for internal
use without much care about namespacing. If it becomes a problem, it's
easy to address.

Although we haven't been terribly consistent about it, some of our macros
address this problem by using local variable names with a leading and/or
trailing underscore, or otherwise making them names you'd be quite
unlikely to use in normal code. I suggest doing something similar
here. (Wouldn't be a bad idea to make PG_TRY's variables follow suit.)

regards, tom lane

#12Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#11)
Re: alternative to PG_CATCH

On 2019-10-29 17:10, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 2019-10-28 13:45, Robert Haas wrote:

In theory, the do_rethrow variable could conflict with a symbol
declared in the surrounding scope, but that doesn't seem like it's a
problem worth getting worked up about.

Right. A PG_TRY block also declares other local variables for internal
use without much care about namespacing. If it becomes a problem, it's
easy to address.

Although we haven't been terribly consistent about it, some of our macros
address this problem by using local variable names with a leading and/or
trailing underscore, or otherwise making them names you'd be quite
unlikely to use in normal code. I suggest doing something similar
here. (Wouldn't be a bad idea to make PG_TRY's variables follow suit.)

committed with a leading underscore

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#12)
Re: alternative to PG_CATCH

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

committed with a leading underscore

I hadn't actually tested this patch before commit, but now that
it's in, I'm seeing assorted compiler warnings:

plpy_exec.c: In function 'PLy_procedure_call':
plpy_exec.c:1028: warning: 'rv' may be used uninitialized in this function
pltcl.c: In function 'pltcl_handler':
pltcl.c:721: warning: 'retval' may be used uninitialized in this function
plpy_typeio.c: In function 'PLyObject_ToBytea':
plpy_typeio.c:904: warning: 'rv' may be used uninitialized in this function
plperl.c: In function 'plperl_call_handler':
plperl.c:1843: warning: 'retval' may be used uninitialized in this function

I'm not happy about that.

regards, tom lane

#14Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#13)
Re: alternative to PG_CATCH

On 2019-11-02 15:36, Tom Lane wrote:

I hadn't actually tested this patch before commit, but now that
it's in, I'm seeing assorted compiler warnings:

I've fixed the ones that I could reproduce on CentOS 6. I haven't seen
any on a variety of newer systems.

It's not clear why only a handful of cases cause warnings, but my guess
is that the functions are above some size/complexity threshold beyond
which those older compilers give up doing a full analysis.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#14)
Re: alternative to PG_CATCH

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 2019-11-02 15:36, Tom Lane wrote:

I hadn't actually tested this patch before commit, but now that
it's in, I'm seeing assorted compiler warnings:

I've fixed the ones that I could reproduce on CentOS 6. I haven't seen
any on a variety of newer systems.

I'd hoped for a way to fix PG_FINALLY rather than having to band-aid
the individual callers :-(. But maybe there isn't one.

Now that I've actually looked at the patched code, there's a far
more severe problem with it. Namely, that use of PG_FINALLY
means that the "finally" segment is run without having popped
the error context stack, which means that any error thrown
within that segment will sigsetjmp right back to the top,
resulting in an infinite loop. (Well, not infinite, because
it'll crash out once the error nesting depth limit is hit.)
We *must* pop the stack before running recovery code.

Possibly this could be fixed like so:

#define PG_FINALLY() \
} \
else \
{ \
PG_exception_stack = _save_exception_stack; \
error_context_stack = _save_context_stack; \
_do_rethrow = true

#define PG_END_TRY() \
} \
if (_do_rethrow) \
PG_RE_THROW(); \
PG_exception_stack = _save_exception_stack; \
error_context_stack = _save_context_stack; \
} while (0)

But I haven't tested that.

regards, tom lane

#16Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#15)
1 attachment(s)
Re: alternative to PG_CATCH

On 2019-11-04 16:01, Tom Lane wrote:

Now that I've actually looked at the patched code, there's a far
more severe problem with it. Namely, that use of PG_FINALLY
means that the "finally" segment is run without having popped
the error context stack, which means that any error thrown
within that segment will sigsetjmp right back to the top,
resulting in an infinite loop. (Well, not infinite, because
it'll crash out once the error nesting depth limit is hit.)
We *must* pop the stack before running recovery code.

I can confirm that that indeed happens. :(

Here is a patch to fix it.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-Fix-nested-error-handling-in-PG_FINALLY.patchtext/plain; charset=UTF-8; name=0001-Fix-nested-error-handling-in-PG_FINALLY.patch; x-mac-creator=0; x-mac-type=0Download
From aa758de0919e2356bf46390b89d8b17d20b8c09e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 5 Nov 2019 22:01:18 +0100
Subject: [PATCH] Fix nested error handling in PG_FINALLY

We need to pop the error stack before running the user-supplied
PG_FINALLY code.  Otherwise an error in the cleanup code would end up
at the same sigsetjmp() invocation and result in an infinite error
handling loop.
---
 src/backend/utils/adt/xml.c |  2 +-
 src/include/utils/elog.h    | 11 ++++++++---
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 3a493dd6bf..2256c24137 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -3864,7 +3864,7 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 
 			result = xmlBuffer_to_xmltype(buf);
 		}
-		PG_FINALLY()
+		PG_FINALLY();
 		{
 			if (nodefree)
 				nodefree(cur_copy);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 853c2e0709..93f26876b0 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -338,14 +338,19 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
 		} \
 		else \
 			_do_rethrow = true; \
-		{
+		{ \
+			PG_exception_stack = _save_exception_stack; \
+			error_context_stack = _save_context_stack
 
 #define PG_END_TRY()  \
 		} \
-		PG_exception_stack = _save_exception_stack; \
-		error_context_stack = _save_context_stack; \
 		if (_do_rethrow) \
 				PG_RE_THROW(); \
+		else \
+		{ \
+			PG_exception_stack = _save_exception_stack; \
+			error_context_stack = _save_context_stack; \
+		} \
 	} while (0)
 
 /*
-- 
2.23.0

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#16)
Re: alternative to PG_CATCH

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 2019-11-04 16:01, Tom Lane wrote:

Now that I've actually looked at the patched code, there's a far
more severe problem with it. Namely, that use of PG_FINALLY
means that the "finally" segment is run without having popped
the error context stack, which means that any error thrown
within that segment will sigsetjmp right back to the top,
resulting in an infinite loop. (Well, not infinite, because
it'll crash out once the error nesting depth limit is hit.)
We *must* pop the stack before running recovery code.

I can confirm that that indeed happens. :(

Here is a patch to fix it.

This seems all right from here. Since PG_RE_THROW() is guaranteed
noreturn, I personally wouldn't have bothered with an "else" after it,
but that's just stylistic.

regards, tom lane

#18Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#17)
Re: alternative to PG_CATCH

On 2019-11-06 15:49, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

On 2019-11-04 16:01, Tom Lane wrote:

Now that I've actually looked at the patched code, there's a far
more severe problem with it. Namely, that use of PG_FINALLY
means that the "finally" segment is run without having popped
the error context stack, which means that any error thrown
within that segment will sigsetjmp right back to the top,
resulting in an infinite loop. (Well, not infinite, because
it'll crash out once the error nesting depth limit is hit.)
We *must* pop the stack before running recovery code.

I can confirm that that indeed happens. :(

Here is a patch to fix it.

This seems all right from here. Since PG_RE_THROW() is guaranteed
noreturn, I personally wouldn't have bothered with an "else" after it,
but that's just stylistic.

Committed, without the "else".

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services