*** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 95,111 **** #define KB_PER_MB (1024) #define KB_PER_GB (1024*1024) #define MS_PER_S 1000 - #define S_PER_MIN 60 #define MS_PER_MIN (1000 * 60) - #define MIN_PER_H 60 - #define S_PER_H (60 * 60) #define MS_PER_H (1000 * 60 * 60) - #define MIN_PER_D (60 * 24) - #define S_PER_D (60 * 60 * 24) #define MS_PER_D (1000 * 60 * 60 * 24) /* XXX these should appear in other modules' header files */ extern bool Log_disconnections; extern int CommitDelay; --- 95,106 ---- *************** parse_bool(const char *value, bool *resu *** 4115,4124 **** --- 4110,4215 ---- } return true; } + #if BLCKSZ < 1024 || XLOG_BLCKSZ < 1024 + #error BLCKSZ or XLOG_BLCKSZ less than 1K is not supported by GUC units code. + #endif + + /* + * Accepted unit abbrevations. + * + * Units are represented in terms of base unit. + * + * Base unit for memory is 1 kbyte. + * Base unit for time is 1 ms. + * + * If several units of same type share common prefix, longer + * must come first. Does not apply if different types. + */ + static const GucUnit unit_list [] = { + /* memory units */ + { "kb", GUC_UNIT_MEMORY, 1 }, + { "mb", GUC_UNIT_MEMORY, KB_PER_MB }, + { "gb", GUC_UNIT_MEMORY, KB_PER_GB }, + /* time units */ + { "ms", GUC_UNIT_TIME, 1 }, + { "s", GUC_UNIT_TIME, MS_PER_S }, + { "min", GUC_UNIT_TIME, MS_PER_MIN }, + { "h", GUC_UNIT_TIME, MS_PER_H }, + { "d", GUC_UNIT_TIME, MS_PER_D }, + /* end marker */ + { NULL }, + }; + + /* hints when unit parsing fails. keep them near unit list */ + static const char *hintmsg_time = gettext_noop("Valid units for this parameter are \"kB\", \"MB\", and \"GB\"."); + static const char *hintmsg_mem = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\"."); + + /* returns guc_unit struct or NULL for error */ + static const GucUnit * + lookup_unit(const char *s, int flags) + { + const GucUnit *unit; + for (unit = unit_list; unit->abbr; unit++) + { + if ((flags & unit->typemask) == 0) + continue; + if (pg_strncasecmp(s, unit->abbr, strlen(unit->abbr)) == 0) + return unit; + } + return NULL; + } + + /* returns final value */ + static int64 + apply_unit(const GucUnit *unit, int flags, int64 val, char **errstr) + { + /* divider to get real units from base units */ + int div; + /* final result */ + int64 res; + + switch (flags & unit->typemask) + { + /* memory values */ + case GUC_UNIT_KB: + div = 1; + break; + case GUC_UNIT_BLOCKS: + div = BLCKSZ / 1024; + break; + case GUC_UNIT_XBLOCKS: + div = XLOG_BLCKSZ / 1024; + break; + /* time values */ + case GUC_UNIT_MS: + div = 1; + break; + case GUC_UNIT_S: + div = MS_PER_S; + break; + case GUC_UNIT_MIN: + div = MS_PER_MIN; + break; + default: + /* can happen only if new GUC_UNIT_* is introduced */ + *errstr = gettext_noop("BUG: apply_unit not updated"); + return val; + } + + res = val * unit->multiplier / div; + + #ifdef INT64_IS_BUSTED + /* check for overflow */ + if (res < (val / div) * unit->multiplier) + *errstr = gettext_noop("unit overflow"); + #endif + + return res; + } /* * Try to parse value as an integer. The accepted formats are the * usual decimal, octal, or hexadecimal formats, optionally followed by * a unit name if "flags" indicates a unit is allowed. *************** parse_bool(const char *value, bool *resu *** 4131,4140 **** --- 4222,4233 ---- bool parse_int(const char *value, int *result, int flags, const char **hintmsg) { int64 val; char *endptr; + char *errmsg = NULL; + const GucUnit *unit; /* To suppress compiler warnings, always set output params */ if (result) *result = 0; if (hintmsg) *************** parse_int(const char *value, int *result *** 4159,4311 **** endptr++; /* Handle possible unit */ if (*endptr != '\0') { ! /* ! * Note: the multiple-switch coding technique here is a bit tedious, ! * but seems necessary to avoid intermediate-value overflows. ! * ! * If INT64_IS_BUSTED (ie, it's really int32) we will fail to detect ! * overflow due to units conversion, but there are few enough such ! * machines that it does not seem worth trying to be smarter. ! */ ! if (flags & GUC_UNIT_MEMORY) { ! /* Set hint for use if no match or trailing garbage */ ! if (hintmsg) ! *hintmsg = gettext_noop("Valid units for this parameter are \"kB\", \"MB\", and \"GB\"."); ! ! #if BLCKSZ < 1024 || BLCKSZ > (1024*1024) ! #error BLCKSZ must be between 1KB and 1MB ! #endif ! #if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024) ! #error XLOG_BLCKSZ must be between 1KB and 1MB ! #endif ! ! if (strncmp(endptr, "kB", 2) == 0) ! { ! endptr += 2; ! switch (flags & GUC_UNIT_MEMORY) ! { ! case GUC_UNIT_BLOCKS: ! val /= (BLCKSZ / 1024); ! break; ! case GUC_UNIT_XBLOCKS: ! val /= (XLOG_BLCKSZ / 1024); ! break; ! } ! } ! else if (strncmp(endptr, "MB", 2) == 0) ! { ! endptr += 2; ! switch (flags & GUC_UNIT_MEMORY) ! { ! case GUC_UNIT_KB: ! val *= KB_PER_MB; ! break; ! case GUC_UNIT_BLOCKS: ! val *= KB_PER_MB / (BLCKSZ / 1024); ! break; ! case GUC_UNIT_XBLOCKS: ! val *= KB_PER_MB / (XLOG_BLCKSZ / 1024); ! break; ! } ! } ! else if (strncmp(endptr, "GB", 2) == 0) ! { ! endptr += 2; ! switch (flags & GUC_UNIT_MEMORY) ! { ! case GUC_UNIT_KB: ! val *= KB_PER_GB; ! break; ! case GUC_UNIT_BLOCKS: ! val *= KB_PER_GB / (BLCKSZ / 1024); ! break; ! case GUC_UNIT_XBLOCKS: ! val *= KB_PER_GB / (XLOG_BLCKSZ / 1024); ! break; ! } ! } } - else if (flags & GUC_UNIT_TIME) - { - /* Set hint for use if no match or trailing garbage */ - if (hintmsg) - *hintmsg = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\"."); ! if (strncmp(endptr, "ms", 2) == 0) ! { ! endptr += 2; ! switch (flags & GUC_UNIT_TIME) ! { ! case GUC_UNIT_S: ! val /= MS_PER_S; ! break; ! case GUC_UNIT_MIN: ! val /= MS_PER_MIN; ! break; ! } ! } ! else if (strncmp(endptr, "s", 1) == 0) ! { ! endptr += 1; ! switch (flags & GUC_UNIT_TIME) ! { ! case GUC_UNIT_MS: ! val *= MS_PER_S; ! break; ! case GUC_UNIT_MIN: ! val /= S_PER_MIN; ! break; ! } ! } ! else if (strncmp(endptr, "min", 3) == 0) ! { ! endptr += 3; ! switch (flags & GUC_UNIT_TIME) ! { ! case GUC_UNIT_MS: ! val *= MS_PER_MIN; ! break; ! case GUC_UNIT_S: ! val *= S_PER_MIN; ! break; ! } ! } ! else if (strncmp(endptr, "h", 1) == 0) ! { ! endptr += 1; ! switch (flags & GUC_UNIT_TIME) ! { ! case GUC_UNIT_MS: ! val *= MS_PER_H; ! break; ! case GUC_UNIT_S: ! val *= S_PER_H; ! break; ! case GUC_UNIT_MIN: ! val *= MIN_PER_H; ! break; ! } ! } ! else if (strncmp(endptr, "d", 1) == 0) { ! endptr += 1; ! switch (flags & GUC_UNIT_TIME) ! { ! case GUC_UNIT_MS: ! val *= MS_PER_D; ! break; ! case GUC_UNIT_S: ! val *= S_PER_D; ! break; ! case GUC_UNIT_MIN: ! val *= MIN_PER_D; ! break; ! } } } /* allow whitespace after unit */ while (isspace((unsigned char) *endptr)) --- 4252,4281 ---- endptr++; /* Handle possible unit */ if (*endptr != '\0') { ! /* prepare hint msg */ ! if (hintmsg) { ! if (flags & GUC_UNIT_MEMORY) ! *hintmsg = hintmsg_mem; ! else if (flags & GUC_UNIT_TIME) ! *hintmsg = hintmsg_time; } ! /* apply unit */ ! unit = lookup_unit(endptr, flags); ! if (unit) ! { ! endptr += strlen(unit->abbr); ! val = apply_unit(unit, flags, val, &errmsg); ! if (errmsg) { ! if (hintmsg) ! *hintmsg = errmsg; ! return false; } } /* allow whitespace after unit */ while (isspace((unsigned char) *endptr)) *** a/src/include/utils/guc_tables.h --- b/src/include/utils/guc_tables.h *************** struct config_string *** 210,219 **** --- 210,232 ---- GucShowHook show_hook; /* variable fields, initialized at runtime: */ char *reset_val; }; + /* + * Units are represented in terms of base unit. + * Base unit for memory is 1 kbyte. + * Base unit for time is 1 ms. + * + * typemask is either GUC_UNIT_MEMORY or GUC_UNIT_TIME. + */ + typedef struct guc_unit { + const char *abbr; + int typemask; + unsigned int multiplier; + } GucUnit; + struct config_enum { struct config_generic gen; /* constant fields, must be set correctly in initial value: */ int *variable;