From a8f59a6a0d21bbcd71db5123c68c355a304d7d6e Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 4 Dec 2018 02:05:10 +0300
Subject: [PATCH 03/13] Remove PG_TRY in jsonpath arithmetics

---
 src/backend/utils/adt/float.c         |  48 +++---
 src/backend/utils/adt/jsonpath_exec.c | 108 +++++-------
 src/backend/utils/adt/numeric.c       | 302 ++++++++++++++++++++++------------
 src/include/utils/elog.h              |  19 +++
 src/include/utils/float.h             |   7 +-
 src/include/utils/numeric.h           |   9 +
 6 files changed, 305 insertions(+), 188 deletions(-)

diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index cf9327f..ffe0813 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -287,7 +287,7 @@ float8in(PG_FUNCTION_ARGS)
 }
 
 /*
- * float8in_internal - guts of float8in()
+ * float8in_internal_safe - guts of float8in()
  *
  * This is exposed for use by functions that want a reasonably
  * platform-independent way of inputting doubles.  The behavior is
@@ -305,8 +305,8 @@ float8in(PG_FUNCTION_ARGS)
  * unreasonable amount of extra casting both here and in callers, so we don't.
  */
 double
-float8in_internal(char *num, char **endptr_p,
-				  const char *type_name, const char *orig_string)
+float8in_internal_safe(char *num, char **endptr_p, const char *type_name,
+					   const char *orig_string, ErrorData **edata)
 {
 	double		val;
 	char	   *endptr;
@@ -320,10 +320,13 @@ float8in_internal(char *num, char **endptr_p,
 	 * strtod() on different platforms.
 	 */
 	if (*num == '\0')
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type %s: \"%s\"",
-						type_name, orig_string)));
+	{
+		ereport_safe(edata, ERROR,
+					 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					  errmsg("invalid input syntax for type %s: \"%s\"",
+							 type_name, orig_string)));
+		return 0;
+	}
 
 	errno = 0;
 	val = strtod(num, &endptr);
@@ -396,17 +399,21 @@ float8in_internal(char *num, char **endptr_p,
 				char	   *errnumber = pstrdup(num);
 
 				errnumber[endptr - num] = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-						 errmsg("\"%s\" is out of range for type double precision",
-								errnumber)));
+				ereport_safe(edata, ERROR,
+							 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+							  errmsg("\"%s\" is out of range for type double precision",
+									 errnumber)));
+				return 0;
 			}
 		}
 		else
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-					 errmsg("invalid input syntax for type %s: \"%s\"",
-							type_name, orig_string)));
+		{
+			ereport_safe(edata, ERROR,
+						 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						  errmsg("invalid input syntax for type %s: \"%s\"",
+								 type_name, orig_string)));
+			return 0;
+		}
 	}
 #ifdef HAVE_BUGGY_SOLARIS_STRTOD
 	else
@@ -429,10 +436,13 @@ float8in_internal(char *num, char **endptr_p,
 	if (endptr_p)
 		*endptr_p = endptr;
 	else if (*endptr != '\0')
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type %s: \"%s\"",
-						type_name, orig_string)));
+	{
+		ereport_safe(edata, ERROR,
+					 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					  errmsg("invalid input syntax for type %s: \"%s\"",
+							 type_name, orig_string)));
+		return 0;
+	}
 
 	return val;
 }
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index e6646c2..b79e01a 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -19,6 +19,7 @@
 #include "regex/regex.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/float.h"
 #include "utils/formatting.h"
 #include "utils/json.h"
 #include "utils/jsonpath.h"
@@ -918,7 +919,6 @@ static JsonPathExecResult
 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
 						JsonbValue *jb, JsonValueList *found)
 {
-	MemoryContext mcxt = CurrentMemoryContext;
 	JsonPathExecResult jper;
 	JsonPathItem elem;
 	JsonValueList lseq = { 0 };
@@ -927,11 +927,10 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
 	JsonbValue *rval;
 	JsonbValue	lvalbuf;
 	JsonbValue	rvalbuf;
-	PGFunction	func;
-	Datum		ldatum;
-	Datum		rdatum;
-	Datum		res;
+	Numeric	  (*func)(Numeric, Numeric, ErrorData **);
+	Numeric		res;
 	bool		hasNext;
+	ErrorData  *edata;
 
 	jspGetLeftArg(jsp, &elem);
 
@@ -970,25 +969,22 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
 	if (!found && !hasNext)
 		return jperOk;
 
-	ldatum = NumericGetDatum(lval->val.numeric);
-	rdatum = NumericGetDatum(rval->val.numeric);
-
 	switch (jsp->type)
 	{
 		case jpiAdd:
-			func = numeric_add;
+			func = numeric_add_internal;
 			break;
 		case jpiSub:
-			func = numeric_sub;
+			func = numeric_sub_internal;
 			break;
 		case jpiMul:
-			func = numeric_mul;
+			func = numeric_mul_internal;
 			break;
 		case jpiDiv:
-			func = numeric_div;
+			func = numeric_div_internal;
 			break;
 		case jpiMod:
-			func = numeric_mod;
+			func = numeric_mod_internal;
 			break;
 		default:
 			elog(ERROR, "unknown jsonpath arithmetic operation %d", jsp->type);
@@ -996,29 +992,15 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
 			break;
 	}
 
-	PG_TRY();
-	{
-		res = DirectFunctionCall2(func, ldatum, rdatum);
-	}
-	PG_CATCH();
-	{
-		int			errcode = geterrcode();
-		ErrorData  *edata;
-
-		if (ERRCODE_TO_CATEGORY(errcode) != ERRCODE_DATA_EXCEPTION)
-			PG_RE_THROW();
-
-		MemoryContextSwitchTo(mcxt);
-		edata = CopyErrorData();
-		FlushErrorState();
+	edata = NULL;
+	res = func(lval->val.numeric, rval->val.numeric, &edata);
 
+	if (edata)
 		return jperMakeErrorData(edata);
-	}
-	PG_END_TRY();
 
 	lval = palloc(sizeof(*lval));
 	lval->type = jbvNumeric;
-	lval->val.numeric = DatumGetNumeric(res);
+	lval->val.numeric = res;
 
 	return recursiveExecuteNext(cxt, jsp, &elem, lval, found, false);
 }
@@ -2032,53 +2014,53 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
 		case jpiDouble:
 			{
 				JsonbValue jbv;
-				MemoryContext mcxt = CurrentMemoryContext;
+				ErrorData  *edata = NULL;
 
 				if (JsonbType(jb) == jbvScalar)
 					jb = JsonbExtractScalar(jb->val.binary.data, &jbv);
 
-				PG_TRY();
+				if (jb->type == jbvNumeric)
 				{
-					if (jb->type == jbvNumeric)
-					{
-						/* only check success of numeric to double cast */
-						DirectFunctionCall1(numeric_float8,
-											NumericGetDatum(jb->val.numeric));
-						res = jperOk;
-					}
-					else if (jb->type == jbvString)
-					{
-						/* cast string as double */
-						char	   *str = pnstrdup(jb->val.string.val,
-												   jb->val.string.len);
-						Datum		val = DirectFunctionCall1(
-											float8in, CStringGetDatum(str));
-						pfree(str);
+					/* only check success of numeric to double cast */
+					(void) numeric_float8_internal(jb->val.numeric, &edata);
+				}
+				else if (jb->type == jbvString)
+				{
+					/* cast string as double */
+					char	   *str = pnstrdup(jb->val.string.val,
+											   jb->val.string.len);
+					double		val;
 
+					val = float8in_internal_safe(str, NULL, "double precision",
+												 str, &edata);
+					pfree(str);
+
+					if (!edata)
+					{
 						jb = &jbv;
 						jb->type = jbvNumeric;
-						jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(
-														float8_numeric, val));
-						res = jperOk;
-
+						jb->val.numeric = float8_numeric_internal(val, &edata);
 					}
-					else
-						res = jperMakeError(ERRCODE_NON_NUMERIC_JSON_ITEM);
 				}
-				PG_CATCH();
+				else
+				{
+					res = jperMakeError(ERRCODE_NON_NUMERIC_JSON_ITEM);
+					break;
+				}
+
+				if (edata)
 				{
-					if (ERRCODE_TO_CATEGORY(geterrcode()) !=
-														ERRCODE_DATA_EXCEPTION)
-						PG_RE_THROW();
+					if (ERRCODE_TO_CATEGORY(edata->sqlerrcode) !=
+						ERRCODE_DATA_EXCEPTION)
+						ThrowErrorData(edata);
 
-					FlushErrorState();
-					MemoryContextSwitchTo(mcxt);
+					FreeErrorData(edata);
 					res = jperMakeError(ERRCODE_NON_NUMERIC_JSON_ITEM);
 				}
-				PG_END_TRY();
-
-				if (res == jperOk)
+				else
+				{
 					res = recursiveExecuteNext(cxt, jsp, NULL, jb, found, true);
+				}
 			}
 			break;
 		case jpiDatetime:
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 444e575..3282b8c 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -466,14 +466,15 @@ 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);
+				 NumericVar *dest, ErrorData **edata);
 static void set_var_from_num(Numeric value, NumericVar *dest);
 static void init_var_from_num(Numeric num, NumericVar *dest);
 static void set_var_from_var(const NumericVar *value, NumericVar *dest);
 static char *get_str_from_var(const NumericVar *var);
 static char *get_str_from_var_sci(const NumericVar *var, int rscale);
 
-static Numeric make_result(const NumericVar *var);
+static inline Numeric make_result(const NumericVar *var);
+static Numeric make_result_safe(const NumericVar *var, ErrorData **edata);
 
 static void apply_typmod(NumericVar *var, int32 typmod);
 
@@ -510,12 +511,12 @@ static void mul_var(const NumericVar *var1, const NumericVar *var2,
 		int rscale);
 static void div_var(const NumericVar *var1, const NumericVar *var2,
 		NumericVar *result,
-		int rscale, bool round);
+		int rscale, bool round, ErrorData **edata);
 static void div_var_fast(const NumericVar *var1, const NumericVar *var2,
 			 NumericVar *result, int rscale, bool round);
 static int	select_div_scale(const NumericVar *var1, const NumericVar *var2);
 static void mod_var(const NumericVar *var1, const NumericVar *var2,
-		NumericVar *result);
+		NumericVar *result, ErrorData **edata);
 static void ceil_var(const NumericVar *var, NumericVar *result);
 static void floor_var(const NumericVar *var, NumericVar *result);
 
@@ -616,7 +617,7 @@ numeric_in(PG_FUNCTION_ARGS)
 
 		init_var(&value);
 
-		cp = set_var_from_str(str, cp, &value);
+		cp = set_var_from_str(str, cp, &value, NULL);
 
 		/*
 		 * We duplicate a few lines of code here because we would like to
@@ -1579,14 +1580,14 @@ compute_bucket(Numeric operand, Numeric bound1, Numeric bound2,
 		sub_var(&operand_var, &bound1_var, &operand_var);
 		sub_var(&bound2_var, &bound1_var, &bound2_var);
 		div_var(&operand_var, &bound2_var, result_var,
-				select_div_scale(&operand_var, &bound2_var), true);
+				select_div_scale(&operand_var, &bound2_var), true, NULL);
 	}
 	else
 	{
 		sub_var(&bound1_var, &operand_var, &operand_var);
 		sub_var(&bound1_var, &bound2_var, &bound1_var);
 		div_var(&operand_var, &bound1_var, result_var,
-				select_div_scale(&operand_var, &bound1_var), true);
+				select_div_scale(&operand_var, &bound1_var), true, NULL);
 	}
 
 	mul_var(result_var, count_var, result_var,
@@ -2386,17 +2387,9 @@ hash_numeric_extended(PG_FUNCTION_ARGS)
  * ----------------------------------------------------------------------
  */
 
-
-/*
- * numeric_add() -
- *
- *	Add two numerics
- */
-Datum
-numeric_add(PG_FUNCTION_ARGS)
+Numeric
+numeric_add_internal(Numeric num1, Numeric num2, ErrorData **edata)
 {
-	Numeric		num1 = PG_GETARG_NUMERIC(0);
-	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	NumericVar	arg1;
 	NumericVar	arg2;
 	NumericVar	result;
@@ -2406,7 +2399,7 @@ numeric_add(PG_FUNCTION_ARGS)
 	 * Handle NaN
 	 */
 	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		PG_RETURN_NUMERIC(make_result(&const_nan));
+		return make_result_safe(&const_nan, edata);
 
 	/*
 	 * Unpack the values, let add_var() compute the result and return it.
@@ -2417,24 +2410,31 @@ numeric_add(PG_FUNCTION_ARGS)
 	init_var(&result);
 	add_var(&arg1, &arg2, &result);
 
-	res = make_result(&result);
+	res = make_result_safe(&result, edata);
 
 	free_var(&result);
 
-	PG_RETURN_NUMERIC(res);
+	return res;
 }
 
-
 /*
- * numeric_sub() -
+ * numeric_add() -
  *
- *	Subtract one numeric from another
+ *	Add two numerics
  */
 Datum
-numeric_sub(PG_FUNCTION_ARGS)
+numeric_add(PG_FUNCTION_ARGS)
 {
 	Numeric		num1 = PG_GETARG_NUMERIC(0);
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
+	Numeric		res = numeric_add_internal(num1, num2, NULL);
+
+	PG_RETURN_NUMERIC(res);
+}
+
+Numeric
+numeric_sub_internal(Numeric num1, Numeric num2, ErrorData **edata)
+{
 	NumericVar	arg1;
 	NumericVar	arg2;
 	NumericVar	result;
@@ -2444,7 +2444,7 @@ numeric_sub(PG_FUNCTION_ARGS)
 	 * Handle NaN
 	 */
 	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		PG_RETURN_NUMERIC(make_result(&const_nan));
+		return make_result_safe(&const_nan, edata);
 
 	/*
 	 * Unpack the values, let sub_var() compute the result and return it.
@@ -2455,24 +2455,31 @@ numeric_sub(PG_FUNCTION_ARGS)
 	init_var(&result);
 	sub_var(&arg1, &arg2, &result);
 
-	res = make_result(&result);
+	res = make_result_safe(&result, edata);
 
 	free_var(&result);
 
-	PG_RETURN_NUMERIC(res);
+	return res;
 }
 
-
 /*
- * numeric_mul() -
+ * numeric_sub() -
  *
- *	Calculate the product of two numerics
+ *	Subtract one numeric from another
  */
 Datum
-numeric_mul(PG_FUNCTION_ARGS)
+numeric_sub(PG_FUNCTION_ARGS)
 {
 	Numeric		num1 = PG_GETARG_NUMERIC(0);
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
+	Numeric		res = numeric_sub_internal(num1, num2, NULL);
+
+	PG_RETURN_NUMERIC(res);
+}
+
+Numeric
+numeric_mul_internal(Numeric num1, Numeric num2, ErrorData **edata)
+{
 	NumericVar	arg1;
 	NumericVar	arg2;
 	NumericVar	result;
@@ -2482,7 +2489,7 @@ numeric_mul(PG_FUNCTION_ARGS)
 	 * Handle NaN
 	 */
 	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		PG_RETURN_NUMERIC(make_result(&const_nan));
+		return make_result_safe(&const_nan, edata);
 
 	/*
 	 * Unpack the values, let mul_var() compute the result and return it.
@@ -2497,24 +2504,31 @@ numeric_mul(PG_FUNCTION_ARGS)
 	init_var(&result);
 	mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
 
-	res = make_result(&result);
+	res = make_result_safe(&result, edata);
 
 	free_var(&result);
 
-	PG_RETURN_NUMERIC(res);
+	return res;
 }
 
-
 /*
- * numeric_div() -
+ * numeric_mul() -
  *
- *	Divide one numeric into another
+ *	Calculate the product of two numerics
  */
 Datum
-numeric_div(PG_FUNCTION_ARGS)
+numeric_mul(PG_FUNCTION_ARGS)
 {
 	Numeric		num1 = PG_GETARG_NUMERIC(0);
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
+	Numeric		res = numeric_mul_internal(num1, num2, NULL);
+
+	PG_RETURN_NUMERIC(res);
+}
+
+Numeric
+numeric_div_internal(Numeric num1, Numeric num2, ErrorData **edata)
+{
 	NumericVar	arg1;
 	NumericVar	arg2;
 	NumericVar	result;
@@ -2525,7 +2539,7 @@ numeric_div(PG_FUNCTION_ARGS)
 	 * Handle NaN
 	 */
 	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		PG_RETURN_NUMERIC(make_result(&const_nan));
+		return make_result_safe(&const_nan, edata);
 
 	/*
 	 * Unpack the arguments
@@ -2543,12 +2557,30 @@ numeric_div(PG_FUNCTION_ARGS)
 	/*
 	 * Do the divide and return the result
 	 */
-	div_var(&arg1, &arg2, &result, rscale, true);
+	div_var(&arg1, &arg2, &result, rscale, true, edata);
 
-	res = make_result(&result);
+	if (edata && *edata)
+		res = NULL;	/* error occured */
+	else
+		res = make_result_safe(&result, edata);
 
 	free_var(&result);
 
+	return res;
+}
+
+/*
+ * numeric_div() -
+ *
+ *	Divide one numeric into another
+ */
+Datum
+numeric_div(PG_FUNCTION_ARGS)
+{
+	Numeric		num1 = PG_GETARG_NUMERIC(0);
+	Numeric		num2 = PG_GETARG_NUMERIC(1);
+	Numeric		res = numeric_div_internal(num1, num2, NULL);
+
 	PG_RETURN_NUMERIC(res);
 }
 
@@ -2585,7 +2617,7 @@ numeric_div_trunc(PG_FUNCTION_ARGS)
 	/*
 	 * Do the divide and return the result
 	 */
-	div_var(&arg1, &arg2, &result, 0, false);
+	div_var(&arg1, &arg2, &result, 0, false, NULL);
 
 	res = make_result(&result);
 
@@ -2594,36 +2626,43 @@ numeric_div_trunc(PG_FUNCTION_ARGS)
 	PG_RETURN_NUMERIC(res);
 }
 
-
-/*
- * numeric_mod() -
- *
- *	Calculate the modulo of two numerics
- */
-Datum
-numeric_mod(PG_FUNCTION_ARGS)
+Numeric
+numeric_mod_internal(Numeric num1, Numeric num2, ErrorData **edata)
 {
-	Numeric		num1 = PG_GETARG_NUMERIC(0);
-	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	Numeric		res;
 	NumericVar	arg1;
 	NumericVar	arg2;
 	NumericVar	result;
 
 	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		PG_RETURN_NUMERIC(make_result(&const_nan));
+		return make_result_safe(&const_nan, edata);
 
 	init_var_from_num(num1, &arg1);
 	init_var_from_num(num2, &arg2);
 
 	init_var(&result);
 
-	mod_var(&arg1, &arg2, &result);
+	mod_var(&arg1, &arg2, &result, edata);
 
-	res = make_result(&result);
+	res = make_result_safe(&result, edata);
 
 	free_var(&result);
 
+	return res;
+}
+
+/*
+ * numeric_mod() -
+ *
+ *	Calculate the modulo of two numerics
+ */
+Datum
+numeric_mod(PG_FUNCTION_ARGS)
+{
+	Numeric		num1 = PG_GETARG_NUMERIC(0);
+	Numeric		num2 = PG_GETARG_NUMERIC(1);
+	Numeric		res = numeric_mod_internal(num1, num2, NULL);
+
 	PG_RETURN_NUMERIC(res);
 }
 
@@ -3227,55 +3266,73 @@ numeric_int2(PG_FUNCTION_ARGS)
 }
 
 
-Datum
-float8_numeric(PG_FUNCTION_ARGS)
+Numeric
+float8_numeric_internal(float8 val, ErrorData **edata)
 {
-	float8		val = PG_GETARG_FLOAT8(0);
 	Numeric		res;
 	NumericVar	result;
 	char		buf[DBL_DIG + 100];
 
 	if (isnan(val))
-		PG_RETURN_NUMERIC(make_result(&const_nan));
+		return make_result_safe(&const_nan, edata);
 
 	if (isinf(val))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot convert infinity to numeric")));
+	{
+		ereport_safe(edata, ERROR,
+					 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					  errmsg("cannot convert infinity to numeric")));
+		return NULL;
+	}
 
 	snprintf(buf, sizeof(buf), "%.*g", DBL_DIG, val);
 
 	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, edata);
 
-	res = make_result(&result);
+	res = make_result_safe(&result, edata);
 
 	free_var(&result);
 
-	PG_RETURN_NUMERIC(res);
+	return res;
 }
 
-
 Datum
-numeric_float8(PG_FUNCTION_ARGS)
+float8_numeric(PG_FUNCTION_ARGS)
+{
+	float8		val = PG_GETARG_FLOAT8(0);
+	Numeric		res = float8_numeric_internal(val, NULL);
+
+	PG_RETURN_NUMERIC(res);
+}
+
+float8
+numeric_float8_internal(Numeric num, ErrorData **edata)
 {
-	Numeric		num = PG_GETARG_NUMERIC(0);
 	char	   *tmp;
-	Datum		result;
+	float8		result;
 
 	if (NUMERIC_IS_NAN(num))
-		PG_RETURN_FLOAT8(get_float8_nan());
+		return get_float8_nan();
 
 	tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
 											  NumericGetDatum(num)));
 
-	result = DirectFunctionCall1(float8in, CStringGetDatum(tmp));
+	result = float8in_internal_safe(tmp, NULL, "double precison", tmp, edata);
 
 	pfree(tmp);
 
-	PG_RETURN_DATUM(result);
+	return result;
+}
+
+Datum
+numeric_float8(PG_FUNCTION_ARGS)
+{
+	Numeric		num = PG_GETARG_NUMERIC(0);
+	float8		result = numeric_float8_internal(num, NULL);
+
+	PG_RETURN_FLOAT8(result);
 }
 
 
@@ -3319,7 +3376,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, NULL);
 
 	res = make_result(&result);
 
@@ -4894,7 +4951,7 @@ numeric_stddev_internal(NumericAggState *state,
 		else
 			mul_var(&vN, &vN, &vNminus1, 0);	/* N * N */
 		rscale = select_div_scale(&vsumX2, &vNminus1);
-		div_var(&vsumX2, &vNminus1, &vsumX, rscale, true);	/* variance */
+		div_var(&vsumX2, &vNminus1, &vsumX, rscale, true, NULL);	/* variance */
 		if (!variance)
 			sqrt_var(&vsumX, &vsumX, rscale);	/* stddev */
 
@@ -5620,7 +5677,8 @@ zero_var(NumericVar *var)
  * reports.  (Typically cp would be the same except advanced over spaces.)
  */
 static const char *
-set_var_from_str(const char *str, const char *cp, NumericVar *dest)
+set_var_from_str(const char *str, const char *cp, NumericVar *dest,
+				 ErrorData **edata)
 {
 	bool		have_dp = false;
 	int			i;
@@ -5658,10 +5716,13 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 	}
 
 	if (!isdigit((unsigned char) *cp))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type %s: \"%s\"",
-						"numeric", str)));
+	{
+		ereport_safe(edata, ERROR,
+					 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					  errmsg("invalid input syntax for type %s: \"%s\"",
+							 "numeric", str)));
+		return NULL;
+	}
 
 	decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
 
@@ -5682,10 +5743,13 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 		else if (*cp == '.')
 		{
 			if (have_dp)
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-						 errmsg("invalid input syntax for type %s: \"%s\"",
-								"numeric", str)));
+			{
+				ereport_safe(edata, ERROR,
+							 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+							  errmsg("invalid input syntax for type %s: \"%s\"",
+									 "numeric", str)));
+				return NULL;
+			}
 			have_dp = true;
 			cp++;
 		}
@@ -5706,10 +5770,14 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
 		cp++;
 		exponent = strtol(cp, &endptr, 10);
 		if (endptr == cp)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-					 errmsg("invalid input syntax for type %s: \"%s\"",
-							"numeric", str)));
+		{
+			ereport_safe(edata, ERROR,
+						 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						  errmsg("invalid input syntax for type %s: \"%s\"",
+								 "numeric", str)));
+			return NULL;
+		}
+
 		cp = endptr;
 
 		/*
@@ -5721,9 +5789,13 @@ 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,
-					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-					 errmsg("value overflows numeric format")));
+		{
+			ereport_safe(edata, ERROR,
+						 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+						  errmsg("value overflows numeric format")));
+			return NULL;
+		}
+
 		dweight += (int) exponent;
 		dscale -= (int) exponent;
 		if (dscale < 0)
@@ -6065,7 +6137,7 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
 	init_var(&significand);
 
 	power_var_int(&const_ten, exponent, &denominator, denom_scale);
-	div_var(var, &denominator, &significand, rscale, true);
+	div_var(var, &denominator, &significand, rscale, true, NULL);
 	sig_out = get_str_from_var(&significand);
 
 	free_var(&denominator);
@@ -6087,15 +6159,17 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
 	return str;
 }
 
-
 /*
- * make_result() -
+ * make_result_safe() -
  *
  *	Create the packed db numeric format in palloc()'d memory from
  *	a variable.
+ *
+ *	If edata is non-NULL then when numeric-related error occurs error info
+ *	should be placed into *edata (not thrown) and NULL is returned.
  */
 static Numeric
-make_result(const NumericVar *var)
+make_result_safe(const NumericVar *var, ErrorData **edata)
 {
 	Numeric		result;
 	NumericDigit *digits = var->digits;
@@ -6166,14 +6240,27 @@ make_result(const NumericVar *var)
 	/* Check for overflow of int16 fields */
 	if (NUMERIC_WEIGHT(result) != weight ||
 		NUMERIC_DSCALE(result) != var->dscale)
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("value overflows numeric format")));
+	{
+		ereport_safe(edata, ERROR,
+					 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					  errmsg("value overflows numeric format")));
+		return NULL;
+	}
 
 	dump_numeric("make_result()", result);
 	return result;
 }
 
+/*
+ * make_result() -
+ *
+ *	Same as make_result_safe(), but numeric-related errors are always thrown.
+ */
+static inline Numeric
+make_result(const NumericVar *var)
+{
+	return make_result_safe(var, NULL);
+}
 
 /*
  * apply_typmod() -
@@ -7051,7 +7138,7 @@ mul_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
  */
 static void
 div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
-		int rscale, bool round)
+		int rscale, bool round, ErrorData **edata)
 {
 	int			div_ndigits;
 	int			res_ndigits;
@@ -7076,9 +7163,12 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
 	 * unnormalized divisor.
 	 */
 	if (var2ndigits == 0 || var2->digits[0] == 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_DIVISION_BY_ZERO),
-				 errmsg("division by zero")));
+	{
+		ereport_safe(edata, ERROR,
+					 (errcode(ERRCODE_DIVISION_BY_ZERO),
+					  errmsg("division by zero")));
+		return;
+	}
 
 	/*
 	 * Now result zero check
@@ -7699,7 +7789,8 @@ select_div_scale(const NumericVar *var1, const NumericVar *var2)
  *	Calculate the modulo of two numerics at variable level
  */
 static void
-mod_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result)
+mod_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
+		ErrorData **edata)
 {
 	NumericVar	tmp;
 
@@ -7711,7 +7802,10 @@ mod_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result)
 	 * div_var can be persuaded to give us trunc(x/y) directly.
 	 * ----------
 	 */
-	div_var(var1, var2, &tmp, 0, false);
+	div_var(var1, var2, &tmp, 0, false, edata);
+
+	if (edata && *edata)
+		return;	/* error occured */
 
 	mul_var(var2, &tmp, &tmp, var2->dscale);
 
@@ -8364,7 +8458,7 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
 			round_var(result, rscale);
 			return;
 		case -1:
-			div_var(&const_one, base, result, rscale, true);
+			div_var(&const_one, base, result, rscale, true, NULL);
 			return;
 		case 2:
 			mul_var(base, base, result, rscale);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 33c6b53..42a834c 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -143,6 +143,25 @@
 
 #define TEXTDOMAIN NULL
 
+/*
+ * ereport_safe() -- special macro for copying error info into the specified
+ * ErrorData **edata (if it is non-NULL) instead of throwing it.  This is
+ * intended for handling of errors of categories like ERRCODE_DATA_EXCEPTION
+ * without PG_TRY/PG_CATCH, but not for errors like ERRCODE_OUT_OF_MEMORY.
+ */
+#define ereport_safe(edata, elevel, rest) \
+	do { \
+		if (edata) { \
+			if (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN)) { \
+				(void)(rest); \
+				*(edata) = CopyErrorData(); \
+				FlushErrorState(); \
+			} \
+		} else { \
+			ereport(elevel, rest); \
+		} \
+	} while (0)
+
 extern bool errstart(int elevel, const char *filename, int lineno,
 		 const char *funcname, const char *domain);
 extern void errfinish(int dummy,...);
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index 05e1b27..d082bdc 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -38,8 +38,11 @@ extern PGDLLIMPORT int extra_float_digits;
  * Utility functions in float.c
  */
 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_safe(char *num, char **endptr_p,
+				  const char *type_name, const char *orig_string,
+				  ErrorData **edata);
+#define float8in_internal(num, endptr_p, type_name, orig_string) \
+		float8in_internal_safe(num, endptr_p, type_name, orig_string, NULL)
 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/include/utils/numeric.h b/src/include/utils/numeric.h
index cd8da8b..6e3e3f0 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -61,4 +61,13 @@ int32		numeric_maximum_size(int32 typmod);
 extern char *numeric_out_sci(Numeric num, int scale);
 extern char *numeric_normalize(Numeric num);
 
+/* Functions for safe handling of numeric errors without PG_TRY/PG_CATCH */
+extern Numeric numeric_add_internal(Numeric n1, Numeric n2, ErrorData **edata);
+extern Numeric numeric_sub_internal(Numeric n1, Numeric n2, ErrorData **edata);
+extern Numeric numeric_mul_internal(Numeric n1, Numeric n2, ErrorData **edata);
+extern Numeric numeric_div_internal(Numeric n1, Numeric n2, ErrorData **edata);
+extern Numeric numeric_mod_internal(Numeric n1, Numeric n2, ErrorData **edata);
+extern Numeric float8_numeric_internal(float8 val, ErrorData **edata);
+extern float8 numeric_float8_internal(Numeric num, ErrorData **edata);
+
 #endif							/* _PG_NUMERIC_H_ */
-- 
2.7.4

