From 51ba68f09bfe6a729140c8f7e77c51f2b0af15a9 Mon Sep 17 00:00:00 2001
From: Amul Sul <amul.sul@enterprisedb.com>
Date: Fri, 22 Apr 2022 01:26:29 -0400
Subject: [PATCH v2 3/5] Add numeric_to_int64 by refactoring numeric_int8

---
 src/backend/utils/adt/cash.c    |  4 ++--
 src/backend/utils/adt/dbsize.c  |  6 +----
 src/backend/utils/adt/jsonb.c   |  7 +++---
 src/backend/utils/adt/numeric.c | 39 +++++++++++++++++++++++++++++++++
 src/include/utils/numeric.h     |  2 ++
 5 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index f7e78fa1056..d05335dbd47 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -1108,8 +1108,8 @@ numeric_cash(PG_FUNCTION_ARGS)
 	numeric_scale = NumericGetDatum(int64_to_numeric(scale));
 	amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
 
-	/* note that numeric_int8 will round to nearest integer for us */
-	result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
+	/* note that numeric_to_int64 will round to nearest integer for us */
+	result = numeric_to_int64_type(DatumGetNumeric(amount), "money");
 
 	PG_RETURN_CASH(result);
 }
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 0576764ac4b..afd6a7ab623 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -701,7 +701,6 @@ pg_size_bytes(PG_FUNCTION_ARGS)
 			   *endptr;
 	char		saved_char;
 	Numeric		num;
-	int64		result;
 	bool		have_digits = false;
 
 	str = text_to_cstring(arg);
@@ -826,10 +825,7 @@ pg_size_bytes(PG_FUNCTION_ARGS)
 		}
 	}
 
-	result = DatumGetInt64(DirectFunctionCall1(numeric_int8,
-											   NumericGetDatum(num)));
-
-	PG_RETURN_INT64(result);
+	PG_RETURN_INT64(numeric_to_int64(num));
 }
 
 /*
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 26d81366c9f..58f45afb9e2 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -2174,17 +2174,16 @@ jsonb_int8(PG_FUNCTION_ARGS)
 {
 	Jsonb	   *in = PG_GETARG_JSONB_P(0);
 	JsonbValue	v;
-	Datum		retValue;
+	int64		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v) || v.type != jbvNumeric)
 		cannotCastJsonbValue(v.type, "bigint");
 
-	retValue = DirectFunctionCall1(numeric_int8,
-								   NumericGetDatum(v.val.numeric));
+	retValue = numeric_to_int64(v.val.numeric);
 
 	PG_FREE_IF_COPY(in, 0);
 
-	PG_RETURN_DATUM(retValue);
+	PG_RETURN_INT64(retValue);
 }
 
 Datum
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 3157df7cd7b..d4970e572aa 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4150,6 +4150,45 @@ int64_to_numeric(int64 val)
 	return res;
 }
 
+int64
+numeric_to_int64(Numeric num)
+{
+	return numeric_to_int64_type(num, "bigint");
+}
+
+/*
+ * typeName is the user-visible type name of uint64 used for the error
+ * reporting.
+ */
+int64
+numeric_to_int64_type(Numeric num, char *typeName)
+{
+	NumericVar	x;
+	int64		result;
+
+	if (NUMERIC_IS_SPECIAL(num))
+	{
+		if (NUMERIC_IS_NAN(num))
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot convert NaN to %s", typeName)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot convert infinity to %s", typeName)));
+	}
+
+	/* Convert to variable format and thence to int8 */
+	init_var_from_num(num, &x);
+
+	if (!numericvar_to_int64(&x, &result))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("%s out of range", typeName)));
+
+	return result;
+}
+
 /*
  * Convert val1/(10**val2) to numeric.  This is much faster than normal
  * numeric division.
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 1b9ad3b5b58..11814ad828e 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -73,6 +73,8 @@ extern char *numeric_out_sci(Numeric num, int scale);
 extern char *numeric_normalize(Numeric num);
 
 extern Numeric int64_to_numeric(int64 val);
+extern int64 numeric_to_int64(Numeric num);
+extern int64 numeric_to_int64_type(Numeric num, char *typeName);
 extern Numeric int64_div_fast_to_numeric(int64 val1, int log10val2);
 extern uint64 numeric_to_uint64_type(Numeric num, char *typeName);
 
-- 
2.18.0

