From adce4788eb204ac8946e25f119565498c867f1bf 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 v31 6/7] Use 64-bit GUCs

Authors:
- Alexander Korotkov <aekorotkov@gmail.com>
- Teodor Sigaev <teodor@sigaev.ru>
- Nikita Glukhov <n.gluhov@postgrespro.ru>
- Maxim Orlov <orlovmg@gmail.com>
- Pavel Borisov <pashkin.elfe@gmail.com>
- Yura Sokolov <y.sokolov@postgrespro.ru> <funny.falcon@gmail.com>
Reviewed-by: 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 |  67 ++++
 src/backend/utils/misc/guc.c           | 425 +++++++++++++++++++++++++
 src/include/access/reloptions.h        |  13 +
 src/include/utils/guc.h                |  17 +
 src/include/utils/guc_tables.h         |  18 ++
 5 files changed, 540 insertions(+)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 599e160ca6..0799f4ce45 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -382,7 +382,12 @@ static relopt_int intRelOpts[] =
 		},
 		-1, 0, 1024
 	},
+	/* list terminator */
+	{{NULL}}
+};
 
+static relopt_int64 int64RelOpts[] =
+{
 	/* list terminator */
 	{{NULL}}
 };
@@ -597,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,
@@ -639,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;
@@ -794,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;
@@ -948,6 +970,25 @@ 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, 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
@@ -1622,6 +1663,27 @@ 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;
@@ -1777,6 +1839,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 :
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9e8ab1420d..f76512dbff 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -166,6 +166,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,
@@ -842,6 +844,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"
@@ -948,6 +951,8 @@ static const unit_conversion time_unit_conversion_table[] =
 	{""}						/* end of table marker */
 };
 
+static const char *int64_unit_overflow_hint = gettext_noop("Int64 value with units should be positive number < 2^53");
+
 /*
  * Contents of GUC tables
  *
@@ -3906,6 +3911,15 @@ static struct config_real ConfigureNamesReal[] =
 };
 
 
+static struct config_int64 ConfigureNamesInt64[] =
+{
+	/* End-of-list marker */
+	{
+		{NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL, NULL
+	}
+};
+
+
 static struct config_string ConfigureNamesString[] =
 {
 	{
@@ -5237,6 +5251,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;
@@ -5298,6 +5316,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;
@@ -5326,6 +5348,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 */
@@ -5380,6 +5403,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];
@@ -5420,6 +5451,9 @@ build_guc_variables(void)
 	for (i = 0; ConfigureNamesInt[i].gen.name; i++)
 		guc_vars[num_vars++] = &ConfigureNamesInt[i].gen;
 
+	for (i = 0; ConfigureNamesInt64[i].gen.name; i++)
+		guc_vars[num_vars++] = &ConfigureNamesInt64[i].gen;
+
 	for (i = 0; ConfigureNamesReal[i].gen.name; i++)
 		guc_vars[num_vars++] = &ConfigureNamesReal[i].gen;
 
@@ -5871,6 +5905,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;
@@ -6158,6 +6210,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);
@@ -6504,6 +6568,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)
 							{
@@ -7032,6 +7114,73 @@ 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 = int64_unit_overflow_hint;
+			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
@@ -7273,6 +7422,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;
@@ -7839,6 +8018,96 @@ set_config_option(const char *name, const char *value,
 					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))
+					free(newextra);
+				break;
+
 #undef newval
 			}
 
@@ -8254,6 +8523,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);
@@ -8301,6 +8575,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);
@@ -9342,6 +9621,36 @@ DefineCustomIntVariable(const char *name,
 	define_custom_variable(&var->gen);
 }
 
+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)
+{
+	struct config_int64 *var;
+
+	var = (struct config_int64 *)
+		init_custom_variable(name, short_desc, long_desc, context, flags,
+							 PGC_INT64, sizeof(struct config_int64));
+	var->variable = valueAddr;
+	var->boot_val = bootValue;
+	var->reset_val = bootValue;
+	var->min = minValue;
+	var->max = maxValue;
+	var->check_hook = check_hook;
+	var->assign_hook = assign_hook;
+	var->show_hook = show_hook;
+	define_custom_variable(&var->gen);
+}
+
 void
 DefineCustomRealVariable(const char *name,
 						 const char *short_desc,
@@ -9879,6 +10188,31 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow)
 			}
 			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;
@@ -10308,6 +10642,21 @@ _ShowOption(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;
@@ -10410,6 +10759,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;
@@ -10681,6 +11038,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.
+				 */
+				if (Abs(*conf->variable) < 1000)
+					valsize = 3 + 1;
+				else
+					valsize = 20 + 1;
+			}
+			break;
+
+
 		case PGC_REAL:
 			{
 				/*
@@ -10838,6 +11213,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;
@@ -11043,6 +11426,14 @@ RestoreGUCState(void *gucstate)
 				{
 					struct config_int *conf = (struct config_int *) gconf;
 
+					if (conf->reset_extra && conf->reset_extra != gconf->extra)
+						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)
 						free(conf->reset_extra);
 					break;
@@ -11617,6 +12008,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/include/access/reloptions.h b/src/include/access/reloptions.h
index f740513303..b3b9cd1922 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, 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 ea774968f0..210ee0bc55 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -173,12 +173,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);
@@ -318,6 +320,19 @@ extern void DefineCustomIntVariable(const char *name,
 									GucIntAssignHook assign_hook,
 									GucShowHook show_hook);
 
+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,
@@ -375,6 +390,8 @@ extern void ReportChangedGUCOptions(void);
 extern void ParseLongOption(const char *string, char **name, char **value);
 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 f7e54a87b7..e59e2739ac 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -23,6 +23,7 @@ enum config_type
 {
 	PGC_BOOL,
 	PGC_INT,
+	PGC_INT64,
 	PGC_REAL,
 	PGC_STRING,
 	PGC_ENUM
@@ -32,6 +33,7 @@ union config_var_val
 {
 	bool		boolval;
 	int			intval;
+	int64		int64val;
 	double		realval;
 	char	   *stringval;
 	int			enumval;
@@ -202,6 +204,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;
-- 
2.35.1

