diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y
index 977dcba965..e6e361736c 100644
--- a/contrib/cube/cubeparse.y
+++ b/contrib/cube/cubeparse.y
@@ -190,18 +190,18 @@ write_box(int dim, char *str1, char *str2)
 	s = str1;
 	i = 0;
 	if (dim > 0)
-		bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
+		bp->x[i++] = float8in_internal(s, &endptr, "cube", str1, NULL);
 	while ((s = strchr(s, ',')) != NULL)
 	{
 		s++;
-		bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
+		bp->x[i++] = float8in_internal(s, &endptr, "cube", str1, NULL);
 	}
 	Assert(i == dim);
 
 	s = str2;
 	if (dim > 0)
 	{
-		bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
+		bp->x[i] = float8in_internal(s, &endptr, "cube", str2, NULL);
 		/* code this way to do right thing with NaN */
 		point &= (bp->x[i] == bp->x[0]);
 		i++;
@@ -209,7 +209,7 @@ write_box(int dim, char *str1, char *str2)
 	while ((s = strchr(s, ',')) != NULL)
 	{
 		s++;
-		bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
+		bp->x[i] = float8in_internal(s, &endptr, "cube", str2, NULL);
 		point &= (bp->x[i] == bp->x[i - dim]);
 		i++;
 	}
@@ -250,11 +250,11 @@ write_point_as_box(int dim, char *str)
 	s = str;
 	i = 0;
 	if (dim > 0)
-		bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
+		bp->x[i++] = float8in_internal(s, &endptr, "cube", str, NULL);
 	while ((s = strchr(s, ',')) != NULL)
 	{
 		s++;
-		bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
+		bp->x[i++] = float8in_internal(s, &endptr, "cube", str, NULL);
 	}
 	Assert(i == dim);
 
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index da97538ebe..b02a19be24 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -163,6 +163,7 @@ Datum
 float4in(PG_FUNCTION_ARGS)
 {
 	char	   *num = PG_GETARG_CSTRING(0);
+	Node	   *escontext = fcinfo->context;
 	char	   *orig_num;
 	float		val;
 	char	   *endptr;
@@ -183,7 +184,7 @@ float4in(PG_FUNCTION_ARGS)
 	 * strtod() on different platforms.
 	 */
 	if (*num == '\0')
-		ereport(ERROR,
+		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
 						"real", orig_num)));
@@ -257,13 +258,13 @@ float4in(PG_FUNCTION_ARGS)
 				(val >= HUGE_VALF || val <= -HUGE_VALF)
 #endif
 				)
-				ereport(ERROR,
+				ereturn(escontext, (Datum) 0,
 						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 						 errmsg("\"%s\" is out of range for type real",
 								orig_num)));
 		}
 		else
-			ereport(ERROR,
+			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("invalid input syntax for type %s: \"%s\"",
 							"real", orig_num)));
@@ -275,7 +276,7 @@ float4in(PG_FUNCTION_ARGS)
 
 	/* if there is any junk left at the end of the string, bail out */
 	if (*endptr != '\0')
-		ereport(ERROR,
+		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
 						"real", orig_num)));
@@ -337,52 +338,40 @@ float8in(PG_FUNCTION_ARGS)
 {
 	char	   *num = PG_GETARG_CSTRING(0);
 
-	PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
+	PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num,
+									   fcinfo->context));
 }
 
-/* Convenience macro: set *have_error flag (if provided) or throw error */
-#define RETURN_ERROR(throw_error, have_error) \
-do { \
-	if (have_error) { \
-		*have_error = true; \
-		return 0.0; \
-	} else { \
-		throw_error; \
-	} \
-} while (0)
-
 /*
- * float8in_internal_opt_error - guts of float8in()
+ * float8in_internal - guts of float8in()
  *
  * This is exposed for use by functions that want a reasonably
  * platform-independent way of inputting doubles.  The behavior is
- * essentially like strtod + ereport on error, but note the following
+ * essentially like strtod + ereturn on error, but note the following
  * differences:
  * 1. Both leading and trailing whitespace are skipped.
- * 2. If endptr_p is NULL, we throw error if there's trailing junk.
+ * 2. If endptr_p is NULL, we report error if there's trailing junk.
  * Otherwise, it's up to the caller to complain about trailing junk.
  * 3. In event of a syntax error, the report mentions the given type_name
  * and prints orig_string as the input; this is meant to support use of
  * this function with types such as "box" and "point", where what we are
  * parsing here is just a substring of orig_string.
  *
+ * If escontext points to an ErrorSaveContext node, that is filled instead
+ * of throwing an error; the caller must check SOFT_ERROR_OCCURRED()
+ * to detect errors.
+ *
  * "num" could validly be declared "const char *", but that results in an
  * unreasonable amount of extra casting both here and in callers, so we don't.
- *
- * When "*have_error" flag is provided, it's set instead of throwing an
- * error.  This is helpful when caller need to handle errors by itself.
  */
-double
-float8in_internal_opt_error(char *num, char **endptr_p,
-							const char *type_name, const char *orig_string,
-							bool *have_error)
+float8
+float8in_internal(char *num, char **endptr_p,
+				  const char *type_name, const char *orig_string,
+				  struct Node *escontext)
 {
 	double		val;
 	char	   *endptr;
 
-	if (have_error)
-		*have_error = false;
-
 	/* skip leading whitespace */
 	while (*num != '\0' && isspace((unsigned char) *num))
 		num++;
@@ -392,11 +381,10 @@ float8in_internal_opt_error(char *num, char **endptr_p,
 	 * strtod() on different platforms.
 	 */
 	if (*num == '\0')
-		RETURN_ERROR(ereport(ERROR,
-							 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-							  errmsg("invalid input syntax for type %s: \"%s\"",
-									 type_name, orig_string))),
-					 have_error);
+		ereturn(escontext, 0,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
 
 	errno = 0;
 	val = strtod(num, &endptr);
@@ -469,20 +457,17 @@ float8in_internal_opt_error(char *num, char **endptr_p,
 				char	   *errnumber = pstrdup(num);
 
 				errnumber[endptr - num] = '\0';
-				RETURN_ERROR(ereport(ERROR,
-									 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-									  errmsg("\"%s\" is out of range for type double precision",
-											 errnumber))),
-							 have_error);
+				ereturn(escontext, 0,
+						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+						 errmsg("\"%s\" is out of range for type double precision",
+								errnumber)));
 			}
 		}
 		else
-			RETURN_ERROR(ereport(ERROR,
-								 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-								  errmsg("invalid input syntax for type "
-										 "%s: \"%s\"",
-										 type_name, orig_string))),
-						 have_error);
+			ereturn(escontext, 0,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							type_name, orig_string)));
 	}
 
 	/* skip trailing whitespace */
@@ -493,27 +478,14 @@ float8in_internal_opt_error(char *num, char **endptr_p,
 	if (endptr_p)
 		*endptr_p = endptr;
 	else if (*endptr != '\0')
-		RETURN_ERROR(ereport(ERROR,
-							 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-							  errmsg("invalid input syntax for type "
-									 "%s: \"%s\"",
-									 type_name, orig_string))),
-					 have_error);
+		ereturn(escontext, 0,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
 
 	return val;
 }
 
-/*
- * Interface to float8in_internal_opt_error() without "have_error" argument.
- */
-double
-float8in_internal(char *num, char **endptr_p,
-				  const char *type_name, const char *orig_string)
-{
-	return float8in_internal_opt_error(num, endptr_p, type_name,
-									   orig_string, NULL);
-}
-
 
 /*
  *		float8out		- converts float8 number to a string
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index d78002b901..721ce6634f 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -189,7 +189,7 @@ static float8
 single_decode(char *num, char **endptr_p,
 			  const char *type_name, const char *orig_string)
 {
-	return float8in_internal(num, endptr_p, type_name, orig_string);
+	return float8in_internal(num, endptr_p, type_name, orig_string, NULL);
 }								/* single_decode() */
 
 static void
@@ -212,7 +212,7 @@ pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
 	if ((has_delim = (*str == LDELIM)))
 		str++;
 
-	*x = float8in_internal(str, &str, type_name, orig_string);
+	*x = float8in_internal(str, &str, type_name, orig_string, NULL);
 
 	if (*str++ != DELIM)
 		ereport(ERROR,
@@ -220,7 +220,7 @@ pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
 				 errmsg("invalid input syntax for type %s: \"%s\"",
 						type_name, orig_string)));
 
-	*y = float8in_internal(str, &str, type_name, orig_string);
+	*y = float8in_internal(str, &str, type_name, orig_string, NULL);
 
 	if (has_delim)
 	{
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index e1837bee71..8de38abd11 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -64,7 +64,7 @@ int2in(PG_FUNCTION_ARGS)
 {
 	char	   *num = PG_GETARG_CSTRING(0);
 
-	PG_RETURN_INT16(pg_strtoint16(num));
+	PG_RETURN_INT16(pg_strtoint16_safe(num, fcinfo->context));
 }
 
 /*
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 98d4323755..7d1767ce0f 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -52,7 +52,7 @@ int8in(PG_FUNCTION_ARGS)
 {
 	char	   *num = PG_GETARG_CSTRING(0);
 
-	PG_RETURN_INT64(pg_strtoint64(num));
+	PG_RETURN_INT64(pg_strtoint64_safe(num, fcinfo->context));
 }
 
 
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 8d83b2edb3..930bd26584 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -64,6 +64,7 @@
 #include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
+#include "nodes/miscnodes.h"
 #include "regex/regex.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
@@ -1041,15 +1042,15 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 					char	   *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
 																		  NumericGetDatum(jb->val.numeric)));
 					double		val;
-					bool		have_error = false;
+					ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-					val = float8in_internal_opt_error(tmp,
-													  NULL,
-													  "double precision",
-													  tmp,
-													  &have_error);
+					val = float8in_internal(tmp,
+											NULL,
+											"double precision",
+											tmp,
+											(Node *) &escontext);
 
-					if (have_error || isinf(val) || isnan(val))
+					if (escontext.error_occurred || isinf(val) || isnan(val))
 						RETURN_ERROR(ereport(ERROR,
 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 											  errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
@@ -1062,15 +1063,15 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 					double		val;
 					char	   *tmp = pnstrdup(jb->val.string.val,
 											   jb->val.string.len);
-					bool		have_error = false;
+					ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-					val = float8in_internal_opt_error(tmp,
-													  NULL,
-													  "double precision",
-													  tmp,
-													  &have_error);
+					val = float8in_internal(tmp,
+											NULL,
+											"double precision",
+											tmp,
+											(Node *) &escontext);
 
-					if (have_error || isinf(val) || isnan(val))
+					if (escontext.error_occurred || isinf(val) || isnan(val))
 						RETURN_ERROR(ereport(ERROR,
 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 											  errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 7f0e93aa80..c024928bc8 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -497,8 +497,9 @@ static void alloc_var(NumericVar *var, int ndigits);
 static void free_var(NumericVar *var);
 static void zero_var(NumericVar *var);
 
-static const char *set_var_from_str(const char *str, const char *cp,
-									NumericVar *dest);
+static bool set_var_from_str(const char *str, const char *cp,
+							 NumericVar *dest, const char **endptr,
+							 Node *escontext);
 static void set_var_from_num(Numeric num, NumericVar *dest);
 static void init_var_from_num(Numeric num, NumericVar *dest);
 static void set_var_from_var(const NumericVar *value, NumericVar *dest);
@@ -512,8 +513,8 @@ static Numeric duplicate_numeric(Numeric num);
 static Numeric make_result(const NumericVar *var);
 static Numeric make_result_opt_error(const NumericVar *var, bool *have_error);
 
-static void apply_typmod(NumericVar *var, int32 typmod);
-static void apply_typmod_special(Numeric num, int32 typmod);
+static bool apply_typmod(NumericVar *var, int32 typmod, Node *escontext);
+static bool apply_typmod_special(Numeric num, int32 typmod, Node *escontext);
 
 static bool numericvar_to_int32(const NumericVar *var, int32 *result);
 static bool numericvar_to_int64(const NumericVar *var, int64 *result);
@@ -617,11 +618,11 @@ Datum
 numeric_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
-
 #ifdef NOT_USED
 	Oid			typelem = PG_GETARG_OID(1);
 #endif
 	int32		typmod = PG_GETARG_INT32(2);
+	Node	   *escontext = fcinfo->context;
 	Numeric		res;
 	const char *cp;
 
@@ -679,10 +680,12 @@ numeric_in(PG_FUNCTION_ARGS)
 		 * Use set_var_from_str() to parse a normal numeric value
 		 */
 		NumericVar	value;
+		bool		have_error;
 
 		init_var(&value);
 
-		cp = set_var_from_str(str, cp, &value);
+		if (!set_var_from_str(str, cp, &value, &cp, escontext))
+			PG_RETURN_NULL();
 
 		/*
 		 * We duplicate a few lines of code here because we would like to
@@ -693,16 +696,23 @@ numeric_in(PG_FUNCTION_ARGS)
 		while (*cp)
 		{
 			if (!isspace((unsigned char) *cp))
-				ereport(ERROR,
+				ereturn(escontext, (Datum) 0,
 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 						 errmsg("invalid input syntax for type %s: \"%s\"",
 								"numeric", str)));
 			cp++;
 		}
 
-		apply_typmod(&value, typmod);
+		if (!apply_typmod(&value, typmod, escontext))
+			PG_RETURN_NULL();
+
+		res = make_result_opt_error(&value, &have_error);
+
+		if (have_error)
+			ereturn(escontext, (Datum) 0,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("value overflows numeric format")));
 
-		res = make_result(&value);
 		free_var(&value);
 
 		PG_RETURN_NUMERIC(res);
@@ -712,7 +722,7 @@ numeric_in(PG_FUNCTION_ARGS)
 	while (*cp)
 	{
 		if (!isspace((unsigned char) *cp))
-			ereport(ERROR,
+			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("invalid input syntax for type %s: \"%s\"",
 							"numeric", str)));
@@ -720,7 +730,8 @@ numeric_in(PG_FUNCTION_ARGS)
 	}
 
 	/* As above, throw any typmod error after finishing syntax check */
-	apply_typmod_special(res, typmod);
+	if (!apply_typmod_special(res, typmod, escontext))
+		PG_RETURN_NULL();
 
 	PG_RETURN_NUMERIC(res);
 }
@@ -1058,7 +1069,7 @@ numeric_recv(PG_FUNCTION_ARGS)
 	{
 		trunc_var(&value, value.dscale);
 
-		apply_typmod(&value, typmod);
+		(void) apply_typmod(&value, typmod, NULL);
 
 		res = make_result(&value);
 	}
@@ -1067,7 +1078,7 @@ numeric_recv(PG_FUNCTION_ARGS)
 		/* apply_typmod_special wants us to make the Numeric first */
 		res = make_result(&value);
 
-		apply_typmod_special(res, typmod);
+		(void) apply_typmod_special(res, typmod, NULL);
 	}
 
 	free_var(&value);
@@ -1180,7 +1191,7 @@ numeric		(PG_FUNCTION_ARGS)
 	 */
 	if (NUMERIC_IS_SPECIAL(num))
 	{
-		apply_typmod_special(num, typmod);
+		(void) apply_typmod_special(num, typmod, NULL);
 		PG_RETURN_NUMERIC(duplicate_numeric(num));
 	}
 
@@ -1231,7 +1242,7 @@ numeric		(PG_FUNCTION_ARGS)
 	init_var(&var);
 
 	set_var_from_num(num, &var);
-	apply_typmod(&var, typmod);
+	(void) apply_typmod(&var, typmod, NULL);
 	new = make_result(&var);
 
 	free_var(&var);
@@ -4395,6 +4406,7 @@ float8_numeric(PG_FUNCTION_ARGS)
 	Numeric		res;
 	NumericVar	result;
 	char		buf[DBL_DIG + 100];
+	const char *endptr;
 
 	if (isnan(val))
 		PG_RETURN_NUMERIC(make_result(&const_nan));
@@ -4412,7 +4424,7 @@ float8_numeric(PG_FUNCTION_ARGS)
 	init_var(&result);
 
 	/* Assume we need not worry about leading/trailing spaces */
-	(void) set_var_from_str(buf, buf, &result);
+	(void) set_var_from_str(buf, buf, &result, &endptr, NULL);
 
 	res = make_result(&result);
 
@@ -4488,6 +4500,7 @@ float4_numeric(PG_FUNCTION_ARGS)
 	Numeric		res;
 	NumericVar	result;
 	char		buf[FLT_DIG + 100];
+	const char *endptr;
 
 	if (isnan(val))
 		PG_RETURN_NUMERIC(make_result(&const_nan));
@@ -4505,7 +4518,7 @@ float4_numeric(PG_FUNCTION_ARGS)
 	init_var(&result);
 
 	/* Assume we need not worry about leading/trailing spaces */
-	(void) set_var_from_str(buf, buf, &result);
+	(void) set_var_from_str(buf, buf, &result, &endptr, NULL);
 
 	res = make_result(&result);
 
@@ -6804,14 +6817,19 @@ zero_var(NumericVar *var)
  *	Parse a string and put the number into a variable
  *
  * This function does not handle leading or trailing spaces.  It returns
- * the end+1 position parsed, so that caller can check for trailing
- * spaces/garbage if deemed necessary.
+ * the end+1 position parsed into *endptr, so that caller can check for
+ * trailing spaces/garbage if deemed necessary.
  *
  * cp is the place to actually start parsing; str is what to use in error
  * reports.  (Typically cp would be the same except advanced over spaces.)
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
  */
-static const char *
-set_var_from_str(const char *str, const char *cp, NumericVar *dest)
+static bool
+set_var_from_str(const char *str, const char *cp,
+				 NumericVar *dest, const char **endptr,
+				 Node *escontext)
 {
 	bool		have_dp = false;
 	int			i;
@@ -6849,7 +6867,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 	}
 
 	if (!isdigit((unsigned char) *cp))
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
 						"numeric", str)));
@@ -6873,7 +6891,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 		else if (*cp == '.')
 		{
 			if (have_dp)
-				ereport(ERROR,
+				ereturn(escontext, false,
 						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 						 errmsg("invalid input syntax for type %s: \"%s\"",
 								"numeric", str)));
@@ -6897,7 +6915,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 		cp++;
 		exponent = strtol(cp, &endptr, 10);
 		if (endptr == cp)
-			ereport(ERROR,
+			ereturn(escontext, false,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("invalid input syntax for type %s: \"%s\"",
 							"numeric", str)));
@@ -6912,7 +6930,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 		 * for consistency use the same ereport errcode/text as make_result().
 		 */
 		if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
-			ereport(ERROR,
+			ereturn(escontext, false,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("value overflows numeric format")));
 		dweight += (int) exponent;
@@ -6963,7 +6981,9 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 	strip_var(dest);
 
 	/* Return end+1 position for caller */
-	return cp;
+	*endptr = cp;
+
+	return true;
 }
 
 
@@ -7455,9 +7475,12 @@ make_result(const NumericVar *var)
  *
  *	Do bounds checking and rounding according to the specified typmod.
  *	Note that this is only applied to normal finite values.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
  */
-static void
-apply_typmod(NumericVar *var, int32 typmod)
+static bool
+apply_typmod(NumericVar *var, int32 typmod, Node *escontext)
 {
 	int			precision;
 	int			scale;
@@ -7467,7 +7490,7 @@ apply_typmod(NumericVar *var, int32 typmod)
 
 	/* Do nothing if we have an invalid typmod */
 	if (!is_valid_numeric_typmod(typmod))
-		return;
+		return true;
 
 	precision = numeric_typmod_precision(typmod);
 	scale = numeric_typmod_scale(typmod);
@@ -7514,7 +7537,7 @@ apply_typmod(NumericVar *var, int32 typmod)
 #error unsupported NBASE
 #endif
 				if (ddigits > maxdigits)
-					ereport(ERROR,
+					ereturn(escontext, false,
 							(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 							 errmsg("numeric field overflow"),
 							 errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.",
@@ -7528,6 +7551,8 @@ apply_typmod(NumericVar *var, int32 typmod)
 			ddigits -= DEC_DIGITS;
 		}
 	}
+
+	return true;
 }
 
 /*
@@ -7535,9 +7560,12 @@ apply_typmod(NumericVar *var, int32 typmod)
  *
  *	Do bounds checking according to the specified typmod, for an Inf or NaN.
  *	For convenience of most callers, the value is presented in packed form.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
  */
-static void
-apply_typmod_special(Numeric num, int32 typmod)
+static bool
+apply_typmod_special(Numeric num, int32 typmod, Node *escontext)
 {
 	int			precision;
 	int			scale;
@@ -7551,16 +7579,16 @@ apply_typmod_special(Numeric num, int32 typmod)
 	 * any finite number of digits.
 	 */
 	if (NUMERIC_IS_NAN(num))
-		return;
+		return true;
 
 	/* Do nothing if we have a default typmod (-1) */
 	if (!is_valid_numeric_typmod(typmod))
-		return;
+		return true;
 
 	precision = numeric_typmod_precision(typmod);
 	scale = numeric_typmod_scale(typmod);
 
-	ereport(ERROR,
+	ereturn(escontext, false,
 			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 			 errmsg("numeric field overflow"),
 			 errdetail("A field with precision %d, scale %d cannot hold an infinite value.",
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index 0de0bed0e8..ab1564f22d 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -88,15 +88,24 @@ decimalLength64(const uint64 v)
 /*
  * Convert input string to a signed 16 bit integer.
  *
- * Allows any number of leading or trailing whitespace characters. Will throw
- * ereport() upon bad input format or overflow.
+ * Allows any number of leading or trailing whitespace characters.
  *
+ * pg_strtoint16() will throw ereport() upon bad input format or overflow;
+ * while pg_strtoint16_safe() instead returns such complaints in *escontext,
+ * if it's an ErrorSaveContext.
+*
  * NB: Accumulate input as an unsigned number, to deal with two's complement
  * representation of the most negative number, which can't be represented as a
  * signed positive number.
  */
 int16
 pg_strtoint16(const char *s)
+{
+	return pg_strtoint16_safe(s, NULL);
+}
+
+int16
+pg_strtoint16_safe(const char *s, Node *escontext)
 {
 	const char *ptr = s;
 	uint16		tmp = 0;
@@ -149,18 +158,16 @@ pg_strtoint16(const char *s)
 	return (int16) tmp;
 
 out_of_range:
-	ereport(ERROR,
+	ereturn(escontext, 0,
 			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 			 errmsg("value \"%s\" is out of range for type %s",
 					s, "smallint")));
 
 invalid_syntax:
-	ereport(ERROR,
+	ereturn(escontext, 0,
 			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 			 errmsg("invalid input syntax for type %s: \"%s\"",
 					"smallint", s)));
-
-	return 0;					/* keep compiler quiet */
 }
 
 /*
@@ -251,8 +258,11 @@ invalid_syntax:
 /*
  * Convert input string to a signed 64 bit integer.
  *
- * Allows any number of leading or trailing whitespace characters. Will throw
- * ereport() upon bad input format or overflow.
+ * Allows any number of leading or trailing whitespace characters.
+ *
+ * pg_strtoint64() will throw ereport() upon bad input format or overflow;
+ * while pg_strtoint64_safe() instead returns such complaints in *escontext,
+ * if it's an ErrorSaveContext.
  *
  * NB: Accumulate input as an unsigned number, to deal with two's complement
  * representation of the most negative number, which can't be represented as a
@@ -260,6 +270,12 @@ invalid_syntax:
  */
 int64
 pg_strtoint64(const char *s)
+{
+	return pg_strtoint64_safe(s, NULL);
+}
+
+int64
+pg_strtoint64_safe(const char *s, Node *escontext)
 {
 	const char *ptr = s;
 	uint64		tmp = 0;
@@ -312,18 +328,16 @@ pg_strtoint64(const char *s)
 	return (int64) tmp;
 
 out_of_range:
-	ereport(ERROR,
+	ereturn(escontext, 0,
 			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 			 errmsg("value \"%s\" is out of range for type %s",
 					s, "bigint")));
 
 invalid_syntax:
-	ereport(ERROR,
+	ereturn(escontext, 0,
 			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 			 errmsg("invalid input syntax for type %s: \"%s\"",
 					"bigint", s)));
-
-	return 0;					/* keep compiler quiet */
 }
 
 /*
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index fbfd8375e3..10d13b0f1e 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -44,9 +44,11 @@ extern int	namestrcmp(Name name, const char *str);
 
 /* numutils.c */
 extern int16 pg_strtoint16(const char *s);
+extern int16 pg_strtoint16_safe(const char *s, Node *escontext);
 extern int32 pg_strtoint32(const char *s);
 extern int32 pg_strtoint32_safe(const char *s, Node *escontext);
 extern int64 pg_strtoint64(const char *s);
+extern int64 pg_strtoint64_safe(const char *s, Node *escontext);
 extern int	pg_itoa(int16 i, char *a);
 extern int	pg_ultoa_n(uint32 value, char *a);
 extern int	pg_ulltoa_n(uint64 value, char *a);
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index 4bf0e3ac07..f92860b4a4 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -42,10 +42,8 @@ extern void float_underflow_error(void) pg_attribute_noreturn();
 extern void float_zero_divide_error(void) pg_attribute_noreturn();
 extern int	is_infinite(float8 val);
 extern float8 float8in_internal(char *num, char **endptr_p,
-								const char *type_name, const char *orig_string);
-extern float8 float8in_internal_opt_error(char *num, char **endptr_p,
-										  const char *type_name, const char *orig_string,
-										  bool *have_error);
+								const char *type_name, const char *orig_string,
+								struct Node *escontext);
 extern char *float8out_internal(float8 num);
 extern int	float4_cmp_internal(float4 a, float4 b);
 extern int	float8_cmp_internal(float8 a, float8 b);
diff --git a/src/test/regress/expected/float4-misrounded-input.out b/src/test/regress/expected/float4-misrounded-input.out
index 3d5d298b73..24fde6cc9f 100644
--- a/src/test/regress/expected/float4-misrounded-input.out
+++ b/src/test/regress/expected/float4-misrounded-input.out
@@ -81,6 +81,31 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
 ERROR:  invalid input syntax for type real: "123            5"
 LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
                                            ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float4');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('xyz', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e400', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e400', 'float4');
+        pg_input_error_message         
+---------------------------------------
+ "1e400" is out of range for type real
+(1 row)
+
 -- special inputs
 SELECT 'NaN'::float4;
  float4 
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index 6ad5d00aa2..1d7090a90d 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -81,6 +81,31 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
 ERROR:  invalid input syntax for type real: "123            5"
 LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
                                            ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float4');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('xyz', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e400', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e400', 'float4');
+        pg_input_error_message         
+---------------------------------------
+ "1e400" is out of range for type real
+(1 row)
+
 -- special inputs
 SELECT 'NaN'::float4;
  float4 
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index de4d57ec9f..2b25784f7f 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -68,6 +68,31 @@ INSERT INTO FLOAT8_TBL(f1) VALUES ('123           5');
 ERROR:  invalid input syntax for type double precision: "123           5"
 LINE 1: INSERT INTO FLOAT8_TBL(f1) VALUES ('123           5');
                                            ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float8');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('xyz', 'float8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e4000', 'float8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e4000', 'float8');
+               pg_input_error_message               
+----------------------------------------------------
+ "1e4000" is out of range for type double precision
+(1 row)
+
 -- special inputs
 SELECT 'NaN'::float8;
  float8 
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 109cf9baaa..6a23567b67 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -45,6 +45,31 @@ SELECT * FROM INT2_TBL;
  -32767
 (5 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int2');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('asdf', 'int2');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('50000', 'int2');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('50000', 'int2');
+             pg_input_error_message              
+-------------------------------------------------
+ value "50000" is out of range for type smallint
+(1 row)
+
 SELECT * FROM INT2_TBL AS f(a, b);
 ERROR:  table "f" has 1 columns available but 2 columns specified
 SELECT * FROM (TABLE int2_tbl) AS s (a, b);
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 1ae23cf3f9..90ed061249 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -42,6 +42,31 @@ SELECT * FROM INT8_TBL;
  4567890123456789 | -4567890123456789
 (5 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int8');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('asdf', 'int8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('10000000000000000000', 'int8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('10000000000000000000', 'int8');
+                    pg_input_error_message                    
+--------------------------------------------------------------
+ value "10000000000000000000" is out of range for type bigint
+(1 row)
+
 -- int8/int8 cmp
 SELECT * FROM INT8_TBL WHERE q2 = 4567890123456789;
         q1        |        q2        
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 3c610646dc..30a5613ed7 100644
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -2199,6 +2199,49 @@ SELECT * FROM num_input_test;
  -Infinity
 (13 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'numeric');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('34xyz', 'numeric');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e400000', 'numeric');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e400000', 'numeric');
+     pg_input_error_message     
+--------------------------------
+ value overflows numeric format
+(1 row)
+
+SELECT pg_input_is_valid('1234.567', 'numeric(8,4)');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('1234.567', 'numeric(7,4)');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1234.567', 'numeric(7,4)');
+ pg_input_error_message 
+------------------------
+ numeric field overflow
+(1 row)
+
 --
 -- Test precision and scale typemods
 --
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 612486ecbd..061477726b 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -36,6 +36,12 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('5.   0');
 INSERT INTO FLOAT4_TBL(f1) VALUES ('     - 3.0');
 INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float4');
+SELECT pg_input_is_valid('xyz', 'float4');
+SELECT pg_input_is_valid('1e400', 'float4');
+SELECT pg_input_error_message('1e400', 'float4');
+
 -- special inputs
 SELECT 'NaN'::float4;
 SELECT 'nan'::float4;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 03c134b078..c276a5324c 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -34,6 +34,12 @@ INSERT INTO FLOAT8_TBL(f1) VALUES ('5.   0');
 INSERT INTO FLOAT8_TBL(f1) VALUES ('    - 3');
 INSERT INTO FLOAT8_TBL(f1) VALUES ('123           5');
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float8');
+SELECT pg_input_is_valid('xyz', 'float8');
+SELECT pg_input_is_valid('1e4000', 'float8');
+SELECT pg_input_error_message('1e4000', 'float8');
+
 -- special inputs
 SELECT 'NaN'::float8;
 SELECT 'nan'::float8;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index ea29066b78..98a761a24a 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -17,6 +17,12 @@ INSERT INTO INT2_TBL(f1) VALUES ('');
 
 SELECT * FROM INT2_TBL;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int2');
+SELECT pg_input_is_valid('asdf', 'int2');
+SELECT pg_input_is_valid('50000', 'int2');
+SELECT pg_input_error_message('50000', 'int2');
+
 SELECT * FROM INT2_TBL AS f(a, b);
 
 SELECT * FROM (TABLE int2_tbl) AS s (a, b);
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index 38b771964d..76007b692b 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -16,6 +16,12 @@ INSERT INTO INT8_TBL(q1) VALUES ('');
 
 SELECT * FROM INT8_TBL;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int8');
+SELECT pg_input_is_valid('asdf', 'int8');
+SELECT pg_input_is_valid('10000000000000000000', 'int8');
+SELECT pg_input_error_message('10000000000000000000', 'int8');
+
 -- int8/int8 cmp
 SELECT * FROM INT8_TBL WHERE q2 = 4567890123456789;
 SELECT * FROM INT8_TBL WHERE q2 <> 4567890123456789;
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index 93bb0996be..7bb34e5021 100644
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -1053,6 +1053,15 @@ INSERT INTO num_input_test(n1) VALUES ('+ infinity');
 
 SELECT * FROM num_input_test;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'numeric');
+SELECT pg_input_is_valid('34xyz', 'numeric');
+SELECT pg_input_is_valid('1e400000', 'numeric');
+SELECT pg_input_error_message('1e400000', 'numeric');
+SELECT pg_input_is_valid('1234.567', 'numeric(8,4)');
+SELECT pg_input_is_valid('1234.567', 'numeric(7,4)');
+SELECT pg_input_error_message('1234.567', 'numeric(7,4)');
+
 --
 -- Test precision and scale typemods
 --
