mirror of
https://github.com/postgres/postgres.git
synced 2025-12-22 17:42:17 +03:00
Allow underscores in integer and numeric constants.
This allows underscores to be used in integer and numeric literals,
and their corresponding type input functions, for visual grouping.
For example:
1_500_000_000
3.14159_26535_89793
0xffff_ffff
0b_1001_0001
A single underscore is allowed between any 2 digits, or immediately
after the base prefix indicator of non-decimal integers, per SQL:202x
draft.
Peter Eisentraut and Dean Rasheed
Discussion: https://postgr.es/m/84aae844-dc55-a4be-86d9-4f0fa405cc97%40enterprisedb.com
This commit is contained in:
@@ -6968,10 +6968,7 @@ set_var_from_str(const char *str, const char *cp,
|
||||
}
|
||||
|
||||
if (!isdigit((unsigned char) *cp))
|
||||
ereturn(escontext, false,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||
"numeric", str)));
|
||||
goto invalid_syntax;
|
||||
|
||||
decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
|
||||
|
||||
@@ -6992,12 +6989,19 @@ set_var_from_str(const char *str, const char *cp,
|
||||
else if (*cp == '.')
|
||||
{
|
||||
if (have_dp)
|
||||
ereturn(escontext, false,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||
"numeric", str)));
|
||||
goto invalid_syntax;
|
||||
have_dp = true;
|
||||
cp++;
|
||||
/* decimal point must not be followed by underscore */
|
||||
if (*cp == '_')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else if (*cp == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
cp++;
|
||||
if (!isdigit((unsigned char) *cp))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
@@ -7010,17 +7014,8 @@ set_var_from_str(const char *str, const char *cp,
|
||||
/* Handle exponent, if any */
|
||||
if (*cp == 'e' || *cp == 'E')
|
||||
{
|
||||
long exponent;
|
||||
char *endptr;
|
||||
|
||||
cp++;
|
||||
exponent = strtol(cp, &endptr, 10);
|
||||
if (endptr == cp)
|
||||
ereturn(escontext, false,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||
"numeric", str)));
|
||||
cp = endptr;
|
||||
int64 exponent = 0;
|
||||
bool neg = false;
|
||||
|
||||
/*
|
||||
* At this point, dweight and dscale can't be more than about
|
||||
@@ -7030,10 +7025,43 @@ set_var_from_str(const char *str, const char *cp,
|
||||
* fit in storage format, make_result() will complain about it later;
|
||||
* for consistency use the same ereport errcode/text as make_result().
|
||||
*/
|
||||
if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
|
||||
ereturn(escontext, false,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value overflows numeric format")));
|
||||
|
||||
/* exponent sign */
|
||||
cp++;
|
||||
if (*cp == '+')
|
||||
cp++;
|
||||
else if (*cp == '-')
|
||||
{
|
||||
neg = true;
|
||||
cp++;
|
||||
}
|
||||
|
||||
/* exponent digits */
|
||||
if (!isdigit((unsigned char) *cp))
|
||||
goto invalid_syntax;
|
||||
|
||||
while (*cp)
|
||||
{
|
||||
if (isdigit((unsigned char) *cp))
|
||||
{
|
||||
exponent = exponent * 10 + (*cp++ - '0');
|
||||
if (exponent > PG_INT32_MAX / 2)
|
||||
goto out_of_range;
|
||||
}
|
||||
else if (*cp == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
cp++;
|
||||
if (!isdigit((unsigned char) *cp))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (neg)
|
||||
exponent = -exponent;
|
||||
|
||||
dweight += (int) exponent;
|
||||
dscale -= (int) exponent;
|
||||
if (dscale < 0)
|
||||
@@ -7085,6 +7113,17 @@ set_var_from_str(const char *str, const char *cp,
|
||||
*endptr = cp;
|
||||
|
||||
return true;
|
||||
|
||||
out_of_range:
|
||||
ereturn(escontext, false,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value overflows numeric format")));
|
||||
|
||||
invalid_syntax:
|
||||
ereturn(escontext, false,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||
"numeric", str)));
|
||||
}
|
||||
|
||||
|
||||
@@ -7167,6 +7206,13 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
|
||||
tmp = tmp * 16 + xdigit_value(*cp++);
|
||||
mul = mul * 16;
|
||||
}
|
||||
else if (*cp == '_')
|
||||
{
|
||||
/* Underscore must be followed by more digits */
|
||||
cp++;
|
||||
if (!isxdigit((unsigned char) *cp))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -7197,6 +7243,13 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
|
||||
tmp = tmp * 8 + (*cp++ - '0');
|
||||
mul = mul * 8;
|
||||
}
|
||||
else if (*cp == '_')
|
||||
{
|
||||
/* Underscore must be followed by more digits */
|
||||
cp++;
|
||||
if (*cp < '0' || *cp > '7')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -7227,6 +7280,13 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
|
||||
tmp = tmp * 2 + (*cp++ - '0');
|
||||
mul = mul * 2;
|
||||
}
|
||||
else if (*cp == '_')
|
||||
{
|
||||
/* Underscore must be followed by more digits */
|
||||
cp++;
|
||||
if (*cp < '0' || *cp > '1')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -141,48 +141,99 @@ pg_strtoint16_safe(const char *s, Node *escontext)
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && isxdigit((unsigned char) *ptr))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 16)))
|
||||
goto out_of_range;
|
||||
if (isxdigit((unsigned char) *ptr))
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 16)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
|
||||
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && (*ptr >= '0' && *ptr <= '7'))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 8)))
|
||||
goto out_of_range;
|
||||
if (*ptr >= '0' && *ptr <= '7')
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 8)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 8 + (*ptr++ - '0');
|
||||
tmp = tmp * 8 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && (*ptr >= '0' && *ptr <= '1'))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 2)))
|
||||
goto out_of_range;
|
||||
if (*ptr >= '0' && *ptr <= '1')
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 2)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 2 + (*ptr++ - '0');
|
||||
tmp = tmp * 2 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
firstdigit = ptr;
|
||||
|
||||
while (*ptr && isdigit((unsigned char) *ptr))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 10)))
|
||||
goto out_of_range;
|
||||
if (isdigit((unsigned char) *ptr))
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT16_MIN / 10)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 10 + (*ptr++ - '0');
|
||||
tmp = tmp * 10 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore may not be first */
|
||||
if (unlikely(ptr == firstdigit))
|
||||
goto invalid_syntax;
|
||||
/* and it must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,48 +319,99 @@ pg_strtoint32_safe(const char *s, Node *escontext)
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && isxdigit((unsigned char) *ptr))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 16)))
|
||||
goto out_of_range;
|
||||
if (isxdigit((unsigned char) *ptr))
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 16)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
|
||||
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && (*ptr >= '0' && *ptr <= '7'))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 8)))
|
||||
goto out_of_range;
|
||||
if (*ptr >= '0' && *ptr <= '7')
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 8)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 8 + (*ptr++ - '0');
|
||||
tmp = tmp * 8 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && (*ptr >= '0' && *ptr <= '1'))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 2)))
|
||||
goto out_of_range;
|
||||
if (*ptr >= '0' && *ptr <= '1')
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 2)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 2 + (*ptr++ - '0');
|
||||
tmp = tmp * 2 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
firstdigit = ptr;
|
||||
|
||||
while (*ptr && isdigit((unsigned char) *ptr))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 10)))
|
||||
goto out_of_range;
|
||||
if (isdigit((unsigned char) *ptr))
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT32_MIN / 10)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 10 + (*ptr++ - '0');
|
||||
tmp = tmp * 10 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore may not be first */
|
||||
if (unlikely(ptr == firstdigit))
|
||||
goto invalid_syntax;
|
||||
/* and it must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,48 +497,99 @@ pg_strtoint64_safe(const char *s, Node *escontext)
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && isxdigit((unsigned char) *ptr))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 16)))
|
||||
goto out_of_range;
|
||||
if (isxdigit((unsigned char) *ptr))
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 16)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
|
||||
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && (*ptr >= '0' && *ptr <= '7'))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 8)))
|
||||
goto out_of_range;
|
||||
if (*ptr >= '0' && *ptr <= '7')
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 8)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 8 + (*ptr++ - '0');
|
||||
tmp = tmp * 8 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
|
||||
{
|
||||
firstdigit = ptr += 2;
|
||||
|
||||
while (*ptr && (*ptr >= '0' && *ptr <= '1'))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 2)))
|
||||
goto out_of_range;
|
||||
if (*ptr >= '0' && *ptr <= '1')
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 2)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 2 + (*ptr++ - '0');
|
||||
tmp = tmp * 2 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
firstdigit = ptr;
|
||||
|
||||
while (*ptr && isdigit((unsigned char) *ptr))
|
||||
while (*ptr)
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 10)))
|
||||
goto out_of_range;
|
||||
if (isdigit((unsigned char) *ptr))
|
||||
{
|
||||
if (unlikely(tmp > -(PG_INT64_MIN / 10)))
|
||||
goto out_of_range;
|
||||
|
||||
tmp = tmp * 10 + (*ptr++ - '0');
|
||||
tmp = tmp * 10 + (*ptr++ - '0');
|
||||
}
|
||||
else if (*ptr == '_')
|
||||
{
|
||||
/* underscore may not be first */
|
||||
if (unlikely(ptr == firstdigit))
|
||||
goto invalid_syntax;
|
||||
/* and it must be followed by more digits */
|
||||
ptr++;
|
||||
if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
|
||||
goto invalid_syntax;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user