In general, enclose the value in single quotes, doubling any single
quotes within the value. Quotes can usually be omitted if the value
is a simple number or identifier, however.
+ (Values that match a SQL keyword require quoting in some contexts.)
</para>
</listitem>
<listitem>
<para>
<emphasis>Numeric (integer and floating point):</emphasis>
- A decimal point is permitted only for floating-point parameters.
- Do not use thousands separators. Quotes are not required.
+ Numeric parameters can be specified in the customary integer and
+ floating-point formats; fractional values are rounded to the nearest
+ integer if the parameter is of integer type. Integer parameters
+ additionally accept hexadecimal input (beginning
+ with <literal>0x</literal>) and octal input (beginning
+ with <literal>0</literal>), but these formats cannot have a fraction.
+ Do not use thousands separators.
+ Quotes are not required, except for hexadecimal input.
</para>
</listitem>
</para>
</listitem>
</itemizedlist>
+
+ If a fractional value is specified with a unit, it will be rounded
+ to a multiple of the next smaller unit if there is one.
+ For example, <literal>30.1 GB</literal> will be converted
+ to <literal>30822 MB</literal> not <literal>32319628902 B</literal>.
+ If the parameter is of integer type, a final rounding to integer
+ occurs after any units conversion.
</para>
</listitem>
if (base_unit == table[i].base_unit &&
strcmp(unitstr, table[i].unit) == 0)
{
- *base_value = value * table[i].multiplier;
+ double cvalue = value * table[i].multiplier;
+
+ /*
+ * If the user gave a fractional value such as "30.1GB", round it
+ * off to the nearest multiple of the next smaller unit, if there
+ * is one.
+ */
+ if (*table[i + 1].unit &&
+ base_unit == table[i + 1].base_unit)
+ cvalue = rint(cvalue / table[i + 1].multiplier) *
+ table[i + 1].multiplier;
+
+ *base_value = cvalue;
return true;
}
}
/*
* 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.
+ * usual decimal, octal, or hexadecimal formats, as well as floating-point
+ * formats (which will be rounded to integer after any units conversion).
+ * Optionally, the value can be followed by a unit name if "flags" indicates
+ * a unit is allowed.
*
* If the string parses okay, return true, else false.
* If okay and result is not NULL, return the value in *result.
bool
parse_int(const char *value, int *result, int flags, const char **hintmsg)
{
- int64 val;
+ /*
+ * We assume here that double is wide enough to represent any integer
+ * value with adequate precision.
+ */
+ double val;
char *endptr;
/* To suppress compiler warnings, always set output params */
if (hintmsg)
*hintmsg = NULL;
- /* We assume here that int64 is at least as wide as long */
+ /*
+ * Try to parse as an integer (allowing octal or hex input). If the
+ * conversion stops at a decimal point or 'e', or overflows, re-parse as
+ * float. This should work fine as long as we have no unit names starting
+ * with 'e'. If we ever do, the test could be extended to check for a
+ * sign or digit after 'e', but for now that's unnecessary.
+ */
errno = 0;
val = strtol(value, &endptr, 0);
-
- if (endptr == value)
- return false; /* no HINT for integer syntax error */
-
- if (errno == ERANGE || val != (int64) ((int32) val))
+ if (*endptr == '.' || *endptr == 'e' || *endptr == 'E' ||
+ errno == ERANGE)
{
- if (hintmsg)
- *hintmsg = gettext_noop("Value exceeds integer range.");
- return false;
+ errno = 0;
+ val = strtod(value, &endptr);
}
- /* allow whitespace between integer and unit */
+ if (endptr == value || errno == ERANGE)
+ return false; /* no HINT for these cases */
+
+ /* reject NaN (infinities will fail range check below) */
+ if (isnan(val))
+ return false; /* treat same as syntax error; no HINT */
+
+ /* allow whitespace between number and unit */
while (isspace((unsigned char) *endptr))
endptr++;
/* Handle possible unit */
if (*endptr != '\0')
{
- double cval;
-
if ((flags & GUC_UNIT) == 0)
return false; /* this setting does not accept a unit */
- if (!convert_to_base_unit((double) val,
+ if (!convert_to_base_unit(val,
endptr, (flags & GUC_UNIT),
- &cval))
+ &val))
{
/* invalid unit, or garbage after the unit; set hint and fail. */
if (hintmsg)
}
return false;
}
+ }
- /* Round to int, then check for overflow due to units conversion */
- cval = rint(cval);
- if (cval > INT_MAX || cval < INT_MIN)
- {
- if (hintmsg)
- *hintmsg = gettext_noop("Value exceeds integer range.");
- return false;
- }
- val = (int64) cval;
+ /* Round to int, then check for overflow */
+ val = rint(val);
+
+ if (val > INT_MAX || val < INT_MIN)
+ {
+ if (hintmsg)
+ *hintmsg = gettext_noop("Value exceeds integer range.");
+ return false;
}
if (result)
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
+ * a unit is allowed.
*
* If the string parses okay, return true, else false.
* If okay and result is not NULL, return the value in *result.