Current GUC units code has 2 problems:

- It requires case-sensiteive representation of units ("kB").
  As the main point of units is to make configuring easier,
  requiring byte-exact typing makes them unnecessarily fragile.

- In attempt to preserve maximum range of values for INT64_IS_BUSTED
  systems, the code is written rather non-obvious way.

Attached patch replaces per-unit custom code with lookup table,
where each unit is represented as multiplier of base unit.
And it compares unit names case-insensitivaly.

It sacrifices some range on INT64_IS_BUSTED systems, but as the only promise
we offer them is that "Postgres may compile" I don't see it as problem.

In case people like the case-sensitivity, it can be restored and the patch
applied as plain cleanup.
*** 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;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to