0002-Don-t-call-data-type-input-functions-in-GUC-check-ho.patch

text/plain

Filename: 0002-Don-t-call-data-type-input-functions-in-GUC-check-ho.patch
Type: text/plain
Part: 1
Message: Re: check_recovery_target_lsn() does a PG_CATCH without a throw

Patch

Format: format-patch
Series: patch 0002
Subject: Don't call data type input functions in GUC check hooks
File+
src/backend/utils/misc/guc.c 53 50
From dc5011a7a798b1dfc5208bd41e83f002e708c7d1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 12 Jun 2019 11:12:13 +0200
Subject: [PATCH 2/2] Don't call data type input functions in GUC check hooks

Instead of calling pg_lsn_in() in check_recovery_target_lsn and
timestamptz_in() in check_recovery_target_time, replicate the
respective code to some degree.  The previous code tried to use
PG_TRY/PG_CATCH to handle errors in a way that is not safe, so now the
code contains no ereport() calls and can operate safely within the GUC
error handling system.
---
 src/backend/utils/misc/guc.c | 103 ++++++++++++++++++-----------------
 1 file changed, 53 insertions(+), 50 deletions(-)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1208eb9a68..3a52b4090b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -86,7 +86,6 @@
 #include "utils/float.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
-#include "utils/pg_lsn.h"
 #include "utils/plancache.h"
 #include "utils/portal.h"
 #include "utils/ps_status.h"
@@ -11577,15 +11576,11 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
 {
 	if (strcmp(*newval, "") != 0)
 	{
-		TimestampTz time;
+		TimestampTz timestamp;
 		TimestampTz *myextra;
-		MemoryContext oldcontext = CurrentMemoryContext;
 
 		/* reject some special values */
-		if (strcmp(*newval, "epoch") == 0 ||
-			strcmp(*newval, "infinity") == 0 ||
-			strcmp(*newval, "-infinity") == 0 ||
-			strcmp(*newval, "now") == 0 ||
+		if (strcmp(*newval, "now") == 0 ||
 			strcmp(*newval, "today") == 0 ||
 			strcmp(*newval, "tomorrow") == 0 ||
 			strcmp(*newval, "yesterday") == 0)
@@ -11593,31 +11588,40 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
 			return false;
 		}
 
-		PG_TRY();
-		{
-			time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-														   CStringGetDatum(*newval),
-														   ObjectIdGetDatum(InvalidOid),
-														   Int32GetDatum(-1)));
-		}
-		PG_CATCH();
+		/*
+		 * parse timestamp value (see also timestamptz_in())
+		 */
 		{
-			ErrorData  *edata;
-
-			/* Save error info */
-			MemoryContextSwitchTo(oldcontext);
-			edata = CopyErrorData();
-			FlushErrorState();
-
-			/* Pass the error message */
-			GUC_check_errdetail("%s", edata->message);
-			FreeErrorData(edata);
-			return false;
+			char	   *str = *newval;
+			fsec_t		fsec;
+			struct pg_tm tt,
+					   *tm = &tt;
+			int			tz;
+			int			dtype;
+			int			nf;
+			int			dterr;
+			char	   *field[MAXDATEFIELDS];
+			int			ftype[MAXDATEFIELDS];
+			char		workbuf[MAXDATELEN + MAXDATEFIELDS];
+
+			dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+								  field, ftype, MAXDATEFIELDS, &nf);
+			if (dterr == 0)
+				dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
+			if (dterr != 0)
+				return false;
+			if (dtype !=  DTK_DATE)
+				return false;
+
+			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
+			{
+				GUC_check_errdetail("timestamp out of range: \"%s\"", str);
+				return false;
+			}
 		}
-		PG_END_TRY();
 
 		myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
-		*myextra = time;
+		*myextra = timestamp;
 		*extra = (void *) myextra;
 	}
 	return true;
@@ -11668,6 +11672,8 @@ assign_recovery_target_name(const char *newval, void *extra)
 		recoveryTarget = RECOVERY_TARGET_UNSET;
 }
 
+#define MAXPG_LSNCOMPONENT	8
+
 static bool
 check_recovery_target_lsn(char **newval, void **extra, GucSource source)
 {
@@ -11675,33 +11681,30 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
 	{
 		XLogRecPtr	lsn;
 		XLogRecPtr *myextra;
-		MemoryContext oldcontext = CurrentMemoryContext;
 
 		/*
-		 * Convert the LSN string given by the user to XLogRecPtr form.
+		 * Convert the LSN string given by the user to XLogRecPtr form (see
+		 * also pg_lsn_in()).
 		 */
-		PG_TRY();
-		{
-			lsn = DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
-												  CStringGetDatum(*newval),
-												  ObjectIdGetDatum(InvalidOid),
-												  Int32GetDatum(-1)));
-		}
-		PG_CATCH();
 		{
-			ErrorData  *edata;
-
-			/* Save error info */
-			MemoryContextSwitchTo(oldcontext);
-			edata = CopyErrorData();
-			FlushErrorState();
-
-			/* Pass the error message */
-			GUC_check_errdetail("%s", edata->message);
-			FreeErrorData(edata);
-			return false;
+			char	   *str = *newval;
+			size_t		len1;
+			size_t		len2;
+			uint32		id;
+			uint32		off;
+
+			len1 = strspn(str, "0123456789abcdefABCDEF");
+			if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
+				return false;
+
+			len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
+			if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
+				return false;
+
+			id = (uint32) strtoul(str, NULL, 16);
+			off = (uint32) strtoul(str + len1 + 1, NULL, 16);
+			lsn = ((uint64) id << 32) | off;
 		}
-		PG_END_TRY();
 
 		myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
 		*myextra = lsn;
-- 
2.22.0