From 6dd3d226f7db9783bf15f041407a27ed2f9ac06f Mon Sep 17 00:00:00 2001
From: Maxim Orlov <m.orlov@postgrespro.ru>
Date: Fri, 11 Mar 2022 11:34:26 +0300
Subject: [PATCH v54 4/5] Use 64-bit GUCs

Author: Alexander Korotkov <aekorotkov@gmail.com>
Author: Teodor Sigaev <teodor@sigaev.ru>
Author: Nikita Glukhov <n.gluhov@postgrespro.ru>
Author: Maxim Orlov <orlovmg@gmail.com>
Author: Pavel Borisov <pashkin.elfe@gmail.com>
Author: Yura Sokolov <y.sokolov@postgrespro.ru> <funny.falcon@gmail.com>
Author: Aleksander Alekseev <aleksander@timescale.com>
Discussion: https://postgr.es/m/CACG%3DezZe1NQSCnfHOr78AtAZxJZeCvxrts0ygrxYwe%3DpyyjVWA%40mail.gmail.com
Discussion: https://postgr.es/m/CAJ7c6TPDOYBYrnCAeyndkBktO0WG2xSdYduTF0nxq%2BvfkmTF5Q%40mail.gmail.com
---
 src/backend/access/common/reloptions.c | 187 ++++++++----
 src/backend/utils/misc/guc.c           | 388 +++++++++++++++++++++++++
 src/backend/utils/misc/guc_funcs.c     |  25 ++
 src/backend/utils/misc/guc_tables.c    |  10 +
 src/include/access/reloptions.h        |  13 +
 src/include/utils/guc.h                |  17 ++
 src/include/utils/guc_tables.h         |  19 ++
 src/include/utils/rel.h                |  14 +-
 8 files changed, 608 insertions(+), 65 deletions(-)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index d6eb5d8559..8a46b8cd60 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -258,58 +258,6 @@ static relopt_int intRelOpts[] =
 		},
 		-1, 1, 10000
 	},
-	{
-		{
-			"autovacuum_freeze_min_age",
-			"Minimum age at which VACUUM should freeze a table row, for autovacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, 1000000000
-	},
-	{
-		{
-			"autovacuum_multixact_freeze_min_age",
-			"Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, 1000000000
-	},
-	{
-		{
-			"autovacuum_freeze_max_age",
-			"Age at which to autovacuum a table to prevent transaction ID wraparound",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 100000, 2000000000
-	},
-	{
-		{
-			"autovacuum_multixact_freeze_max_age",
-			"Multixact age at which to autovacuum a table to prevent multixact wraparound",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 10000, 2000000000
-	},
-	{
-		{
-			"autovacuum_freeze_table_age",
-			"Age at which VACUUM should perform a full table sweep to freeze row versions",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		}, -1, 0, 2000000000
-	},
-	{
-		{
-			"autovacuum_multixact_freeze_table_age",
-			"Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		}, -1, 0, 2000000000
-	},
 	{
 		{
 			"log_autovacuum_min_duration",
@@ -380,7 +328,66 @@ static relopt_int intRelOpts[] =
 		},
 		-1, 0, 1024
 	},
+	/* list terminator */
+	{{NULL}}
+};
 
+static relopt_int64 int64RelOpts[] =
+{
+	{
+		{
+			"autovacuum_freeze_min_age",
+			"Minimum age at which VACUUM should freeze a table row, for autovacuum",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+			ShareUpdateExclusiveLock
+		},
+		INT64CONST(-1), INT64CONST(0), INT64CONST(1000000000)
+	},
+	{
+		{
+			"autovacuum_multixact_freeze_min_age",
+			"Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+			ShareUpdateExclusiveLock
+		},
+		INT64CONST(-1), INT64CONST(0), INT64CONST(1000000000)
+	},
+	{
+		{
+			"autovacuum_freeze_max_age",
+			"Age at which to autovacuum a table to prevent transaction ID wraparound",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+			ShareUpdateExclusiveLock
+		},
+		INT64CONST(-1), INT64CONST(100000), INT64CONST(2000000000)
+	},
+	{
+		{
+			"autovacuum_multixact_freeze_max_age",
+			"Multixact age at which to autovacuum a table to prevent multixact wraparound",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+			ShareUpdateExclusiveLock
+		},
+		INT64CONST(-1), INT64CONST(10000), INT64CONST(2000000000)
+	},
+	{
+		{
+			"autovacuum_freeze_table_age",
+			"Age at which VACUUM should perform a full table sweep to freeze row versions",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+			ShareUpdateExclusiveLock
+		},
+		INT64CONST(-1), INT64CONST(0), INT64CONST(2000000000)
+	},
+	{
+		{
+			"autovacuum_multixact_freeze_table_age",
+			"Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+			ShareUpdateExclusiveLock
+		},
+		INT64CONST(-1), INT64CONST(0), INT64CONST(2000000000)
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -595,6 +602,12 @@ initialize_reloptions(void)
 								   intRelOpts[i].gen.lockmode));
 		j++;
 	}
+	for (i = 0; int64RelOpts[i].gen.name; i++)
+	{
+		Assert(DoLockModesConflict(int64RelOpts[i].gen.lockmode,
+								   int64RelOpts[i].gen.lockmode));
+		j++;
+	}
 	for (i = 0; realRelOpts[i].gen.name; i++)
 	{
 		Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
@@ -637,6 +650,14 @@ initialize_reloptions(void)
 		j++;
 	}
 
+	for (i = 0; int64RelOpts[i].gen.name; i++)
+	{
+		relOpts[j] = &int64RelOpts[i].gen;
+		relOpts[j]->type = RELOPT_TYPE_INT64;
+		relOpts[j]->namelen = strlen(relOpts[j]->name);
+		j++;
+	}
+
 	for (i = 0; realRelOpts[i].gen.name; i++)
 	{
 		relOpts[j] = &realRelOpts[i].gen;
@@ -792,6 +813,9 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
 		case RELOPT_TYPE_INT:
 			size = sizeof(relopt_int);
 			break;
+		case RELOPT_TYPE_INT64:
+			size = sizeof(relopt_int64);
+			break;
 		case RELOPT_TYPE_REAL:
 			size = sizeof(relopt_real);
 			break;
@@ -946,6 +970,26 @@ init_real_reloption(bits32 kinds, const char *name, const char *desc,
 	return newoption;
 }
 
+/*
+ * add_int64_reloption
+ *		Add a new 64-bit integer reloption
+ */
+void
+add_int64_reloption(bits32 kinds, const char *name, char *desc,
+					int64 default_val, int64 min_val, int64 max_val,
+					LOCKMODE lockmode)
+{
+	relopt_int64 *newoption;
+
+	newoption = (relopt_int64 *) allocate_reloption(kinds, RELOPT_TYPE_INT64,
+													name, desc, lockmode);
+	newoption->default_val = default_val;
+	newoption->min = min_val;
+	newoption->max = max_val;
+
+	add_reloption((relopt_gen *) newoption);
+}
+
 /*
  * add_real_reloption
  *		Add a new float reloption
@@ -1617,6 +1661,28 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
 									   optint->min, optint->max)));
 			}
 			break;
+		case RELOPT_TYPE_INT64:
+			{
+				relopt_int64 *optint = (relopt_int64 *) option->gen;
+
+				parsed = parse_int64(value, &option->values.int64_val, 0, NULL);
+				if (validate && !parsed)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid value for 64-bit integer option \"%s\": %s",
+									option->gen->name, value)));
+				if (validate && (option->values.int64_val < optint->min ||
+								 option->values.int64_val > optint->max))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("value %s out of bounds for option \"%s\"",
+									value, option->gen->name),
+							 errdetail("Valid values are between \"%lld"
+									   "\" and \"%lld\".",
+									   (long long ) optint->min,
+									   (long long) optint->max)));
+			}
+			break;
 		case RELOPT_TYPE_REAL:
 			{
 				relopt_real *optreal = (relopt_real *) option->gen;
@@ -1772,6 +1838,11 @@ fillRelOptions(void *rdopts, Size basesize,
 							options[i].values.int_val :
 							((relopt_int *) options[i].gen)->default_val;
 						break;
+					case RELOPT_TYPE_INT64:
+						*(int64 *) itempos = options[i].isset ?
+							options[i].values.int64_val :
+							((relopt_int64 *) options[i].gen)->default_val;
+						break;
 					case RELOPT_TYPE_REAL:
 						*(double *) itempos = options[i].isset ?
 							options[i].values.real_val :
@@ -1849,17 +1920,17 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
 		{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
-		{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
+		{"autovacuum_freeze_min_age", RELOPT_TYPE_INT64,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
-		{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
+		{"autovacuum_freeze_max_age", RELOPT_TYPE_INT64,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
-		{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
+		{"autovacuum_freeze_table_age", RELOPT_TYPE_INT64,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
-		{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
+		{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT64,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
-		{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
+		{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT64,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
-		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
+		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT64,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
 		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 547cecde24..24b74015c7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -265,6 +265,8 @@ static bool call_bool_check_hook(struct config_bool *conf, bool *newval,
 								 void **extra, GucSource source, int elevel);
 static bool call_int_check_hook(struct config_int *conf, int *newval,
 								void **extra, GucSource source, int elevel);
+static bool call_int64_check_hook(struct config_int64 *conf, int64 *newval,
+								  void **extra, GucSource source, int elevel);
 static bool call_real_check_hook(struct config_real *conf, double *newval,
 								 void **extra, GucSource source, int elevel);
 static bool call_string_check_hook(struct config_string *conf, char **newval,
@@ -762,6 +764,10 @@ extra_field_used(struct config_generic *gconf, void *extra)
 			if (extra == ((struct config_int *) gconf)->reset_extra)
 				return true;
 			break;
+		case PGC_INT64:
+			if (extra == ((struct config_int64 *) gconf)->reset_extra)
+				return true;
+			break;
 		case PGC_REAL:
 			if (extra == ((struct config_real *) gconf)->reset_extra)
 				return true;
@@ -823,6 +829,10 @@ set_stack_value(struct config_generic *gconf, config_var_value *val)
 			val->val.intval =
 				*((struct config_int *) gconf)->variable;
 			break;
+		case PGC_INT64:
+			val->val.int64val =
+				*((struct config_int64 *) gconf)->variable;
+			break;
 		case PGC_REAL:
 			val->val.realval =
 				*((struct config_real *) gconf)->variable;
@@ -851,6 +861,7 @@ discard_stack_value(struct config_generic *gconf, config_var_value *val)
 	{
 		case PGC_BOOL:
 		case PGC_INT:
+		case PGC_INT64:
 		case PGC_REAL:
 		case PGC_ENUM:
 			/* no need to do anything */
@@ -939,6 +950,14 @@ build_guc_variables(void)
 		num_vars++;
 	}
 
+	for (i = 0; ConfigureNamesInt64[i].gen.name; i++)
+	{
+		struct config_int64 *conf = &ConfigureNamesInt64[i];
+
+		conf->gen.vartype = PGC_INT64;
+		num_vars++;
+	}
+
 	for (i = 0; ConfigureNamesReal[i].gen.name; i++)
 	{
 		struct config_real *conf = &ConfigureNamesReal[i];
@@ -1002,6 +1021,18 @@ build_guc_variables(void)
 		hentry->gucvar = gucvar;
 	}
 
+	for (i = 0; ConfigureNamesInt64[i].gen.name; i++)
+	{
+		struct config_generic *gucvar = &ConfigureNamesInt64[i].gen;
+
+		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
+											  &gucvar->name,
+											  HASH_ENTER,
+											  &found);
+		Assert(!found);
+		hentry->gucvar = gucvar;
+	}
+
 	for (i = 0; ConfigureNamesReal[i].gen.name; i++)
 	{
 		struct config_generic *gucvar = &ConfigureNamesReal[i].gen;
@@ -1429,6 +1460,7 @@ check_GUC_name_for_parameter_acl(const char *name)
  * The following validation rules apply for the values:
  * bool - can be false, otherwise must be same as the boot_val
  * int  - can be 0, otherwise must be same as the boot_val
+ * int64 - can be 0, otherwise must be same as the boot_val
  * real - can be 0.0, otherwise must be same as the boot_val
  * string - can be NULL, otherwise must be strcmp equal to the boot_val
  * enum - must be same as the boot_val
@@ -1464,6 +1496,20 @@ check_GUC_init(struct config_generic *gconf)
 				}
 				break;
 			}
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) gconf;
+
+				if (*conf->variable != 0 && *conf->variable != conf->boot_val)
+				{
+					elog(LOG, "GUC (PGC_INT64) %s, boot_val=%lld, C-var=%lld",
+						 conf->gen.name,
+						 (long long) conf->boot_val,
+						 (long long) *conf->variable);
+					return false;
+				}
+				break;
+			}
 		case PGC_REAL:
 			{
 				struct config_real *conf = (struct config_real *) gconf;
@@ -1694,6 +1740,24 @@ InitializeOneGUCOption(struct config_generic *gconf)
 				conf->gen.extra = conf->reset_extra = extra;
 				break;
 			}
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) gconf;
+				int64		newval = conf->boot_val;
+				void	   *extra = NULL;
+
+				Assert(newval >= conf->min);
+				Assert(newval <= conf->max);
+				if (!call_int64_check_hook(conf, &newval, &extra,
+										   PGC_S_DEFAULT, LOG))
+					elog(FATAL, "failed to initialize %s to %lld",
+						 conf->gen.name, (long long) newval);
+				if (conf->assign_hook)
+					(*conf->assign_hook) (newval, extra);
+				*conf->variable = conf->reset_val = newval;
+				conf->gen.extra = conf->reset_extra = extra;
+				break;
+			}
 		case PGC_REAL:
 			{
 				struct config_real *conf = (struct config_real *) gconf;
@@ -2044,6 +2108,18 @@ ResetAllOptions(void)
 				{
 					struct config_int *conf = (struct config_int *) gconf;
 
+					if (conf->assign_hook)
+						conf->assign_hook(conf->reset_val,
+										  conf->reset_extra);
+					*conf->variable = conf->reset_val;
+					set_extra_field(&conf->gen, &conf->gen.extra,
+									conf->reset_extra);
+					break;
+				}
+			case PGC_INT64:
+				{
+					struct config_int64 *conf = (struct config_int64 *) gconf;
+
 					if (conf->assign_hook)
 						conf->assign_hook(conf->reset_val,
 										  conf->reset_extra);
@@ -2427,6 +2503,24 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
 							int			newval = newvalue.val.intval;
 							void	   *newextra = newvalue.extra;
 
+							if (*conf->variable != newval ||
+								conf->gen.extra != newextra)
+							{
+								if (conf->assign_hook)
+									conf->assign_hook(newval, newextra);
+								*conf->variable = newval;
+								set_extra_field(&conf->gen, &conf->gen.extra,
+												newextra);
+								changed = true;
+							}
+							break;
+						}
+					case PGC_INT64:
+						{
+							struct config_int64 *conf = (struct config_int64 *) gconf;
+							int64		newval = newvalue.val.int64val;
+							void	   *newextra = newvalue.extra;
+
 							if (*conf->variable != newval ||
 								conf->gen.extra != newextra)
 							{
@@ -2949,6 +3043,71 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg)
 	return true;
 }
 
+/*
+ * Try to parse value as an 64-bit integer.  The accepted format is
+ * decimal number.
+ *
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
+ * If not okay and hintmsg is not NULL, *hintmsg is set to a suitable
+ *	HINT message, or NULL if no hint provided.
+ */
+bool
+parse_int64(const char *value, int64 *result, int flags, const char **hintmsg)
+{
+	int64		val;
+	char	   *endptr;
+
+	/* To suppress compiler warnings, always set output params */
+	if (result)
+		*result = 0;
+	if (hintmsg)
+		*hintmsg = NULL;
+
+	/* We assume here that int64 is at least as wide as long */
+	errno = 0;
+	val = strtoi64(value, &endptr, 0);
+
+	if (endptr == value)
+		return false;			/* no HINT for integer syntax error */
+
+	if (errno == ERANGE)
+	{
+		if (hintmsg)
+			*hintmsg = gettext_noop("Value exceeds 64-bit integer range.");
+		return false;
+	}
+
+	/*
+	 * got double format and/or units. For now we attempts parse it as double
+	 * and throw error on 53bit overflow
+	 */
+	if (*endptr != '\0')
+	{
+		double		dval;
+		bool		ok;
+
+		ok = parse_real(value, &dval, flags, hintmsg);
+		if (!ok)
+			return false;
+
+		dval = rint(val);
+
+		if (fabs(dval) >= (double) ((uint64) 1 << 53))
+		{
+			*hintmsg = gettext_noop("Int64 value with units should be positive number < 2^53");
+			return false;
+		}
+
+		val = (int64) dval;
+	}
+
+
+	if (result)
+		*result = val;
+	return true;
+}
+
 /*
  * Try to parse value as a floating point number in the usual format.
  * Optionally, the value can be followed by a unit name if "flags" indicates
@@ -3195,6 +3354,36 @@ parse_and_validate_value(struct config_generic *record,
 					return false;
 			}
 			break;
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) record;
+				const char *hintmsg;
+
+				if (!parse_int64(value, &newval->int64val, conf->gen.flags, &hintmsg))
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid value for parameter \"%s\": \"%s\"",
+									name, value),
+							 hintmsg ? errhint("%s", _(hintmsg)) : 0));
+					return false;
+				}
+
+				if (newval->int64val < conf->min || newval->int64val > conf->max)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("%lld is outside the valid range for parameter \"%s\" (%lld .. %lld)",
+									(long long) newval->int64val, name,
+									(long long) conf->min, (long long) conf->max)));
+					return false;
+				}
+
+				if (!call_int64_check_hook(conf, &newval->int64val, newextra,
+										   source, elevel))
+					return false;
+			}
+			break;
 		case PGC_REAL:
 			{
 				struct config_real *conf = (struct config_real *) record;
@@ -3889,6 +4078,96 @@ set_config_with_handle(const char *name, config_handle *handle,
 					guc_free(newextra);
 				break;
 
+#undef newval
+			}
+
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) record;
+
+#define newval (newval_union.int64val)
+
+				if (value)
+				{
+					if (!parse_and_validate_value(record, name, value,
+												  source, elevel,
+												  &newval_union, &newextra))
+						return 0;
+				}
+				else if (source == PGC_S_DEFAULT)
+				{
+					newval = conf->boot_val;
+					if (!call_int64_check_hook(conf, &newval, &newextra,
+											   source, elevel))
+						return 0;
+				}
+				else
+				{
+					newval = conf->reset_val;
+					newextra = conf->reset_extra;
+					source = conf->gen.reset_source;
+					context = conf->gen.reset_scontext;
+				}
+
+				if (prohibitValueChange)
+				{
+					if (*conf->variable != newval)
+					{
+						record->status |= GUC_PENDING_RESTART;
+						ereport(elevel,
+								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
+										name)));
+						return 0;
+					}
+					record->status &= ~GUC_PENDING_RESTART;
+					return -1;
+				}
+
+				if (changeVal)
+				{
+					/* Save old value to support transaction abort */
+					if (!makeDefault)
+						push_old_value(&conf->gen, action);
+
+					if (conf->assign_hook)
+						(*conf->assign_hook) (newval, newextra);
+					*conf->variable = newval;
+					set_extra_field(&conf->gen, &conf->gen.extra,
+									newextra);
+					conf->gen.source = source;
+					conf->gen.scontext = context;
+				}
+				if (makeDefault)
+				{
+					GucStack   *stack;
+
+					if (conf->gen.reset_source <= source)
+					{
+						conf->reset_val = newval;
+						set_extra_field(&conf->gen, &conf->reset_extra,
+										newextra);
+						conf->gen.reset_source = source;
+						conf->gen.reset_scontext = context;
+					}
+					for (stack = conf->gen.stack; stack; stack = stack->prev)
+					{
+						if (stack->source <= source)
+						{
+							stack->prior.val.intval = newval;
+							set_extra_field(&conf->gen, &stack->prior.extra,
+											newextra);
+							stack->source = source;
+							stack->scontext = context;
+						}
+					}
+				}
+
+				/* Perhaps we didn't install newextra anywhere */
+				if (newextra && !extra_field_used(&conf->gen, newextra))
+					guc_free(newextra);
+				break;
+
 #undef newval
 			}
 
@@ -4331,6 +4610,11 @@ GetConfigOption(const char *name, bool missing_ok, bool restrict_privileged)
 					 *((struct config_int *) record)->variable);
 			return buffer;
 
+		case PGC_INT64:
+			snprintf(buffer, sizeof(buffer), "%lld",
+					 (long long) *((struct config_int64 *) record)->variable);
+			return buffer;
+
 		case PGC_REAL:
 			snprintf(buffer, sizeof(buffer), "%g",
 					 *((struct config_real *) record)->variable);
@@ -4379,6 +4663,11 @@ GetConfigOptionResetString(const char *name)
 					 ((struct config_int *) record)->reset_val);
 			return buffer;
 
+		case PGC_INT64:
+			snprintf(buffer, sizeof(buffer), "%lld",
+					 (long long) ((struct config_int64 *) record)->reset_val);
+			return buffer;
+
 		case PGC_REAL:
 			snprintf(buffer, sizeof(buffer), "%g",
 					 ((struct config_real *) record)->reset_val);
@@ -5330,6 +5619,14 @@ get_explain_guc_options(int *num)
 				}
 				break;
 
+			case PGC_INT64:
+				{
+					struct config_int64 *lconf = (struct config_int64 *) conf;
+
+					modified = (lconf->boot_val != *(lconf->variable));
+				}
+				break;
+
 			case PGC_REAL:
 				{
 					struct config_real *lconf = (struct config_real *) conf;
@@ -5462,6 +5759,21 @@ ShowGUCOption(struct config_generic *record, bool use_units)
 			}
 			break;
 
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) record;
+
+				if (conf->show_hook)
+					val = (*conf->show_hook) ();
+				else
+				{
+					snprintf(buffer, sizeof(buffer), "%lld",
+							 (long long) *conf->variable);
+					val = buffer;
+				}
+			}
+			break;
+
 		case PGC_REAL:
 			{
 				struct config_real *conf = (struct config_real *) record;
@@ -5564,6 +5876,14 @@ write_one_nondefault_variable(FILE *fp, struct config_generic *gconf)
 			}
 			break;
 
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) gconf;
+
+				fprintf(fp, "%lld", (long long) *conf->variable);
+			}
+			break;
+
 		case PGC_REAL:
 			{
 				struct config_real *conf = (struct config_real *) gconf;
@@ -5842,6 +6162,24 @@ estimate_variable_size(struct config_generic *gconf)
 			}
 			break;
 
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) gconf;
+
+				/*
+				 * Instead of getting the exact display length, use max
+				 * length.  Also reduce the max length for typical ranges of
+				 * small values.  Maximum value is 2^63, i.e. 20 chars.
+				 * Include one byte for sign.
+				 */
+#define ABS(x)			((x) >= 0 ? (x) : -(x))
+				if (ABS(*conf->variable) < 1000)
+					valsize = 3 + 1;
+				else
+					valsize = 20 + 1;
+			}
+			break;
+
 		case PGC_REAL:
 			{
 				/*
@@ -6008,6 +6346,14 @@ serialize_variable(char **destptr, Size *maxbytes,
 			}
 			break;
 
+		case PGC_INT64:
+			{
+				struct config_int64 *conf = (struct config_int64 *) gconf;
+
+				do_serialize(destptr, maxbytes, "%lld", (long long) *conf->variable);
+			}
+			break;
+
 		case PGC_REAL:
 			{
 				struct config_real *conf = (struct config_real *) gconf;
@@ -6221,6 +6567,14 @@ RestoreGUCState(void *gucstate)
 				{
 					struct config_int *conf = (struct config_int *) gconf;
 
+					if (conf->reset_extra && conf->reset_extra != gconf->extra)
+						guc_free(conf->reset_extra);
+					break;
+				}
+			case PGC_INT64:
+				{
+					struct config_int64 *conf = (struct config_int64 *) gconf;
+
 					if (conf->reset_extra && conf->reset_extra != gconf->extra)
 						guc_free(conf->reset_extra);
 					break;
@@ -6821,6 +7175,40 @@ call_int_check_hook(struct config_int *conf, int *newval, void **extra,
 	return true;
 }
 
+static bool
+call_int64_check_hook(struct config_int64 *conf, int64 *newval, void **extra,
+					  GucSource source, int elevel)
+{
+	/* Quick success if no hook */
+	if (!conf->check_hook)
+		return true;
+
+	/* Reset variables that might be set by hook */
+	GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+	GUC_check_errmsg_string = NULL;
+	GUC_check_errdetail_string = NULL;
+	GUC_check_errhint_string = NULL;
+
+	if (!(*conf->check_hook) (newval, extra, source))
+	{
+		ereport(elevel,
+				(errcode(GUC_check_errcode_value),
+				 GUC_check_errmsg_string ?
+				 errmsg_internal("%s", GUC_check_errmsg_string) :
+				 errmsg("invalid value for parameter \"%s\": %lld",
+						conf->gen.name, (long long) *newval),
+				 GUC_check_errdetail_string ?
+				 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
+				 GUC_check_errhint_string ?
+				 errhint("%s", GUC_check_errhint_string) : 0));
+		/* Flush any strings created in ErrorContext */
+		FlushErrorState();
+		return false;
+	}
+
+	return true;
+}
+
 static bool
 call_real_check_hook(struct config_real *conf, double *newval, void **extra,
 					 GucSource source, int elevel)
diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c
index 9c9edd3d2f..d0625d18b3 100644
--- a/src/backend/utils/misc/guc_funcs.c
+++ b/src/backend/utils/misc/guc_funcs.c
@@ -673,6 +673,31 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			}
 			break;
 
+		case PGC_INT64:
+			{
+				struct config_int64 *lconf = (struct config_int64 *) conf;
+
+				/* min_val */
+				snprintf(buffer, sizeof(buffer), "%lld", (long long) lconf->min);
+				values[9] = pstrdup(buffer);
+
+				/* max_val */
+				snprintf(buffer, sizeof(buffer), "%lld", (long long) lconf->max);
+				values[10] = pstrdup(buffer);
+
+				/* enumvals */
+				values[11] = NULL;
+
+				/* boot_val */
+				snprintf(buffer, sizeof(buffer), "%lld", (long long) lconf->boot_val);
+				values[12] = pstrdup(buffer);
+
+				/* reset_val */
+				snprintf(buffer, sizeof(buffer), "%lld", (long long) lconf->reset_val);
+				values[13] = pstrdup(buffer);
+			}
+			break;
+
 		case PGC_REAL:
 			{
 				struct config_real *lconf = (struct config_real *) conf;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 46c258be28..635b72213a 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -731,6 +731,7 @@ const char *const config_type_names[] =
 {
 	[PGC_BOOL] = "bool",
 	[PGC_INT] = "integer",
+	[PGC_INT64] = "int64",
 	[PGC_REAL] = "real",
 	[PGC_STRING] = "string",
 	[PGC_ENUM] = "enum",
@@ -3654,6 +3655,15 @@ struct config_int ConfigureNamesInt[] =
 };
 
 
+struct config_int64 ConfigureNamesInt64[] =
+{
+	/* End-of-list marker */
+	{
+		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
+	}
+};
+
+
 struct config_real ConfigureNamesReal[] =
 {
 	{
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 81829b8270..1c175a941f 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -30,6 +30,7 @@ typedef enum relopt_type
 {
 	RELOPT_TYPE_BOOL,
 	RELOPT_TYPE_INT,
+	RELOPT_TYPE_INT64,
 	RELOPT_TYPE_REAL,
 	RELOPT_TYPE_ENUM,
 	RELOPT_TYPE_STRING,
@@ -81,6 +82,7 @@ typedef struct relopt_value
 	{
 		bool		bool_val;
 		int			int_val;
+		int64		int64_val;
 		double		real_val;
 		int			enum_val;
 		char	   *string_val; /* allocated separately */
@@ -102,6 +104,14 @@ typedef struct relopt_int
 	int			max;
 } relopt_int;
 
+typedef struct relopt_int64
+{
+	relopt_gen	gen;
+	int64		default_val;
+	int64		min;
+	int64		max;
+}			relopt_int64;
+
 typedef struct relopt_real
 {
 	relopt_gen	gen;
@@ -185,6 +195,9 @@ extern void add_bool_reloption(bits32 kinds, const char *name, const char *desc,
 extern void add_int_reloption(bits32 kinds, const char *name, const char *desc,
 							  int default_val, int min_val, int max_val,
 							  LOCKMODE lockmode);
+extern void add_int64_reloption(bits32 kinds, const char *name, char *desc,
+								int64 default_val, int64 min_val, int64 max_val,
+								LOCKMODE lockmode);
 extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
 							   double default_val, double min_val, double max_val,
 							   LOCKMODE lockmode);
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ff506bf48d..9cbaddca32 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -178,12 +178,14 @@ struct config_enum_entry
  */
 typedef bool (*GucBoolCheckHook) (bool *newval, void **extra, GucSource source);
 typedef bool (*GucIntCheckHook) (int *newval, void **extra, GucSource source);
+typedef bool (*GucInt64CheckHook) (int64 *newval, void **extra, GucSource source);
 typedef bool (*GucRealCheckHook) (double *newval, void **extra, GucSource source);
 typedef bool (*GucStringCheckHook) (char **newval, void **extra, GucSource source);
 typedef bool (*GucEnumCheckHook) (int *newval, void **extra, GucSource source);
 
 typedef void (*GucBoolAssignHook) (bool newval, void *extra);
 typedef void (*GucIntAssignHook) (int newval, void *extra);
+typedef void (*GucInt64AssignHook) (int64 newval, void *extra);
 typedef void (*GucRealAssignHook) (double newval, void *extra);
 typedef void (*GucStringAssignHook) (const char *newval, void *extra);
 typedef void (*GucEnumAssignHook) (int newval, void *extra);
@@ -319,6 +321,19 @@ extern void DefineCustomIntVariable(const char *name,
 									GucIntAssignHook assign_hook,
 									GucShowHook show_hook) pg_attribute_nonnull(1, 4);
 
+extern void DefineCustomInt64Variable(const char *name,
+									  const char *short_desc,
+									  const char *long_desc,
+									  int64 *valueAddr,
+									  int64 bootValue,
+									  int64 minValue,
+									  int64 maxValue,
+									  GucContext context,
+									  int flags,
+									  GucInt64CheckHook check_hook,
+									  GucInt64AssignHook assign_hook,
+									  GucShowHook show_hook);
+
 extern void DefineCustomRealVariable(const char *name,
 									 const char *short_desc,
 									 const char *long_desc,
@@ -380,6 +395,8 @@ extern void ParseLongOption(const char *string, char **name, char **value);
 extern const char *get_config_unit_name(int flags);
 extern bool parse_int(const char *value, int *result, int flags,
 					  const char **hintmsg);
+extern bool parse_int64(const char *value, int64 *result, int flags,
+						const char **hintmsg);
 extern bool parse_real(const char *value, double *result, int flags,
 					   const char **hintmsg);
 extern int	set_config_option(const char *name, const char *value,
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 0c0277c423..c4dfe0afab 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -24,6 +24,7 @@ enum config_type
 {
 	PGC_BOOL,
 	PGC_INT,
+	PGC_INT64,
 	PGC_REAL,
 	PGC_STRING,
 	PGC_ENUM,
@@ -33,6 +34,7 @@ union config_var_val
 {
 	bool		boolval;
 	int			intval;
+	int64		int64val;
 	double		realval;
 	char	   *stringval;
 	int			enumval;
@@ -225,6 +227,22 @@ struct config_int
 	void	   *reset_extra;
 };
 
+struct config_int64
+{
+	struct config_generic gen;
+	/* constant fields, must be set correctly in initial value: */
+	int64	   *variable;
+	int64		boot_val;
+	int64		min;
+	int64		max;
+	GucInt64CheckHook check_hook;
+	GucInt64AssignHook assign_hook;
+	GucShowHook show_hook;
+	/* variable fields, initialized at runtime: */
+	int64		reset_val;
+	void	   *reset_extra;
+};
+
 struct config_real
 {
 	struct config_generic gen;
@@ -289,6 +307,7 @@ extern PGDLLIMPORT const char *const GucSource_Names[];
 /* data arrays defining all the built-in GUC variables */
 extern PGDLLIMPORT struct config_bool ConfigureNamesBool[];
 extern PGDLLIMPORT struct config_int ConfigureNamesInt[];
+extern PGDLLIMPORT struct config_int64 ConfigureNamesInt64[];
 extern PGDLLIMPORT struct config_real ConfigureNamesReal[];
 extern PGDLLIMPORT struct config_string ConfigureNamesString[];
 extern PGDLLIMPORT struct config_enum ConfigureNamesEnum[];
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8700204953..442a11c329 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -312,13 +312,13 @@ typedef struct AutoVacOpts
 	int			vacuum_ins_threshold;
 	int			analyze_threshold;
 	int			vacuum_cost_limit;
-	int			freeze_min_age;
-	int			freeze_max_age;
-	int			freeze_table_age;
-	int			multixact_freeze_min_age;
-	int			multixact_freeze_max_age;
-	int			multixact_freeze_table_age;
-	int			log_min_duration;
+	int64		freeze_min_age;
+	int64		freeze_max_age;
+	int64		freeze_table_age;
+	int64		multixact_freeze_min_age;
+	int64		multixact_freeze_max_age;
+	int64		multixact_freeze_table_age;
+	int64		log_min_duration;
 	float8		vacuum_cost_delay;
 	float8		vacuum_scale_factor;
 	float8		vacuum_ins_scale_factor;
-- 
2.45.2

