From 50861cd683e86d5ef2dc1cb669fb503225e4eb98 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 30 Mar 2016 17:25:03 -0400
Subject: [PATCH] Improve portability of I/O behavior for the geometric types.

Formerly, the geometric I/O routines such as box_in and point_out relied
directly on strtod() and sprintf() for conversion of the float8 component
values of their data types.  However, the behavior of those functions is
pretty platform-dependent, especially for edge-case values such as
infinities and NaNs.  This was exposed by commit acdf2a8b372aec1d, which
added test cases involving boxes with infinity endpoints, and immediately
failed on Windows and AIX buildfarm members.  We solved these problems
years ago in the main float8in and float8out functions, so let's fix it
by making the geometric types use that code instead of depending directly
on the platform-supplied functions.

To do this, refactor the float8in code so that it can be used to parse
just part of a string, and as a convenience make the guts of float8out
usable without going through DirectFunctionCall.

While at it, get rid of geo_ops.c's fairly shaky assumptions about the
maximum output string length for a double, by having it build results in
StringInfo buffers instead of fixed-length strings.

In passing, convert all the "invalid input syntax for type foo" messages
in this area of the code into "invalid input syntax for type %s" to reduce
the number of distinct translatable strings, per recent discussion.
We would have needed a fair number of the latter anyway for code-sharing
reasons, so we might as well just go whole hog.

Note: this patch is by no means intended to guarantee that the geometric
types uniformly behave sanely for infinity or NaN component values.
But any bugs we have in that line were there all along, they were just
harder to reach in a platform-independent way.
---
 src/backend/utils/adt/float.c     |  85 ++++--
 src/backend/utils/adt/geo_ops.c   | 458 ++++++++++++------------------
 src/include/utils/builtins.h      |   3 +
 src/test/regress/expected/box.out |  40 +--
 4 files changed, 272 insertions(+), 314 deletions(-)

diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index d4e5d553477..4ee93ac04e1 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -411,17 +411,35 @@ Datum
 float8in(PG_FUNCTION_ARGS)
 {
 	char	   *num = PG_GETARG_CSTRING(0);
-	char	   *orig_num;
+
+	PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
+}
+
+/*
+ * 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
+ * differences:
+ * 1. Both leading and trailing whitespace are skipped.
+ * 2. If endptr_p is NULL, we throw 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.
+ *
+ * "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.
+ */
+double
+float8in_internal(char *num, char **endptr_p,
+				  const char *type_name, const char *orig_string)
+{
 	double		val;
 	char	   *endptr;
 
-	/*
-	 * endptr points to the first character _after_ the sequence we recognized
-	 * as a valid floating point number. orig_num points to the original input
-	 * string.
-	 */
-	orig_num = num;
-
 	/* skip leading whitespace */
 	while (*num != '\0' && isspace((unsigned char) *num))
 		num++;
@@ -433,8 +451,8 @@ float8in(PG_FUNCTION_ARGS)
 	if (*num == '\0')
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			 errmsg("invalid input syntax for type double precision: \"%s\"",
-					orig_num)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
 
 	errno = 0;
 	val = strtod(num, &endptr);
@@ -497,18 +515,27 @@ float8in(PG_FUNCTION_ARGS)
 			 * precision).  We'd prefer not to throw error for that, so try to
 			 * detect whether it's a "real" out-of-range condition by checking
 			 * to see if the result is zero or huge.
+			 *
+			 * On error, we intentionally complain about double precision not
+			 * the given type name, and we print only the part of the string
+			 * that is the current number.
 			 */
 			if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL)
+			{
+				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",
-						  orig_num)));
+						  errnumber)));
+			}
 		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			 errmsg("invalid input syntax for type double precision: \"%s\"",
-					orig_num)));
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							type_name, orig_string)));
 	}
 #ifdef HAVE_BUGGY_SOLARIS_STRTOD
 	else
@@ -527,16 +554,16 @@ float8in(PG_FUNCTION_ARGS)
 	while (*endptr != '\0' && isspace((unsigned char) *endptr))
 		endptr++;
 
-	/* if there is any junk left at the end of the string, bail out */
-	if (*endptr != '\0')
+	/* report stopping point if wanted, else complain if not end of string */
+	if (endptr_p)
+		*endptr_p = endptr;
+	else if (*endptr != '\0')
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			 errmsg("invalid input syntax for type double precision: \"%s\"",
-					orig_num)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
 
-	CHECKFLOATVAL(val, true, true);
-
-	PG_RETURN_FLOAT8(val);
+	return val;
 }
 
 /*
@@ -547,10 +574,24 @@ Datum
 float8out(PG_FUNCTION_ARGS)
 {
 	float8		num = PG_GETARG_FLOAT8(0);
+
+	PG_RETURN_CSTRING(float8out_internal(num));
+}
+
+/*
+ * float8out_internal - guts of float8out()
+ *
+ * This is exposed for use by functions that want a reasonably
+ * platform-independent way of outputting doubles.
+ * The result is always palloc'd.
+ */
+char *
+float8out_internal(double num)
+{
 	char	   *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);
 
 	if (isnan(num))
-		PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
+		return strcpy(ascii, "NaN");
 
 	switch (is_infinite(num))
 	{
@@ -571,7 +612,7 @@ float8out(PG_FUNCTION_ARGS)
 			}
 	}
 
-	PG_RETURN_CSTRING(ascii);
+	return ascii;
 }
 
 /*
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index b84673d64c1..657bcee4fd4 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -57,12 +57,16 @@ static void make_bound_box(POLYGON *poly);
 static bool plist_same(int npts, Point *p1, Point *p2);
 static Point *point_construct(double x, double y);
 static Point *point_copy(Point *pt);
-static int	single_decode(char *str, float8 *x, char **ss);
-static int	single_encode(float8 x, char *str);
-static int	pair_decode(char *str, float8 *x, float8 *y, char **s);
-static int	pair_encode(float8 x, float8 y, char *str);
+static double single_decode(char *num, char **endptr_p,
+			  const char *type_name, const char *orig_string);
+static void single_encode(float8 x, StringInfo str);
+static void pair_decode(char *str, double *x, double *y, char **endptr_p,
+			const char *type_name, const char *orig_string);
+static void pair_encode(float8 x, float8 y, StringInfo str);
 static int	pair_count(char *s, char delim);
-static int	path_decode(int opentype, int npts, char *str, int *isopen, char **ss, Point *p);
+static void path_decode(char *str, bool opentype, int npts, Point *p,
+			bool *isopen, char **endptr_p,
+			const char *type_name, const char *orig_string);
 static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
 static void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
 static double box_ar(BOX *box);
@@ -91,10 +95,6 @@ static double dist_ppoly_internal(Point *pt, POLYGON *poly);
 #define LDELIM_C		'<'
 #define RDELIM_C		'>'
 
-/* Maximum number of characters printed by pair_encode() */
-/* ...+3+7 : 3 accounts for extra_float_digits max value */
-#define P_MAXLEN (2*(DBL_DIG+3+7)+1)
-
 
 /*
  * Geometric data types are composed of points.
@@ -121,195 +121,166 @@ static double dist_ppoly_internal(Point *pt, POLYGON *poly);
  *	and restore that order for text output - tgl 97/01/16
  */
 
-static int
-single_decode(char *str, float8 *x, char **s)
+static double
+single_decode(char *num, char **endptr_p,
+			  const char *type_name, const char *orig_string)
 {
-	char	   *cp;
-
-	if (!PointerIsValid(str))
-		return FALSE;
-
-	*x = strtod(str, &cp);
-
-#ifdef GEODEBUG
-	printf("single_decode- decoded first %d chars of \"%s\" to %g\n",
-		   (int) (cp - str), str, *x);
-#endif
-
-	if (s != NULL)
-	{
-		while (isspace((unsigned char) *cp))
-			cp++;
-		*s = cp;
-	}
-
-	return TRUE;
+	return float8in_internal(num, endptr_p, type_name, orig_string);
 }	/* single_decode() */
 
-static int
-single_encode(float8 x, char *str)
+static void
+single_encode(float8 x, StringInfo str)
 {
-	int			ndig = DBL_DIG + extra_float_digits;
+	char	   *xstr = float8out_internal(x);
 
-	if (ndig < 1)
-		ndig = 1;
-
-	sprintf(str, "%.*g", ndig, x);
-	return TRUE;
+	appendStringInfoString(str, xstr);
+	pfree(xstr);
 }	/* single_encode() */
 
-static int
-pair_decode(char *str, float8 *x, float8 *y, char **s)
+static void
+pair_decode(char *str, double *x, double *y, char **endptr_p,
+			const char *type_name, const char *orig_string)
 {
-	int			has_delim;
-	char	   *cp;
-
-	if (!PointerIsValid(str))
-		return FALSE;
+	bool		has_delim;
 
 	while (isspace((unsigned char) *str))
 		str++;
 	if ((has_delim = (*str == LDELIM)))
 		str++;
 
-	while (isspace((unsigned char) *str))
-		str++;
-	*x = strtod(str, &cp);
-	if (cp <= str)
-		return FALSE;
-	while (isspace((unsigned char) *cp))
-		cp++;
-	if (*cp++ != DELIM)
-		return FALSE;
-	while (isspace((unsigned char) *cp))
-		cp++;
-	*y = strtod(cp, &str);
-	if (str <= cp)
-		return FALSE;
-	while (isspace((unsigned char) *str))
-		str++;
+	*x = float8in_internal(str, &str, type_name, orig_string);
+
+	if (*str++ != DELIM)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
+
+	*y = float8in_internal(str, &str, type_name, orig_string);
+
 	if (has_delim)
 	{
-		if (*str != RDELIM)
-			return FALSE;
-		str++;
+		if (*str++ != RDELIM)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							type_name, orig_string)));
 		while (isspace((unsigned char) *str))
 			str++;
 	}
-	if (s != NULL)
-		*s = str;
 
-	return TRUE;
+	/* report stopping point if wanted, else complain if not end of string */
+	if (endptr_p)
+		*endptr_p = str;
+	else if (*str != '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
 }
 
-static int
-pair_encode(float8 x, float8 y, char *str)
+static void
+pair_encode(float8 x, float8 y, StringInfo str)
 {
-	int			ndig = DBL_DIG + extra_float_digits;
+	char	   *xstr = float8out_internal(x);
+	char	   *ystr = float8out_internal(y);
 
-	if (ndig < 1)
-		ndig = 1;
-
-	sprintf(str, "%.*g,%.*g", ndig, x, ndig, y);
-	return TRUE;
+	appendStringInfo(str, "%s,%s", xstr, ystr);
+	pfree(xstr);
+	pfree(ystr);
 }
 
-static int
-path_decode(int opentype, int npts, char *str, int *isopen, char **ss, Point *p)
+static void
+path_decode(char *str, bool opentype, int npts, Point *p,
+			bool *isopen, char **endptr_p,
+			const char *type_name, const char *orig_string)
 {
 	int			depth = 0;
-	char	   *s,
-			   *cp;
+	char	   *cp;
 	int			i;
 
-	s = str;
-	while (isspace((unsigned char) *s))
-		s++;
-	if ((*isopen = (*s == LDELIM_EP)))
+	while (isspace((unsigned char) *str))
+		str++;
+	if ((*isopen = (*str == LDELIM_EP)))
 	{
 		/* no open delimiter allowed? */
 		if (!opentype)
-			return FALSE;
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							type_name, orig_string)));
 		depth++;
-		s++;
-		while (isspace((unsigned char) *s))
-			s++;
-
+		str++;
 	}
-	else if (*s == LDELIM)
+	else if (*str == LDELIM)
 	{
-		cp = (s + 1);
+		cp = (str + 1);
 		while (isspace((unsigned char) *cp))
 			cp++;
 		if (*cp == LDELIM)
 		{
-#ifdef NOT_USED
-			/* nested delimiters with only one point? */
-			if (npts <= 1)
-				return FALSE;
-#endif
 			depth++;
-			s = cp;
+			str = cp;
 		}
-		else if (strrchr(s, LDELIM) == s)
+		else if (strrchr(str, LDELIM) == str)
 		{
 			depth++;
-			s = cp;
+			str = cp;
 		}
 	}
 
 	for (i = 0; i < npts; i++)
 	{
-		if (!pair_decode(s, &(p->x), &(p->y), &s))
-			return FALSE;
-
-		if (*s == DELIM)
-			s++;
+		pair_decode(str, &(p->x), &(p->y), &str, type_name, orig_string);
+		if (*str == DELIM)
+			str++;
 		p++;
 	}
 
+	while (isspace((unsigned char) *str))
+		str++;
 	while (depth > 0)
 	{
-		if ((*s == RDELIM)
-			|| ((*s == RDELIM_EP) && (*isopen) && (depth == 1)))
+		if ((*str == RDELIM)
+			|| ((*str == RDELIM_EP) && (*isopen) && (depth == 1)))
 		{
 			depth--;
-			s++;
-			while (isspace((unsigned char) *s))
-				s++;
+			str++;
+			while (isspace((unsigned char) *str))
+				str++;
 		}
 		else
-			return FALSE;
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							type_name, orig_string)));
 	}
-	*ss = s;
 
-	return TRUE;
+	/* report stopping point if wanted, else complain if not end of string */
+	if (endptr_p)
+		*endptr_p = str;
+	else if (*str != '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						type_name, orig_string)));
 }	/* path_decode() */
 
 static char *
 path_encode(enum path_delim path_delim, int npts, Point *pt)
 {
-	int			size = npts * (P_MAXLEN + 3) + 2;
-	char	   *result;
-	char	   *cp;
+	StringInfoData str;
 	int			i;
 
-	/* Check for integer overflow */
-	if ((size - 2) / npts != (P_MAXLEN + 3))
-		ereport(ERROR,
-				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-				 errmsg("too many points requested")));
+	initStringInfo(&str);
 
-	result = palloc(size);
-
-	cp = result;
 	switch (path_delim)
 	{
 		case PATH_CLOSED:
-			*cp++ = LDELIM;
+			appendStringInfoChar(&str, LDELIM);
 			break;
 		case PATH_OPEN:
-			*cp++ = LDELIM_EP;
+			appendStringInfoChar(&str, LDELIM_EP);
 			break;
 		case PATH_NONE:
 			break;
@@ -317,32 +288,27 @@ path_encode(enum path_delim path_delim, int npts, Point *pt)
 
 	for (i = 0; i < npts; i++)
 	{
-		*cp++ = LDELIM;
-		if (!pair_encode(pt->x, pt->y, cp))
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("could not format \"path\" value")));
-
-		cp += strlen(cp);
-		*cp++ = RDELIM;
-		*cp++ = DELIM;
+		if (i > 0)
+			appendStringInfoChar(&str, DELIM);
+		appendStringInfoChar(&str, LDELIM);
+		pair_encode(pt->x, pt->y, &str);
+		appendStringInfoChar(&str, RDELIM);
 		pt++;
 	}
-	cp--;
+
 	switch (path_delim)
 	{
 		case PATH_CLOSED:
-			*cp++ = RDELIM;
+			appendStringInfoChar(&str, RDELIM);
 			break;
 		case PATH_OPEN:
-			*cp++ = RDELIM_EP;
+			appendStringInfoChar(&str, RDELIM_EP);
 			break;
 		case PATH_NONE:
 			break;
 	}
-	*cp = '\0';
 
-	return result;
+	return str.data;
 }	/* path_encode() */
 
 /*-------------------------------------------------------------
@@ -387,16 +353,11 @@ box_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
 	BOX		   *box = (BOX *) palloc(sizeof(BOX));
-	int			isopen;
-	char	   *s;
+	bool		isopen;
 	double		x,
 				y;
 
-	if ((!path_decode(FALSE, 2, str, &isopen, &s, &(box->high)))
-		|| (*s != '\0'))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type box: \"%s\"", str)));
+	path_decode(str, false, 2, &(box->high), &isopen, NULL, "box", str);
 
 	/* reorder corners if necessary... */
 	if (box->high.x < box->low.x)
@@ -935,43 +896,22 @@ box_diagonal(PG_FUNCTION_ARGS)
  ***********************************************************************/
 
 static bool
-line_decode(const char *str, LINE *line)
+line_decode(char *s, const char *str, LINE *line)
 {
-	char	   *tail;
-
-	while (isspace((unsigned char) *str))
-		str++;
-	if (*str++ != '{')
+	/* s was already advanced over leading '{' */
+	line->A = single_decode(s, &s, "line", str);
+	if (*s++ != DELIM)
 		return false;
-	line->A = strtod(str, &tail);
-	if (tail <= str)
+	line->B = single_decode(s, &s, "line", str);
+	if (*s++ != DELIM)
 		return false;
-	str = tail;
-	while (isspace((unsigned char) *str))
-		str++;
-	if (*str++ != DELIM)
+	line->C = single_decode(s, &s, "line", str);
+	if (*s++ != '}')
 		return false;
-	line->B = strtod(str, &tail);
-	if (tail <= str)
+	while (isspace((unsigned char) *s))
+		s++;
+	if (*s != '\0')
 		return false;
-	str = tail;
-	while (isspace((unsigned char) *str))
-		str++;
-	if (*str++ != DELIM)
-		return false;
-	line->C = strtod(str, &tail);
-	if (tail <= str)
-		return false;
-	str = tail;
-	while (isspace((unsigned char) *str))
-		str++;
-	if (*str++ != '}')
-		return false;
-	while (isspace((unsigned char) *str))
-		str++;
-	if (*str)
-		return false;
-
 	return true;
 }
 
@@ -979,33 +919,35 @@ Datum
 line_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
-	LINE	   *line;
+	LINE	   *line = (LINE *) palloc(sizeof(LINE));
 	LSEG		lseg;
-	int			isopen;
+	bool		isopen;
 	char	   *s;
 
-	line = (LINE *) palloc(sizeof(LINE));
-
-	if (path_decode(TRUE, 2, str, &isopen, &s, &(lseg.p[0])) && *s == '\0')
+	s = str;
+	while (isspace((unsigned char) *s))
+		s++;
+	if (*s == '{')
 	{
-		if (FPeq(lseg.p[0].x, lseg.p[1].x) && FPeq(lseg.p[0].y, lseg.p[1].y))
+		if (!line_decode(s + 1, str, line))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-					 errmsg("invalid line specification: must be two distinct points")));
-
-		line_construct_pts(line, &lseg.p[0], &lseg.p[1]);
-	}
-	else if (line_decode(str, line))
-	{
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							"line", str)));
 		if (FPzero(line->A) && FPzero(line->B))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("invalid line specification: A and B cannot both be zero")));
 	}
 	else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type line: \"%s\"", str)));
+	{
+		path_decode(s, true, 2, &(lseg.p[0]), &isopen, NULL, "line", str);
+		if (FPeq(lseg.p[0].x, lseg.p[1].x) && FPeq(lseg.p[0].y, lseg.p[1].y))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid line specification: must be two distinct points")));
+		line_construct_pts(line, &lseg.p[0], &lseg.p[1]);
+	}
 
 	PG_RETURN_LINE_P(line);
 }
@@ -1015,12 +957,11 @@ Datum
 line_out(PG_FUNCTION_ARGS)
 {
 	LINE	   *line = PG_GETARG_LINE_P(0);
-	int			ndig = DBL_DIG + extra_float_digits;
+	char	   *astr = float8out_internal(line->A);
+	char	   *bstr = float8out_internal(line->B);
+	char	   *cstr = float8out_internal(line->C);
 
-	if (ndig < 1)
-		ndig = 1;
-
-	PG_RETURN_CSTRING(psprintf("{%.*g,%.*g,%.*g}", ndig, line->A, ndig, line->B, ndig, line->C));
+	PG_RETURN_CSTRING(psprintf("{%s,%s,%s}", astr, bstr, cstr));
 }
 
 /*
@@ -1367,7 +1308,7 @@ path_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
 	PATH	   *path;
-	int			isopen;
+	bool		isopen;
 	char	   *s;
 	int			npts;
 	int			size;
@@ -1377,7 +1318,8 @@ path_in(PG_FUNCTION_ARGS)
 	if ((npts = pair_count(str, ',')) <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type path: \"%s\"", str)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"path", str)));
 
 	s = str;
 	while (isspace((unsigned char) *s))
@@ -1404,11 +1346,23 @@ path_in(PG_FUNCTION_ARGS)
 	SET_VARSIZE(path, size);
 	path->npts = npts;
 
-	if ((!path_decode(TRUE, npts, s, &isopen, &s, &(path->p[0])))
-	&& (!((depth == 0) && (*s == '\0'))) && !((depth >= 1) && (*s == RDELIM)))
+	path_decode(s, true, npts, &(path->p[0]), &isopen, &s, "path", str);
+
+	if (depth >= 1)
+	{
+		if (*s++ != RDELIM)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							"path", str)));
+		while (isspace((unsigned char) *s))
+			s++;
+	}
+	if (*s != '\0')
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type path: \"%s\"", str)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"path", str)));
 
 	path->closed = (!isopen);
 	/* prevent instability in unused pad bytes */
@@ -1782,21 +1736,9 @@ Datum
 point_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
-	Point	   *point;
-	double		x,
-				y;
-	char	   *s;
-
-	if (!pair_decode(str, &x, &y, &s) || (*s != '\0'))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type point: \"%s\"", str)));
-
-	point = (Point *) palloc(sizeof(Point));
-
-	point->x = x;
-	point->y = y;
+	Point	   *point = (Point *) palloc(sizeof(Point));
 
+	pair_decode(str, &point->x, &point->y, NULL, "point", str);
 	PG_RETURN_POINT_P(point);
 }
 
@@ -2008,18 +1950,10 @@ Datum
 lseg_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
-	LSEG	   *lseg;
-	int			isopen;
-	char	   *s;
-
-	lseg = (LSEG *) palloc(sizeof(LSEG));
-
-	if ((!path_decode(TRUE, 2, str, &isopen, &s, &(lseg->p[0])))
-		|| (*s != '\0'))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type lseg: \"%s\"", str)));
+	LSEG	   *lseg = (LSEG *) palloc(sizeof(LSEG));
+	bool		isopen;
 
+	path_decode(str, true, 2, &(lseg->p[0]), &isopen, NULL, "lseg", str);
 	PG_RETURN_LSEG_P(lseg);
 }
 
@@ -3480,13 +3414,13 @@ poly_in(PG_FUNCTION_ARGS)
 	int			npts;
 	int			size;
 	int			base_size;
-	int			isopen;
-	char	   *s;
+	bool		isopen;
 
 	if ((npts = pair_count(str, ',')) <= 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			  errmsg("invalid input syntax for type polygon: \"%s\"", str)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"polygon", str)));
 
 	base_size = sizeof(poly->p[0]) * npts;
 	size = offsetof(POLYGON, p) +base_size;
@@ -3502,11 +3436,7 @@ poly_in(PG_FUNCTION_ARGS)
 	SET_VARSIZE(poly, size);
 	poly->npts = npts;
 
-	if ((!path_decode(FALSE, npts, str, &isopen, &s, &(poly->p[0])))
-		|| (*s != '\0'))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			  errmsg("invalid input syntax for type polygon: \"%s\"", str)));
+	path_decode(str, false, npts, &(poly->p[0]), &isopen, NULL, "polygon", str);
 
 	make_bound_box(poly);
 
@@ -4595,13 +4525,11 @@ Datum
 circle_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
-	CIRCLE	   *circle;
+	CIRCLE	   *circle = (CIRCLE *) palloc(sizeof(CIRCLE));
 	char	   *s,
 			   *cp;
 	int			depth = 0;
 
-	circle = (CIRCLE *) palloc(sizeof(CIRCLE));
-
 	s = str;
 	while (isspace((unsigned char) *s))
 		s++;
@@ -4615,20 +4543,17 @@ circle_in(PG_FUNCTION_ARGS)
 			s = cp;
 	}
 
-	if (!pair_decode(s, &circle->center.x, &circle->center.y, &s))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			   errmsg("invalid input syntax for type circle: \"%s\"", str)));
+	pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle", str);
 
 	if (*s == DELIM)
 		s++;
-	while (isspace((unsigned char) *s))
-		s++;
 
-	if ((!single_decode(s, &circle->radius, &s)) || (circle->radius < 0))
+	circle->radius = single_decode(s, &s, "circle", str);
+	if (circle->radius < 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			   errmsg("invalid input syntax for type circle: \"%s\"", str)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"circle", str)));
 
 	while (depth > 0)
 	{
@@ -4643,13 +4568,15 @@ circle_in(PG_FUNCTION_ARGS)
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			   errmsg("invalid input syntax for type circle: \"%s\"", str)));
+					 errmsg("invalid input syntax for type %s: \"%s\"",
+							"circle", str)));
 	}
 
 	if (*s != '\0')
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			   errmsg("invalid input syntax for type circle: \"%s\"", str)));
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"circle", str)));
 
 	PG_RETURN_CIRCLE_P(circle);
 }
@@ -4660,32 +4587,19 @@ Datum
 circle_out(PG_FUNCTION_ARGS)
 {
 	CIRCLE	   *circle = PG_GETARG_CIRCLE_P(0);
-	char	   *result;
-	char	   *cp;
+	StringInfoData str;
 
-	result = palloc(2 * P_MAXLEN + 6);
+	initStringInfo(&str);
 
-	cp = result;
-	*cp++ = LDELIM_C;
-	*cp++ = LDELIM;
-	if (!pair_encode(circle->center.x, circle->center.y, cp))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("could not format \"circle\" value")));
+	appendStringInfoChar(&str, LDELIM_C);
+	appendStringInfoChar(&str, LDELIM);
+	pair_encode(circle->center.x, circle->center.y, &str);
+	appendStringInfoChar(&str, RDELIM);
+	appendStringInfoChar(&str, DELIM);
+	single_encode(circle->radius, &str);
+	appendStringInfoChar(&str, RDELIM_C);
 
-	cp += strlen(cp);
-	*cp++ = RDELIM;
-	*cp++ = DELIM;
-	if (!single_encode(circle->radius, cp))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("could not format \"circle\" value")));
-
-	cp += strlen(cp);
-	*cp++ = RDELIM_C;
-	*cp = '\0';
-
-	PG_RETURN_CSTRING(result);
+	PG_RETURN_CSTRING(str.data);
 }
 
 /*
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 206288da810..d5c4b01ada4 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -343,6 +343,9 @@ extern float get_float4_infinity(void);
 extern double get_float8_nan(void);
 extern float get_float4_nan(void);
 extern int	is_infinite(double val);
+extern double float8in_internal(char *num, char **endptr_p,
+				  const char *type_name, const char *orig_string);
+extern char *float8out_internal(double num);
 
 extern Datum float4in(PG_FUNCTION_ARGS);
 extern Datum float4out(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/box.out b/src/test/regress/expected/box.out
index 300190f762d..7e251a3a569 100644
--- a/src/test/regress/expected/box.out
+++ b/src/test/regress/expected/box.out
@@ -232,15 +232,15 @@ INSERT INTO box_temp
 		   ('(-infinity,-infinity)(infinity,infinity)');
 SET enable_seqscan = false;
 SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)';
-        f1        
-------------------
+             f1             
+----------------------------
  (2,2),(1,1)
  (4,4),(2,2)
  (6,6),(3,3)
  (8,8),(4,4)
  (-0,100),(0,0)
- (0,inf),(0,100)
- (0,inf),(-inf,0)
+ (0,Infinity),(0,100)
+ (0,Infinity),(-Infinity,0)
 (7 rows)
 
 EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)';
@@ -251,16 +251,16 @@ EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)';
 (2 rows)
 
 SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)';
-        f1        
-------------------
+             f1             
+----------------------------
  (2,2),(1,1)
  (4,4),(2,2)
  (6,6),(3,3)
  (8,8),(4,4)
  (10,10),(5,5)
  (-0,100),(0,0)
- (0,inf),(0,100)
- (0,inf),(-inf,0)
+ (0,Infinity),(0,100)
+ (0,Infinity),(-Infinity,0)
 (8 rows)
 
 EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)';
@@ -271,8 +271,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)';
 (2 rows)
 
 SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)';
-          f1           
------------------------
+                    f1                     
+-------------------------------------------
  (20,20),(10,10)
  (22,22),(11,11)
  (24,24),(12,12)
@@ -289,7 +289,7 @@ SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)';
  (46,46),(23,23)
  (48,48),(24,24)
  (50,50),(25,25)
- (inf,inf),(-inf,-inf)
+ (Infinity,Infinity),(-Infinity,-Infinity)
 (17 rows)
 
 EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)';
@@ -375,10 +375,10 @@ EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 &<| '(10,4.3333334),(5,1)';
 (2 rows)
 
 SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49.99)';
-        f1         
--------------------
+          f1          
+----------------------
  (100,100),(50,50)
- (0,inf),(0,100)
+ (0,Infinity),(0,100)
 (2 rows)
 
 EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49.99)';
@@ -389,8 +389,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49
 (2 rows)
 
 SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)';
-        f1         
--------------------
+          f1          
+----------------------
  (82,82),(41,41)
  (84,84),(42,42)
  (86,86),(43,43)
@@ -401,7 +401,7 @@ SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)';
  (96,96),(48,48)
  (98,98),(49,49)
  (100,100),(50,50)
- (0,inf),(0,100)
+ (0,Infinity),(0,100)
 (11 rows)
 
 EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)';
@@ -412,12 +412,12 @@ EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)';
 (2 rows)
 
 SELECT * FROM box_temp WHERE f1 @> '(10,11),(15,16)';
-          f1           
------------------------
+                    f1                     
+-------------------------------------------
  (16,16),(8,8)
  (18,18),(9,9)
  (20,20),(10,10)
- (inf,inf),(-inf,-inf)
+ (Infinity,Infinity),(-Infinity,-Infinity)
 (4 rows)
 
 EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 @> '(10,11),(15,15)';