diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c new file mode 100644 index ec10762..98e25d6 *** a/src/backend/access/common/reloptions.c --- b/src/backend/access/common/reloptions.c *************** static relopt_int intRelOpts[] = *** 334,339 **** --- 334,347 ---- {{NULL}} }; + + static relopt_int64 int64RelOpts[] = + { + /* list terminator */ + {{NULL}} + }; + + static relopt_real realRelOpts[] = { { *************** initialize_reloptions(void) *** 460,465 **** --- 468,479 ---- 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, *************** initialize_reloptions(void) *** 496,501 **** --- 510,523 ---- 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; *************** allocate_reloption(bits32 kinds, int typ *** 598,603 **** --- 620,628 ---- 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; *************** add_int_reloption(bits32 kinds, char *na *** 661,666 **** --- 686,710 ---- } /* + * 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) + { + relopt_int64 *newoption; + + newoption = (relopt_int64 *) allocate_reloption(kinds, RELOPT_TYPE_INT64, + name, desc); + 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 */ *************** parse_one_reloption(relopt_value *option *** 1161,1166 **** --- 1205,1231 ---- optint->min, optint->max))); } break; + case RELOPT_TYPE_INT64: + { + relopt_int64 *optint = (relopt_int64 *) option->gen; + + parsed = parse_int64(value, &option->values.int64_val, 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 \"" INT64_FORMAT + "\" and \"" INT64_FORMAT "\".", + optint->min, optint->max))); + } + break; case RELOPT_TYPE_REAL: { relopt_real *optreal = (relopt_real *) option->gen; *************** fillRelOptions(void *rdopts, Size basesi *** 1269,1274 **** --- 1334,1344 ---- 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 new file mode 100644 index bc9f09a..1226f67 *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** static bool call_bool_check_hook(struct *** 140,145 **** --- 140,147 ---- 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, *************** const char *const config_type_names[] = *** 675,680 **** --- 677,683 ---- { /* PGC_BOOL */ "bool", /* PGC_INT */ "integer", + /* PGC_INT64 */ "int64", /* PGC_REAL */ "real", /* PGC_STRING */ "string", /* PGC_ENUM */ "enum" *************** static struct config_real ConfigureNames *** 3054,3059 **** --- 3057,3071 ---- }; + 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[] = { { *************** extra_field_used(struct config_generic * *** 4089,4094 **** --- 4101,4110 ---- 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; *************** set_stack_value(struct config_generic *g *** 4150,4155 **** --- 4166,4175 ---- 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; *************** discard_stack_value(struct config_generi *** 4178,4183 **** --- 4198,4204 ---- { case PGC_BOOL: case PGC_INT: + case PGC_INT64: case PGC_REAL: case PGC_ENUM: /* no need to do anything */ *************** build_guc_variables(void) *** 4232,4237 **** --- 4253,4266 ---- 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]; *************** build_guc_variables(void) *** 4272,4277 **** --- 4301,4309 ---- 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; *************** InitializeOneGUCOption(struct config_gen *** 4626,4631 **** --- 4658,4681 ---- 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 " INT64_FORMAT, + conf->gen.name, 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; *************** ResetAllOptions(void) *** 4921,4926 **** --- 4971,4988 ---- 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); + *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); + break; + } case PGC_REAL: { struct config_real *conf = (struct config_real *) gconf; *************** AtEOXact_GUC(bool isCommit, int nestLeve *** 5267,5272 **** --- 5329,5352 ---- } 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) + { + 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_REAL: { struct config_real *conf = (struct config_real *) gconf; *************** parse_int(const char *value, int *result *** 5587,5592 **** --- 5667,5720 ---- /* + * 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, 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; + #ifdef _MSC_VER /* MSVC only */ + val = _strtoi64(value, &endptr, 10); + #elif defined(HAVE_STRTOLL) && SIZEOF_LONG < 8 + val = strtoll(value, &endptr, 10); + #else + val = strtol(value, &endptr, 10); + #endif + + 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; + } + + if (result) + *result = val; + return true; + } + + + + /* * Try to parse value as a floating point number in the usual format. * If the string parses okay, return true, else false. * If okay and result is not NULL, return the value in *result. *************** parse_and_validate_value(struct config_g *** 5789,5794 **** --- 5917,5953 ---- return false; } break; + case PGC_INT64: + { + struct config_int64 *conf = (struct config_int64 *) record; + const char *hintmsg; + + if (!parse_int64(value, &newval->int64val, &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(INT64_FORMAT " is outside the valid range for parameter \"%s\" (" + INT64_FORMAT " .. " INT64_FORMAT ")", + newval->int64val, name, + conf->min, 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; *************** set_config_option(const char *name, cons *** 6342,6347 **** --- 6501,6596 ---- #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 + } + case PGC_REAL: { struct config_real *conf = (struct config_real *) record; *************** GetConfigOption(const char *name, bool m *** 6738,6743 **** --- 6987,6997 ---- *((struct config_int *) record)->variable); return buffer; + case PGC_INT64: + snprintf(buffer, sizeof(buffer), INT64_FORMAT, + *((struct config_int64 *) record)->variable); + return buffer; + case PGC_REAL: snprintf(buffer, sizeof(buffer), "%g", *((struct config_real *) record)->variable); *************** GetConfigOptionResetString(const char *n *** 6788,6793 **** --- 7042,7052 ---- ((struct config_int *) record)->reset_val); return buffer; + case PGC_INT64: + snprintf(buffer, sizeof(buffer), INT64_FORMAT, + ((struct config_int64 *) record)->reset_val); + return buffer; + case PGC_REAL: snprintf(buffer, sizeof(buffer), "%g", ((struct config_real *) record)->reset_val); *************** DefineCustomIntVariable(const char *name *** 7794,7799 **** --- 8053,8088 ---- } 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, const char *long_desc, *************** GetConfigOptionByNum(int varnum, const c *** 8220,8225 **** --- 8509,8539 ---- } break; + case PGC_INT64: + { + struct config_int64 *lconf = (struct config_int64 *) conf; + + /* min_val */ + snprintf(buffer, sizeof(buffer), INT64_FORMAT, lconf->min); + values[9] = pstrdup(buffer); + + /* max_val */ + snprintf(buffer, sizeof(buffer), INT64_FORMAT, lconf->max); + values[10] = pstrdup(buffer); + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + snprintf(buffer, sizeof(buffer), INT64_FORMAT, lconf->boot_val); + values[12] = pstrdup(buffer); + + /* reset_val */ + snprintf(buffer, sizeof(buffer), INT64_FORMAT, lconf->reset_val); + values[13] = pstrdup(buffer); + } + break; + case PGC_REAL: { struct config_real *lconf = (struct config_real *) conf; *************** _ShowOption(struct config_generic *recor *** 8693,8698 **** --- 9007,9027 ---- } 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), INT64_FORMAT, + *conf->variable); + val = buffer; + } + } + break; + case PGC_REAL: { struct config_real *conf = (struct config_real *) record; *************** write_one_nondefault_variable(FILE *fp, *** 8785,8790 **** --- 9114,9127 ---- } break; + case PGC_INT64: + { + struct config_int64 *conf = (struct config_int64 *) gconf; + + fprintf(fp, INT64_FORMAT, *conf->variable); + } + break; + case PGC_REAL: { struct config_real *conf = (struct config_real *) gconf; *************** estimate_variable_size(struct config_gen *** 9042,9047 **** --- 9379,9402 ---- } 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: { /* *************** serialize_variable(char **destptr, Size *** 9203,9208 **** --- 9558,9571 ---- } break; + case PGC_INT64: + { + struct config_int64 *conf = (struct config_int64 *) gconf; + + do_serialize(destptr, maxbytes, INT64_FORMAT, *conf->variable); + } + break; + case PGC_REAL: { struct config_real *conf = (struct config_real *) gconf; *************** call_int_check_hook(struct config_int *c *** 9866,9871 **** --- 10229,10268 ---- } 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\": " INT64_FORMAT, + conf->gen.name, *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 new file mode 100644 index 5cdaa3b..286b39c *** a/src/include/access/reloptions.h --- b/src/include/access/reloptions.h *************** typedef enum relopt_type *** 30,35 **** --- 30,36 ---- { RELOPT_TYPE_BOOL, RELOPT_TYPE_INT, + RELOPT_TYPE_INT64, RELOPT_TYPE_REAL, RELOPT_TYPE_STRING } relopt_type; *************** typedef struct relopt_value *** 79,84 **** --- 80,86 ---- { bool bool_val; int int_val; + int64 int64_val; double real_val; char *string_val; /* allocated separately */ } values; *************** typedef struct relopt_int *** 99,104 **** --- 101,114 ---- 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; *************** extern void add_bool_reloption(bits32 ki *** 250,255 **** --- 260,267 ---- bool default_val); extern void add_int_reloption(bits32 kinds, char *name, char *desc, int default_val, int min_val, int max_val); + extern void add_int64_reloption(bits32 kinds, char *name, char *desc, + int64 default_val, int64 min_val, int64 max_val); extern void add_real_reloption(bits32 kinds, char *name, char *desc, double default_val, double min_val, double max_val); extern void add_string_reloption(bits32 kinds, char *name, char *desc, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h new file mode 100644 index 467125a..13956d2 *** a/src/include/utils/guc.h --- b/src/include/utils/guc.h *************** struct config_enum_entry *** 173,184 **** --- 173,186 ---- */ 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); *************** extern void DefineCustomIntVariable( *** 304,309 **** --- 306,325 ---- 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, *************** extern void BeginReportingGUCOptions(voi *** 359,364 **** --- 375,381 ---- 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, const char **hintmsg); extern bool parse_real(const char *value, double *result); extern int set_config_option(const char *name, const char *value, GucContext context, GucSource source, diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h new file mode 100644 index 042f7a0..469f83a *** a/src/include/utils/guc_tables.h --- b/src/include/utils/guc_tables.h *************** enum config_type *** 23,28 **** --- 23,29 ---- { PGC_BOOL, PGC_INT, + PGC_INT64, PGC_REAL, PGC_STRING, PGC_ENUM *************** union config_var_val *** 32,37 **** --- 33,39 ---- { bool boolval; int intval; + int64 int64val; double realval; char *stringval; int enumval; *************** struct config_int *** 204,209 **** --- 206,227 ---- 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;