@@ -5995,7 +5995,19 @@ convert_to_base_unit(double value, const char *unit,
5995
5995
if (base_unit == table [i ].base_unit &&
5996
5996
strcmp (unitstr , table [i ].unit ) == 0 )
5997
5997
{
5998
- * base_value = value * table [i ].multiplier ;
5998
+ double cvalue = value * table [i ].multiplier ;
5999
+
6000
+ /*
6001
+ * If the user gave a fractional value such as "30.1GB", round it
6002
+ * off to the nearest multiple of the next smaller unit, if there
6003
+ * is one.
6004
+ */
6005
+ if (* table [i + 1 ].unit &&
6006
+ base_unit == table [i + 1 ].base_unit )
6007
+ cvalue = rint (cvalue / table [i + 1 ].multiplier ) *
6008
+ table [i + 1 ].multiplier ;
6009
+
6010
+ * base_value = cvalue ;
5999
6011
return true;
6000
6012
}
6001
6013
}
@@ -6141,8 +6153,10 @@ get_config_unit_name(int flags)
6141
6153
6142
6154
/*
6143
6155
* Try to parse value as an integer. The accepted formats are the
6144
- * usual decimal, octal, or hexadecimal formats, optionally followed by
6145
- * a unit name if "flags" indicates a unit is allowed.
6156
+ * usual decimal, octal, or hexadecimal formats, as well as floating-point
6157
+ * formats (which will be rounded to integer after any units conversion).
6158
+ * Optionally, the value can be followed by a unit name if "flags" indicates
6159
+ * a unit is allowed.
6146
6160
*
6147
6161
* If the string parses okay, return true, else false.
6148
6162
* If okay and result is not NULL, return the value in *result.
@@ -6152,7 +6166,11 @@ get_config_unit_name(int flags)
6152
6166
bool
6153
6167
parse_int (const char * value , int * result , int flags , const char * * hintmsg )
6154
6168
{
6155
- int64 val ;
6169
+ /*
6170
+ * We assume here that double is wide enough to represent any integer
6171
+ * value with adequate precision.
6172
+ */
6173
+ double val ;
6156
6174
char * endptr ;
6157
6175
6158
6176
/* To suppress compiler warnings, always set output params */
@@ -6161,35 +6179,42 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg)
6161
6179
if (hintmsg )
6162
6180
* hintmsg = NULL ;
6163
6181
6164
- /* We assume here that int64 is at least as wide as long */
6182
+ /*
6183
+ * Try to parse as an integer (allowing octal or hex input). If the
6184
+ * conversion stops at a decimal point or 'e', or overflows, re-parse as
6185
+ * float. This should work fine as long as we have no unit names starting
6186
+ * with 'e'. If we ever do, the test could be extended to check for a
6187
+ * sign or digit after 'e', but for now that's unnecessary.
6188
+ */
6165
6189
errno = 0 ;
6166
6190
val = strtol (value , & endptr , 0 );
6167
-
6168
- if (endptr == value )
6169
- return false; /* no HINT for integer syntax error */
6170
-
6171
- if (errno == ERANGE || val != (int64 ) ((int32 ) val ))
6191
+ if (* endptr == '.' || * endptr == 'e' || * endptr == 'E' ||
6192
+ errno == ERANGE )
6172
6193
{
6173
- if (hintmsg )
6174
- * hintmsg = gettext_noop ("Value exceeds integer range." );
6175
- return false;
6194
+ errno = 0 ;
6195
+ val = strtod (value , & endptr );
6176
6196
}
6177
6197
6178
- /* allow whitespace between integer and unit */
6198
+ if (endptr == value || errno == ERANGE )
6199
+ return false; /* no HINT for these cases */
6200
+
6201
+ /* reject NaN (infinities will fail range check below) */
6202
+ if (isnan (val ))
6203
+ return false; /* treat same as syntax error; no HINT */
6204
+
6205
+ /* allow whitespace between number and unit */
6179
6206
while (isspace ((unsigned char ) * endptr ))
6180
6207
endptr ++ ;
6181
6208
6182
6209
/* Handle possible unit */
6183
6210
if (* endptr != '\0' )
6184
6211
{
6185
- double cval ;
6186
-
6187
6212
if ((flags & GUC_UNIT ) == 0 )
6188
6213
return false; /* this setting does not accept a unit */
6189
6214
6190
- if (!convert_to_base_unit (( double ) val ,
6215
+ if (!convert_to_base_unit (val ,
6191
6216
endptr , (flags & GUC_UNIT ),
6192
- & cval ))
6217
+ & val ))
6193
6218
{
6194
6219
/* invalid unit, or garbage after the unit; set hint and fail. */
6195
6220
if (hintmsg )
@@ -6201,27 +6226,27 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg)
6201
6226
}
6202
6227
return false;
6203
6228
}
6229
+ }
6204
6230
6205
- /* Round to int, then check for overflow due to units conversion */
6206
- cval = rint (cval );
6207
- if (cval > INT_MAX || cval < INT_MIN )
6208
- {
6209
- if (hintmsg )
6210
- * hintmsg = gettext_noop ("Value exceeds integer range." );
6211
- return false;
6212
- }
6213
- val = (int64 ) cval ;
6231
+ /* Round to int, then check for overflow */
6232
+ val = rint (val );
6233
+
6234
+ if (val > INT_MAX || val < INT_MIN )
6235
+ {
6236
+ if (hintmsg )
6237
+ * hintmsg = gettext_noop ("Value exceeds integer range." );
6238
+ return false;
6214
6239
}
6215
6240
6216
6241
if (result )
6217
6242
* result = (int ) val ;
6218
6243
return true;
6219
6244
}
6220
6245
6221
-
6222
-
6223
6246
/*
6224
6247
* Try to parse value as a floating point number in the usual format.
6248
+ * Optionally, the value can be followed by a unit name if "flags" indicates
6249
+ * a unit is allowed.
6225
6250
*
6226
6251
* If the string parses okay, return true, else false.
6227
6252
* If okay and result is not NULL, return the value in *result.
0 commit comments