From d2c744aa56aca7fe756a156a8e2109aaa676e54f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 8 Nov 2002 17:37:52 +0000
Subject: [PATCH] Add extra_float_digits GUC parameter to allow adjustment of
 displayed precision for float4, float8, and geometric types.  Set it in
 pg_dump so that float data can be dumped/reloaded exactly (at least on
 platforms where the float I/O support is properly implemented).  Initial
 patch by Pedro Ferreira, some additional work by Tom Lane.

---
 doc/src/sgml/runtime.sgml                     | 26 ++++++++++++-
 src/backend/utils/adt/float.c                 | 31 +++++++++++----
 src/backend/utils/adt/geo_ops.c               | 39 ++++++++++++-------
 src/backend/utils/misc/guc.c                  |  7 +++-
 src/backend/utils/misc/postgresql.conf.sample |  3 +-
 src/bin/pg_dump/pg_dump.c                     | 31 ++++++++++-----
 src/bin/psql/tab-complete.c                   |  3 +-
 src/include/utils/builtins.h                  |  4 +-
 8 files changed, 106 insertions(+), 38 deletions(-)

diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 25a39862042..c5718040fcb 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.148 2002/11/05 23:16:56 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.149 2002/11/08 17:37:52 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -1448,6 +1448,30 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <indexterm>
+       <primary>significant digits</primary>
+      </indexterm>
+      <indexterm>
+       <primary>display</primary>
+       <secondary>of float numbers</secondary>
+      </indexterm>
+
+      <term><varname>EXTRA_FLOAT_DIGITS</varname> (<type>integer</type>)</term>
+      <listitem>
+       <para>
+        This parameter adjusts the number of digits displayed for
+	floating-point values, including <type>float4</>, <type>float8</>,
+	and geometric datatypes.  The parameter value is added to the
+	standard number of digits (<literal>FLT_DIG</> or <literal>DBL_DIG</>
+	as appropriate).  The value can be set as high as 2, to include
+	partially-significant digits; this is especially useful for dumping
+	float data that needs to be restored exactly.  Or it can be set
+	negative to suppress unwanted digits.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><varname>KRB_SERVER_KEYFILE</varname> (<type>string</type>)</term>
       <listitem>
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index fa34e105c15..c0acc554b8e 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.82 2002/10/19 02:08:17 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.83 2002/11/08 17:37:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -94,9 +94,6 @@ extern double rint(double x);
 #endif   /* NeXT check */
 
 
-static void CheckFloat4Val(double val);
-static void CheckFloat8Val(double val);
-
 #ifndef M_PI
 /* from my RH5.2 gcc math.h file - thomas 2000-04-03 */
 #define M_PI 3.14159265358979323846
@@ -113,8 +110,6 @@ static void CheckFloat8Val(double val);
 #define SHRT_MIN (-32768)
 #endif
 
-#define FORMAT			'g'		/* use "g" output format as standard
-								 * format */
 /* not sure what the following should be, but better to make it over-sufficient */
 #define MAXFLOATWIDTH	64
 #define MAXDOUBLEWIDTH	128
@@ -128,6 +123,14 @@ static void CheckFloat8Val(double val);
 #define FLOAT8_MIN		 DBL_MIN
 
 
+/* Configurable GUC parameter */
+int		extra_float_digits = 0;	/* Added to DBL_DIG or FLT_DIG */
+
+
+static void CheckFloat4Val(double val);
+static void CheckFloat8Val(double val);
+
+
 /*
  * check to see if a float4 val is outside of
  * the FLOAT4_MIN, FLOAT4_MAX bounds.
@@ -228,6 +231,7 @@ float4out(PG_FUNCTION_ARGS)
 	float4		num = PG_GETARG_FLOAT4(0);
 	char	   *ascii = (char *) palloc(MAXFLOATWIDTH + 1);
 	int			infflag;
+	int			ndig;
 
 	if (isnan(num))
 		PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
@@ -237,7 +241,12 @@ float4out(PG_FUNCTION_ARGS)
 	if (infflag < 0)
 		PG_RETURN_CSTRING(strcpy(ascii, "-Infinity"));
 
-	sprintf(ascii, "%.*g", FLT_DIG, num);
+	ndig = FLT_DIG + extra_float_digits;
+	if (ndig < 1)
+		ndig = 1;
+
+	sprintf(ascii, "%.*g", ndig, num);
+
 	PG_RETURN_CSTRING(ascii);
 }
 
@@ -290,6 +299,7 @@ float8out(PG_FUNCTION_ARGS)
 	float8		num = PG_GETARG_FLOAT8(0);
 	char	   *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);
 	int			infflag;
+	int			ndig;
 
 	if (isnan(num))
 		PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
@@ -299,7 +309,12 @@ float8out(PG_FUNCTION_ARGS)
 	if (infflag < 0)
 		PG_RETURN_CSTRING(strcpy(ascii, "-Infinity"));
 
-	sprintf(ascii, "%.*g", DBL_DIG, num);
+	ndig = DBL_DIG + extra_float_digits;
+	if (ndig < 1)
+		ndig = 1;
+
+	sprintf(ascii, "%.*g", ndig, num);
+
 	PG_RETURN_CSTRING(ascii);
 }
 
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 68917a64d60..ddad1225851 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.66 2002/09/05 00:43:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.67 2002/11/08 17:37:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 #include <float.h>
 #include <ctype.h>
 
+#include "utils/builtins.h"
 #include "utils/geo_decls.h"
 
 #ifndef PI
@@ -79,11 +80,9 @@ static Point *line_interpt_internal(LINE *l1, LINE *l2);
 #define LDELIM_C		'<'
 #define RDELIM_C		'>'
 
-/* Maximum number of output digits printed */
-#define P_MAXDIG DBL_DIG
-#define P_MAXLEN (2*(P_MAXDIG+7)+1)
-
-static int	digits8 = P_MAXDIG;
+/* Maximum number of characters printed by pair_encode() */
+/* ...+2+7 : 2 accounts for extra_float_digits max value */
+#define P_MAXLEN (2*(DBL_DIG+2+7)+1)
 
 
 /*
@@ -139,7 +138,12 @@ single_decode(char *str, float8 *x, char **s)
 static int
 single_encode(float8 x, char *str)
 {
-	sprintf(str, "%.*g", digits8, x);
+	int	ndig = DBL_DIG + extra_float_digits;
+
+	if (ndig < 1)
+		ndig = 1;
+
+	sprintf(str, "%.*g", ndig, x);
 	return TRUE;
 }	/* single_encode() */
 
@@ -190,7 +194,12 @@ pair_decode(char *str, float8 *x, float8 *y, char **s)
 static int
 pair_encode(float8 x, float8 y, char *str)
 {
-	sprintf(str, "%.*g,%.*g", digits8, x, digits8, y);
+	int	ndig = DBL_DIG + extra_float_digits;
+
+	if (ndig < 1)
+		ndig = 1;
+
+	sprintf(str, "%.*g,%.*g", ndig, x, ndig, y);
 	return TRUE;
 }
 
@@ -976,7 +985,7 @@ line_construct_pts(LINE *line, Point *pt1, Point *pt2)
 #endif
 #ifdef GEODEBUG
 		printf("line_construct_pts- line is neither vertical nor horizontal (diffs x=%.*g, y=%.*g\n",
-			   digits8, (pt2->x - pt1->x), digits8, (pt2->y - pt1->y));
+			   DBL_DIG, (pt2->x - pt1->x), DBL_DIG, (pt2->y - pt1->y));
 #endif
 	}
 }
@@ -1181,8 +1190,8 @@ line_interpt_internal(LINE *l1, LINE *l2)
 
 #ifdef GEODEBUG
 	printf("line_interpt- lines are A=%.*g, B=%.*g, C=%.*g, A=%.*g, B=%.*g, C=%.*g\n",
-		   digits8, l1->A, digits8, l1->B, digits8, l1->C, digits8, l2->A, digits8, l2->B, digits8, l2->C);
-	printf("line_interpt- lines intersect at (%.*g,%.*g)\n", digits8, x, digits8, y);
+		   DBL_DIG, l1->A, DBL_DIG, l1->B, DBL_DIG, l1->C, DBL_DIG, l2->A, DBL_DIG, l2->B, DBL_DIG, l2->C);
+	printf("line_interpt- lines intersect at (%.*g,%.*g)\n", DBL_DIG, x, DBL_DIG, y);
 #endif
 
 	return result;
@@ -2381,14 +2390,14 @@ interpt_sl(LSEG *lseg, LINE *line)
 	p = line_interpt_internal(&tmp, line);
 #ifdef GEODEBUG
 	printf("interpt_sl- segment is (%.*g %.*g) (%.*g %.*g)\n",
-		   digits8, lseg->p[0].x, digits8, lseg->p[0].y, digits8, lseg->p[1].x, digits8, lseg->p[1].y);
+		   DBL_DIG, lseg->p[0].x, DBL_DIG, lseg->p[0].y, DBL_DIG, lseg->p[1].x, DBL_DIG, lseg->p[1].y);
 	printf("interpt_sl- segment becomes line A=%.*g B=%.*g C=%.*g\n",
-		   digits8, tmp.A, digits8, tmp.B, digits8, tmp.C);
+		   DBL_DIG, tmp.A, DBL_DIG, tmp.B, DBL_DIG, tmp.C);
 #endif
 	if (PointerIsValid(p))
 	{
 #ifdef GEODEBUG
-		printf("interpt_sl- intersection point is (%.*g %.*g)\n", digits8, p->x, digits8, p->y);
+		printf("interpt_sl- intersection point is (%.*g %.*g)\n", DBL_DIG, p->x, DBL_DIG, p->y);
 #endif
 		if (on_ps_internal(p, lseg))
 		{
@@ -3940,7 +3949,7 @@ circle_out(PG_FUNCTION_ARGS)
 	char	   *result;
 	char	   *cp;
 
-	result = palloc(3 * (P_MAXLEN + 1) + 3);
+	result = palloc(2 * P_MAXLEN + 6);
 
 	cp = result;
 	*cp++ = LDELIM_C;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 48c3cdb4bf8..ac1c97fedff 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5,7 +5,7 @@
  * command, configuration file, and command line options.
  * See src/backend/utils/misc/README for more information.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.99 2002/11/01 22:52:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.100 2002/11/08 17:37:52 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -680,6 +680,11 @@ static struct config_int
 		5, 1, 1000, NULL, NULL
 	},
 
+	{
+		{"extra_float_digits", PGC_USERSET}, &extra_float_digits,
+		0, -15, 2, NULL, NULL
+	},
+
 	{
 		{NULL, 0}, NULL, 0, 0, 0, NULL, NULL
 	}
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c5973422635..ee721a358a8 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -127,7 +127,7 @@
 #log_duration = false
 #log_timestamp = false
 
-#log_min_error_statement = error		# Values in order of increasing severity:
+#log_min_error_statement = error	# Values in order of increasing severity:
 									# debug5, debug4, debug3, debug2, debug1,
 					                # info, notice, warning, error
 #debug_print_parse = false
@@ -198,6 +198,7 @@
 #authentication_timeout = 60	# 1-600, in seconds
 #deadlock_timeout = 1000	# in milliseconds
 #default_transaction_isolation = 'read committed'
+#extra_float_digits = 0		# min -15, max 2
 #max_expr_depth = 10000		# min 10
 #max_files_per_process = 1000	# min 25
 #password_encryption = true
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0b9a1e8b7a2..2b111bdce5b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.305 2002/10/22 19:15:23 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.306 2002/11/08 17:37:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -171,6 +171,7 @@ main(int argc, char **argv)
 	const char *pgport = NULL;
 	const char *username = NULL;
 	bool		oids = false;
+	PGresult   *res;
 	TableInfo  *tblinfo;
 	int			numTables;
 	bool		force_password = false;
@@ -549,22 +550,32 @@ main(int argc, char **argv)
 	/*
 	 * Start serializable transaction to dump consistent data.
 	 */
-	{
-		PGresult   *res;
+	res = PQexec(g_conn, "BEGIN");
+	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+		exit_horribly(g_fout, NULL, "BEGIN command failed: %s",
+					  PQerrorMessage(g_conn));
+	PQclear(res);
 
-		res = PQexec(g_conn, "BEGIN");
-		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-			exit_horribly(g_fout, NULL, "BEGIN command failed: %s",
-						  PQerrorMessage(g_conn));
-		PQclear(res);
+	res = PQexec(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+		exit_horribly(g_fout, NULL, "could not set transaction isolation level to serializable: %s",
+					  PQerrorMessage(g_conn));
+	PQclear(res);
 
-		res = PQexec(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+	/*
+	 * If supported, set extra_float_digits so that we can dump float data
+	 * exactly (given correctly implemented float I/O code, anyway)
+	 */
+	if (g_fout->remoteVersion >= 70400)
+	{
+		res = PQexec(g_conn, "SET extra_float_digits TO 2");
 		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-			exit_horribly(g_fout, NULL, "could not set transaction isolation level to serializable: %s",
+			exit_horribly(g_fout, NULL, "could not set extra_float_digits: %s",
 						  PQerrorMessage(g_conn));
 		PQclear(res);
 	}
 
+	/* Find the last built-in OID, if needed */
 	if (g_fout->remoteVersion < 70300)
 	{
 		if (g_fout->remoteVersion >= 70100)
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index ce4a2b5a809..4b3e4c13a9b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
  *
  * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.64 2002/09/04 20:31:36 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.65 2002/11/08 17:37:52 tgl Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -261,6 +261,7 @@ psql_completion(char *text, int start, int end)
 		"max_expr_depth",
 		"commit_delay",
 		"commit_siblings",
+		"extra_float_digits",
 
 		"effective_cache_size",
 		"random_page_cost",
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index a933520daa2..f134bdb4955 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.204 2002/11/02 18:41:22 tgl Exp $
+ * $Id: builtins.h,v 1.205 2002/11/08 17:37:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -212,6 +212,8 @@ extern Datum btnamecmp(PG_FUNCTION_ARGS);
 extern Datum bttextcmp(PG_FUNCTION_ARGS);
 
 /* float.c */
+extern int	extra_float_digits;
+
 extern Datum float4in(PG_FUNCTION_ARGS);
 extern Datum float4out(PG_FUNCTION_ARGS);
 extern Datum float8in(PG_FUNCTION_ARGS);
-- 
GitLab