aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numutils.c
diff options
context:
space:
mode:
authorPeter Eisentraut <peter@eisentraut.org>2022-12-14 05:40:38 +0100
committerPeter Eisentraut <peter@eisentraut.org>2022-12-14 06:17:07 +0100
commit6fcda9aba83449082124825b6d375c0a61e21c42 (patch)
treed2e23f5322bf6879e0ee328593fbc7b3f6f71702 /src/backend/utils/adt/numutils.c
parent60684dd834a222fefedd49b19d1f0a6189c1632e (diff)
downloadpostgresql-6fcda9aba83449082124825b6d375c0a61e21c42.tar.gz
postgresql-6fcda9aba83449082124825b6d375c0a61e21c42.zip
Non-decimal integer literals
Add support for hexadecimal, octal, and binary integer literals: 0x42F 0o273 0b100101 per SQL:202x draft. This adds support in the lexer as well as in the integer type input functions. Reviewed-by: John Naylor <john.naylor@enterprisedb.com> Reviewed-by: Zhihong Yu <zyu@yugabyte.com> Reviewed-by: David Rowley <dgrowleyml@gmail.com> Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/b239564c-cad0-b23e-c57e-166d883cb97d@enterprisedb.com
Diffstat (limited to 'src/backend/utils/adt/numutils.c')
-rw-r--r--src/backend/utils/adt/numutils.c185
1 files changed, 161 insertions, 24 deletions
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index ab1564f22da..7cded73e6e6 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -85,6 +85,17 @@ decimalLength64(const uint64 v)
return t + (v >= PowersOfTen[t]);
}
+static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
/*
* Convert input string to a signed 16 bit integer.
*
@@ -108,6 +119,7 @@ int16
pg_strtoint16_safe(const char *s, Node *escontext)
{
const char *ptr = s;
+ const char *firstdigit;
uint16 tmp = 0;
bool neg = false;
@@ -124,19 +136,60 @@ pg_strtoint16_safe(const char *s, Node *escontext)
else if (*ptr == '+')
ptr++;
- /* require at least one digit */
- if (unlikely(!isdigit((unsigned char) *ptr)))
- goto invalid_syntax;
-
/* process digits */
- while (*ptr && isdigit((unsigned char) *ptr))
+ if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
{
- if (unlikely(tmp > -(PG_INT16_MIN / 10)))
- goto out_of_range;
+ firstdigit = ptr += 2;
+
+ while (*ptr && isxdigit((unsigned char) *ptr))
+ {
+ if (unlikely(tmp > -(PG_INT16_MIN / 16)))
+ goto out_of_range;
+
+ tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+ }
+ }
+ else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+ {
+ firstdigit = ptr += 2;
+
+ while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+ {
+ if (unlikely(tmp > -(PG_INT16_MIN / 8)))
+ goto out_of_range;
+
+ tmp = tmp * 8 + (*ptr++ - '0');
+ }
+ }
+ else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+ {
+ firstdigit = ptr += 2;
+
+ while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+ {
+ if (unlikely(tmp > -(PG_INT16_MIN / 2)))
+ goto out_of_range;
+
+ tmp = tmp * 2 + (*ptr++ - '0');
+ }
+ }
+ else
+ {
+ firstdigit = ptr;
- tmp = tmp * 10 + (*ptr++ - '0');
+ while (*ptr && isdigit((unsigned char) *ptr))
+ {
+ if (unlikely(tmp > -(PG_INT16_MIN / 10)))
+ goto out_of_range;
+
+ tmp = tmp * 10 + (*ptr++ - '0');
+ }
}
+ /* require at least one digit */
+ if (unlikely(ptr == firstdigit))
+ goto invalid_syntax;
+
/* allow trailing whitespace, but not other trailing chars */
while (*ptr != '\0' && isspace((unsigned char) *ptr))
ptr++;
@@ -193,6 +246,7 @@ int32
pg_strtoint32_safe(const char *s, Node *escontext)
{
const char *ptr = s;
+ const char *firstdigit;
uint32 tmp = 0;
bool neg = false;
@@ -209,19 +263,60 @@ pg_strtoint32_safe(const char *s, Node *escontext)
else if (*ptr == '+')
ptr++;
- /* require at least one digit */
- if (unlikely(!isdigit((unsigned char) *ptr)))
- goto invalid_syntax;
-
/* process digits */
- while (*ptr && isdigit((unsigned char) *ptr))
+ if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
{
- if (unlikely(tmp > -(PG_INT32_MIN / 10)))
- goto out_of_range;
+ firstdigit = ptr += 2;
+
+ while (*ptr && isxdigit((unsigned char) *ptr))
+ {
+ if (unlikely(tmp > -(PG_INT32_MIN / 16)))
+ goto out_of_range;
+
+ tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+ }
+ }
+ else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+ {
+ firstdigit = ptr += 2;
+
+ while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+ {
+ if (unlikely(tmp > -(PG_INT32_MIN / 8)))
+ goto out_of_range;
+
+ tmp = tmp * 8 + (*ptr++ - '0');
+ }
+ }
+ else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+ {
+ firstdigit = ptr += 2;
+
+ while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+ {
+ if (unlikely(tmp > -(PG_INT32_MIN / 2)))
+ goto out_of_range;
+
+ tmp = tmp * 2 + (*ptr++ - '0');
+ }
+ }
+ else
+ {
+ firstdigit = ptr;
+
+ while (*ptr && 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');
+ }
}
+ /* require at least one digit */
+ if (unlikely(ptr == firstdigit))
+ goto invalid_syntax;
+
/* allow trailing whitespace, but not other trailing chars */
while (*ptr != '\0' && isspace((unsigned char) *ptr))
ptr++;
@@ -278,6 +373,7 @@ int64
pg_strtoint64_safe(const char *s, Node *escontext)
{
const char *ptr = s;
+ const char *firstdigit;
uint64 tmp = 0;
bool neg = false;
@@ -294,18 +390,59 @@ pg_strtoint64_safe(const char *s, Node *escontext)
else if (*ptr == '+')
ptr++;
- /* require at least one digit */
- if (unlikely(!isdigit((unsigned char) *ptr)))
- goto invalid_syntax;
-
/* process digits */
- while (*ptr && isdigit((unsigned char) *ptr))
+ if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
{
- if (unlikely(tmp > -(PG_INT64_MIN / 10)))
- goto out_of_range;
+ firstdigit = ptr += 2;
+
+ while (*ptr && isxdigit((unsigned char) *ptr))
+ {
+ if (unlikely(tmp > -(PG_INT64_MIN / 16)))
+ goto out_of_range;
- tmp = tmp * 10 + (*ptr++ - '0');
+ tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+ }
}
+ else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+ {
+ firstdigit = ptr += 2;
+
+ while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+ {
+ if (unlikely(tmp > -(PG_INT64_MIN / 8)))
+ goto out_of_range;
+
+ tmp = tmp * 8 + (*ptr++ - '0');
+ }
+ }
+ else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+ {
+ firstdigit = ptr += 2;
+
+ while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+ {
+ if (unlikely(tmp > -(PG_INT64_MIN / 2)))
+ goto out_of_range;
+
+ tmp = tmp * 2 + (*ptr++ - '0');
+ }
+ }
+ else
+ {
+ firstdigit = ptr;
+
+ while (*ptr && isdigit((unsigned char) *ptr))
+ {
+ if (unlikely(tmp > -(PG_INT64_MIN / 10)))
+ goto out_of_range;
+
+ tmp = tmp * 10 + (*ptr++ - '0');
+ }
+ }
+
+ /* require at least one digit */
+ if (unlikely(ptr == firstdigit))
+ goto invalid_syntax;
/* allow trailing whitespace, but not other trailing chars */
while (*ptr != '\0' && isspace((unsigned char) *ptr))