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
This commit is contained in:
Peter Eisentraut 2022-12-14 05:40:38 +01:00
parent 60684dd834
commit 6fcda9aba8
16 changed files with 1022 additions and 112 deletions

View File

@ -694,6 +694,40 @@ $function$
</literallayout>
</para>
<para>
Additionally, non-decimal integer constants can be used in these forms:
<synopsis>
0x<replaceable>hexdigits</replaceable>
0o<replaceable>octdigits</replaceable>
0b<replaceable>bindigits</replaceable>
</synopsis>
<replaceable>hexdigits</replaceable> is one or more hexadecimal digits
(0-9, A-F), <replaceable>octdigits</replaceable> is one or more octal
digits (0-7), <replaceable>bindigits</replaceable> is one or more binary
digits (0 or 1). Hexadecimal digits and the radix prefixes can be in
upper or lower case. Note that only integers can have non-decimal forms,
not numbers with fractional parts.
</para>
<para>
These are some examples of this:
<literallayout>0b100101
0B10011001
0o273
0O755
0x42f
0XFFFF
</literallayout>
</para>
<note>
<para>
Nondecimal integer constants are currently only supported in the range
of the <type>bigint</type> type (see <xref
linkend="datatype-numeric-table"/>).
</para>
</note>
<para>
<indexterm><primary>integer</primary></indexterm>
<indexterm><primary>bigint</primary></indexterm>

View File

@ -119,7 +119,7 @@ RETURN
WHEN 1700 /*numeric*/ THEN
CASE WHEN $2 = -1
THEN null
ELSE (($2 - 4) >> 16) & 65535
ELSE (($2 - 4) >> 16) & 0xFFFF
END
WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
@ -147,7 +147,7 @@ RETURN
WHEN $1 IN (1700) THEN
CASE WHEN $2 = -1
THEN null
ELSE ($2 - 4) & 65535
ELSE ($2 - 4) & 0xFFFF
END
ELSE null
END;
@ -163,7 +163,7 @@ RETURN
WHEN $1 IN (1083, 1114, 1184, 1266) /* time, timestamp, same + tz */
THEN CASE WHEN $2 < 0 THEN 6 ELSE $2 END
WHEN $1 IN (1186) /* interval */
THEN CASE WHEN $2 < 0 OR $2 & 65535 = 65535 THEN 6 ELSE $2 & 65535 END
THEN CASE WHEN $2 < 0 OR $2 & 0xFFFF = 0xFFFF THEN 6 ELSE $2 & 0xFFFF END
ELSE null
END;

View File

@ -527,6 +527,7 @@ T652 SQL-dynamic statements in SQL routines NO
T653 SQL-schema statements in external routines YES
T654 SQL-dynamic statements in external routines NO
T655 Cyclically dependent routines YES
T661 Non-decimal integer literals YES SQL:202x draft
T811 Basic SQL/JSON constructor functions NO
T812 SQL/JSON: JSON_OBJECTAGG NO
T813 SQL/JSON: JSON_ARRAYAGG with ORDER BY NO

View File

@ -385,11 +385,46 @@ make_const(ParseState *pstate, A_Const *aconst)
{
/* could be an oversize integer as well as a float ... */
int base = 10;
char *startptr;
int sign;
char *testvalue;
int64 val64;
char *endptr;
startptr = aconst->val.fval.fval;
if (startptr[0] == '-')
{
sign = -1;
startptr++;
}
else
sign = +1;
if (startptr[0] == '0')
{
if (startptr[1] == 'b' || startptr[1] == 'B')
{
base = 2;
startptr += 2;
}
else if (startptr[1] == 'o' || startptr[1] == 'O')
{
base = 8;
startptr += 2;
}
if (startptr[1] == 'x' || startptr[1] == 'X')
{
base = 16;
startptr += 2;
}
}
if (sign == +1)
testvalue = startptr;
else
testvalue = psprintf("-%s", startptr);
errno = 0;
val64 = strtoi64(aconst->val.fval.fval, &endptr, 10);
val64 = strtoi64(testvalue, &endptr, base);
if (errno == 0 && *endptr == '\0')
{
/*

View File

@ -124,7 +124,7 @@ static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner);
static void addlitchar(unsigned char ychar, core_yyscan_t yyscanner);
static char *litbufdup(core_yyscan_t yyscanner);
static unsigned char unescape_single_char(unsigned char c, core_yyscan_t yyscanner);
static int process_integer_literal(const char *token, YYSTYPE *lval);
static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
static void addunicode(pg_wchar c, yyscan_t yyscanner);
#define yyerror(msg) scanner_yyerror(msg, yyscanner)
@ -385,25 +385,40 @@ operator {op_chars}+
* Unary minus is not part of a number here. Instead we pass it separately to
* the parser, and there it gets coerced via doNegate().
*
* {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
* {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
*
* {realfail} is added to prevent the need for scanner
* backup when the {real} rule fails to match completely.
*/
digit [0-9]
decdigit [0-9]
hexdigit [0-9A-Fa-f]
octdigit [0-7]
bindigit [0-1]
integer {digit}+
decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
decimalfail {digit}+\.\.
real ({integer}|{decimal})[Ee][-+]?{digit}+
realfail ({integer}|{decimal})[Ee][-+]
decinteger {decdigit}+
hexinteger 0[xX]{hexdigit}+
octinteger 0[oO]{octdigit}+
bininteger 0[bB]{bindigit}+
integer_junk {integer}{ident_start}
decimal_junk {decimal}{ident_start}
hexfail 0[xX]
octfail 0[oO]
binfail 0[bB]
numeric (({decinteger}\.{decinteger}?)|(\.{decinteger}))
numericfail {decdigit}+\.\.
real ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
realfail ({decinteger}|{numeric})[Ee][-+]
decinteger_junk {decinteger}{ident_start}
hexinteger_junk {hexinteger}{ident_start}
octinteger_junk {octinteger}{ident_start}
bininteger_junk {bininteger}{ident_start}
numeric_junk {numeric}{ident_start}
real_junk {real}{ident_start}
param \${integer}
param_junk \${integer}{ident_start}
param \${decinteger}
param_junk \${decinteger}{ident_start}
other .
@ -983,20 +998,44 @@ other .
yyerror("trailing junk after parameter");
}
{integer} {
{decinteger} {
SET_YYLLOC();
return process_integer_literal(yytext, yylval);
return process_integer_literal(yytext, yylval, 10);
}
{decimal} {
{hexinteger} {
SET_YYLLOC();
return process_integer_literal(yytext, yylval, 16);
}
{octinteger} {
SET_YYLLOC();
return process_integer_literal(yytext, yylval, 8);
}
{bininteger} {
SET_YYLLOC();
return process_integer_literal(yytext, yylval, 2);
}
{hexfail} {
SET_YYLLOC();
yyerror("invalid hexadecimal integer");
}
{octfail} {
SET_YYLLOC();
yyerror("invalid octal integer");
}
{binfail} {
SET_YYLLOC();
yyerror("invalid binary integer");
}
{numeric} {
SET_YYLLOC();
yylval->str = pstrdup(yytext);
return FCONST;
}
{decimalfail} {
{numericfail} {
/* throw back the .., and treat as integer */
yyless(yyleng - 2);
SET_YYLLOC();
return process_integer_literal(yytext, yylval);
return process_integer_literal(yytext, yylval, 10);
}
{real} {
SET_YYLLOC();
@ -1007,11 +1046,23 @@ other .
SET_YYLLOC();
yyerror("trailing junk after numeric literal");
}
{integer_junk} {
{decinteger_junk} {
SET_YYLLOC();
yyerror("trailing junk after numeric literal");
}
{decimal_junk} {
{hexinteger_junk} {
SET_YYLLOC();
yyerror("trailing junk after numeric literal");
}
{octinteger_junk} {
SET_YYLLOC();
yyerror("trailing junk after numeric literal");
}
{bininteger_junk} {
SET_YYLLOC();
yyerror("trailing junk after numeric literal");
}
{numeric_junk} {
SET_YYLLOC();
yyerror("trailing junk after numeric literal");
}
@ -1307,17 +1358,17 @@ litbufdup(core_yyscan_t yyscanner)
}
/*
* Process {integer}. Note this will also do the right thing with {decimal},
* ie digits and a decimal point.
* Process {decinteger}, {hexinteger}, etc. Note this will also do the right
* thing with {numeric}, ie digits and a decimal point.
*/
static int
process_integer_literal(const char *token, YYSTYPE *lval)
process_integer_literal(const char *token, YYSTYPE *lval, int base)
{
int val;
char *endptr;
errno = 0;
val = strtoint(token, &endptr, 10);
val = strtoint(base == 10 ? token : token + 2, &endptr, base);
if (*endptr != '\0' || errno == ERANGE)
{
/* integer too large (or contains decimal pt), treat it as a float */

View File

@ -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,18 +136,59 @@ 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;
tmp = tmp * 10 + (*ptr++ - '0');
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;
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))
@ -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,18 +263,59 @@ 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;
tmp = tmp * 10 + (*ptr++ - '0');
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');
}
}
/* 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))
@ -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;
tmp = tmp * 10 + (*ptr++ - '0');
while (*ptr && isxdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT64_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_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))

View File

@ -323,25 +323,40 @@ operator {op_chars}+
* Unary minus is not part of a number here. Instead we pass it separately to
* the parser, and there it gets coerced via doNegate().
*
* {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
* {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
*
* {realfail} is added to prevent the need for scanner
* backup when the {real} rule fails to match completely.
*/
digit [0-9]
decdigit [0-9]
hexdigit [0-9A-Fa-f]
octdigit [0-7]
bindigit [0-1]
integer {digit}+
decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
decimalfail {digit}+\.\.
real ({integer}|{decimal})[Ee][-+]?{digit}+
realfail ({integer}|{decimal})[Ee][-+]
decinteger {decdigit}+
hexinteger 0[xX]{hexdigit}+
octinteger 0[oO]{octdigit}+
bininteger 0[bB]{bindigit}+
integer_junk {integer}{ident_start}
decimal_junk {decimal}{ident_start}
hexfail 0[xX]
octfail 0[oO]
binfail 0[bB]
numeric (({decinteger}\.{decinteger}?)|(\.{decinteger}))
numericfail {decdigit}+\.\.
real ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
realfail ({decinteger}|{numeric})[Ee][-+]
decinteger_junk {decinteger}{ident_start}
hexinteger_junk {hexinteger}{ident_start}
octinteger_junk {octinteger}{ident_start}
bininteger_junk {bininteger}{ident_start}
numeric_junk {numeric}{ident_start}
real_junk {real}{ident_start}
param \${integer}
param_junk \${integer}{ident_start}
param \${decinteger}
param_junk \${decinteger}{ident_start}
/* psql-specific: characters allowed in variable names */
variable_char [A-Za-z\200-\377_0-9]
@ -847,13 +862,31 @@ other .
ECHO;
}
{integer} {
{decinteger} {
ECHO;
}
{decimal} {
{hexinteger} {
ECHO;
}
{decimalfail} {
{octinteger} {
ECHO;
}
{bininteger} {
ECHO;
}
{hexfail} {
ECHO;
}
{octfail} {
ECHO;
}
{binfail} {
ECHO;
}
{numeric} {
ECHO;
}
{numericfail} {
/* throw back the .., and treat as integer */
yyless(yyleng - 2);
ECHO;
@ -864,10 +897,19 @@ other .
{realfail} {
ECHO;
}
{integer_junk} {
{decinteger_junk} {
ECHO;
}
{decimal_junk} {
{hexinteger_junk} {
ECHO;
}
{octinteger_junk} {
ECHO;
}
{bininteger_junk} {
ECHO;
}
{numeric_junk} {
ECHO;
}
{real_junk} {

View File

@ -57,7 +57,7 @@ static bool include_next;
#define startlit() (literalbuf[0] = '\0', literallen = 0)
static void addlit(char *ytext, int yleng);
static void addlitchar(unsigned char ychar);
static int process_integer_literal(const char *token, YYSTYPE *lval);
static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
static void parse_include(void);
static bool ecpg_isspace(char ch);
static bool isdefine(void);
@ -351,25 +351,40 @@ operator {op_chars}+
* Unary minus is not part of a number here. Instead we pass it separately to
* the parser, and there it gets coerced via doNegate().
*
* {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
* {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
*
* {realfail} is added to prevent the need for scanner
* backup when the {real} rule fails to match completely.
*/
digit [0-9]
decdigit [0-9]
hexdigit [0-9A-Fa-f]
octdigit [0-7]
bindigit [0-1]
integer {digit}+
decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
decimalfail {digit}+\.\.
real ({integer}|{decimal})[Ee][-+]?{digit}+
realfail ({integer}|{decimal})[Ee][-+]
decinteger {decdigit}+
hexinteger 0[xX]{hexdigit}+
octinteger 0[oO]{octdigit}+
bininteger 0[bB]{bindigit}+
integer_junk {integer}{ident_start}
decimal_junk {decimal}{ident_start}
hexfail 0[xX]
octfail 0[oO]
binfail 0[bB]
numeric (({decinteger}\.{decinteger}?)|(\.{decinteger}))
numericfail {decdigit}+\.\.
real ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
realfail ({decinteger}|{numeric})[Ee][-+]
decinteger_junk {decinteger}{ident_start}
hexinteger_junk {hexinteger}{ident_start}
octinteger_junk {octinteger}{ident_start}
bininteger_junk {bininteger}{ident_start}
numeric_junk {numeric}{ident_start}
real_junk {real}{ident_start}
param \${integer}
param_junk \${integer}{ident_start}
param \${decinteger}
param_junk \${decinteger}{ident_start}
/* special characters for other dbms */
/* we have to react differently in compat mode */
@ -399,9 +414,6 @@ include_next [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
import [iI][mM][pP][oO][rR][tT]
undef [uU][nN][dD][eE][fF]
/* C version of hex number */
xch 0[xX][0-9A-Fa-f]*
ccomment "//".*\n
if [iI][fF]
@ -414,7 +426,7 @@ endif [eE][nN][dD][iI][fF]
struct [sS][tT][rR][uU][cC][tT]
exec_sql {exec}{space}*{sql}{space}*
ipdigit ({digit}|{digit}{digit}|{digit}{digit}{digit})
ipdigit ({decdigit}|{decdigit}{decdigit}|{decdigit}{decdigit}{decdigit})
ip {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
/* we might want to parse all cpp include files */
@ -932,17 +944,20 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
} /* <SQL> */
<C,SQL>{
{integer} {
return process_integer_literal(yytext, &base_yylval);
{decinteger} {
return process_integer_literal(yytext, &base_yylval, 10);
}
{decimal} {
{hexinteger} {
return process_integer_literal(yytext, &base_yylval, 16);
}
{numeric} {
base_yylval.str = mm_strdup(yytext);
return FCONST;
}
{decimalfail} {
{numericfail} {
/* throw back the .., and treat as integer */
yyless(yyleng - 2);
return process_integer_literal(yytext, &base_yylval);
return process_integer_literal(yytext, &base_yylval, 10);
}
{real} {
base_yylval.str = mm_strdup(yytext);
@ -951,22 +966,38 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
{realfail} {
/*
* throw back the [Ee][+-], and figure out whether what
* remains is an {integer} or {decimal}.
* remains is an {decinteger} or {numeric}.
*/
yyless(yyleng - 2);
return process_integer_literal(yytext, &base_yylval);
return process_integer_literal(yytext, &base_yylval, 10);
}
} /* <C,SQL> */
<SQL>{
{octinteger} {
return process_integer_literal(yytext, &base_yylval, 8);
}
{bininteger} {
return process_integer_literal(yytext, &base_yylval, 2);
}
/*
* Note that some trailing junk is valid in C (such as 100LL), so we
* contain this to SQL mode.
*/
{integer_junk} {
{decinteger_junk} {
mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
}
{decimal_junk} {
{hexinteger_junk} {
mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
}
{octinteger_junk} {
mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
}
{bininteger_junk} {
mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
}
{numeric_junk} {
mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
}
{real_junk} {
@ -1036,19 +1067,6 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
return S_ANYTHING;
}
<C>{ccomment} { ECHO; }
<C>{xch} {
char* endptr;
errno = 0;
base_yylval.ival = strtoul((char *) yytext, &endptr, 16);
if (*endptr != '\0' || errno == ERANGE)
{
errno = 0;
base_yylval.str = mm_strdup(yytext);
return SCONST;
}
return ICONST;
}
<C>{cppinclude} {
if (system_includes)
{
@ -1573,17 +1591,17 @@ addlitchar(unsigned char ychar)
}
/*
* Process {integer}. Note this will also do the right thing with {decimal},
* ie digits and a decimal point.
* Process {decinteger}, {hexinteger}, etc. Note this will also do the right
* thing with {numeric}, ie digits and a decimal point.
*/
static int
process_integer_literal(const char *token, YYSTYPE *lval)
process_integer_literal(const char *token, YYSTYPE *lval, int base)
{
int val;
char *endptr;
errno = 0;
val = strtoint(token, &endptr, 10);
val = strtoint(base == 10 ? token : token + 2, &endptr, base);
if (*endptr != '\0' || errno == ERANGE)
{
/* integer too large (or contains decimal pt), treat it as a float */

View File

@ -329,3 +329,95 @@ FROM (VALUES (-2.5::numeric),
2.5 | 3
(7 rows)
-- non-decimal literals
SELECT int2 '0b100101';
int2
------
37
(1 row)
SELECT int2 '0o273';
int2
------
187
(1 row)
SELECT int2 '0x42F';
int2
------
1071
(1 row)
SELECT int2 '0b';
ERROR: invalid input syntax for type smallint: "0b"
LINE 1: SELECT int2 '0b';
^
SELECT int2 '0o';
ERROR: invalid input syntax for type smallint: "0o"
LINE 1: SELECT int2 '0o';
^
SELECT int2 '0x';
ERROR: invalid input syntax for type smallint: "0x"
LINE 1: SELECT int2 '0x';
^
-- cases near overflow
SELECT int2 '0b111111111111111';
int2
-------
32767
(1 row)
SELECT int2 '0b1000000000000000';
ERROR: value "0b1000000000000000" is out of range for type smallint
LINE 1: SELECT int2 '0b1000000000000000';
^
SELECT int2 '0o77777';
int2
-------
32767
(1 row)
SELECT int2 '0o100000';
ERROR: value "0o100000" is out of range for type smallint
LINE 1: SELECT int2 '0o100000';
^
SELECT int2 '0x7FFF';
int2
-------
32767
(1 row)
SELECT int2 '0x8000';
ERROR: value "0x8000" is out of range for type smallint
LINE 1: SELECT int2 '0x8000';
^
SELECT int2 '-0b1000000000000000';
int2
--------
-32768
(1 row)
SELECT int2 '-0b1000000000000001';
ERROR: value "-0b1000000000000001" is out of range for type smallint
LINE 1: SELECT int2 '-0b1000000000000001';
^
SELECT int2 '-0o100000';
int2
--------
-32768
(1 row)
SELECT int2 '-0o100001';
ERROR: value "-0o100001" is out of range for type smallint
LINE 1: SELECT int2 '-0o100001';
^
SELECT int2 '-0x8000';
int2
--------
-32768
(1 row)
SELECT int2 '-0x8001';
ERROR: value "-0x8001" is out of range for type smallint
LINE 1: SELECT int2 '-0x8001';
^

View File

@ -456,3 +456,95 @@ SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
ERROR: integer out of range
SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
ERROR: integer out of range
-- non-decimal literals
SELECT int4 '0b100101';
int4
------
37
(1 row)
SELECT int4 '0o273';
int4
------
187
(1 row)
SELECT int4 '0x42F';
int4
------
1071
(1 row)
SELECT int4 '0b';
ERROR: invalid input syntax for type integer: "0b"
LINE 1: SELECT int4 '0b';
^
SELECT int4 '0o';
ERROR: invalid input syntax for type integer: "0o"
LINE 1: SELECT int4 '0o';
^
SELECT int4 '0x';
ERROR: invalid input syntax for type integer: "0x"
LINE 1: SELECT int4 '0x';
^
-- cases near overflow
SELECT int4 '0b1111111111111111111111111111111';
int4
------------
2147483647
(1 row)
SELECT int4 '0b10000000000000000000000000000000';
ERROR: value "0b10000000000000000000000000000000" is out of range for type integer
LINE 1: SELECT int4 '0b10000000000000000000000000000000';
^
SELECT int4 '0o17777777777';
int4
------------
2147483647
(1 row)
SELECT int4 '0o20000000000';
ERROR: value "0o20000000000" is out of range for type integer
LINE 1: SELECT int4 '0o20000000000';
^
SELECT int4 '0x7FFFFFFF';
int4
------------
2147483647
(1 row)
SELECT int4 '0x80000000';
ERROR: value "0x80000000" is out of range for type integer
LINE 1: SELECT int4 '0x80000000';
^
SELECT int4 '-0b10000000000000000000000000000000';
int4
-------------
-2147483648
(1 row)
SELECT int4 '-0b10000000000000000000000000000001';
ERROR: value "-0b10000000000000000000000000000001" is out of range for type integer
LINE 1: SELECT int4 '-0b10000000000000000000000000000001';
^
SELECT int4 '-0o20000000000';
int4
-------------
-2147483648
(1 row)
SELECT int4 '-0o20000000001';
ERROR: value "-0o20000000001" is out of range for type integer
LINE 1: SELECT int4 '-0o20000000001';
^
SELECT int4 '-0x80000000';
int4
-------------
-2147483648
(1 row)
SELECT int4 '-0x80000001';
ERROR: value "-0x80000001" is out of range for type integer
LINE 1: SELECT int4 '-0x80000001';
^

View File

@ -952,3 +952,95 @@ SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow
ERROR: bigint out of range
SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow
ERROR: bigint out of range
-- non-decimal literals
SELECT int8 '0b100101';
int8
------
37
(1 row)
SELECT int8 '0o273';
int8
------
187
(1 row)
SELECT int8 '0x42F';
int8
------
1071
(1 row)
SELECT int8 '0b';
ERROR: invalid input syntax for type bigint: "0b"
LINE 1: SELECT int8 '0b';
^
SELECT int8 '0o';
ERROR: invalid input syntax for type bigint: "0o"
LINE 1: SELECT int8 '0o';
^
SELECT int8 '0x';
ERROR: invalid input syntax for type bigint: "0x"
LINE 1: SELECT int8 '0x';
^
-- cases near overflow
SELECT int8 '0b111111111111111111111111111111111111111111111111111111111111111';
int8
---------------------
9223372036854775807
(1 row)
SELECT int8 '0b1000000000000000000000000000000000000000000000000000000000000000';
ERROR: value "0b1000000000000000000000000000000000000000000000000000000000000000" is out of range for type bigint
LINE 1: SELECT int8 '0b100000000000000000000000000000000000000000000...
^
SELECT int8 '0o777777777777777777777';
int8
---------------------
9223372036854775807
(1 row)
SELECT int8 '0o1000000000000000000000';
ERROR: value "0o1000000000000000000000" is out of range for type bigint
LINE 1: SELECT int8 '0o1000000000000000000000';
^
SELECT int8 '0x7FFFFFFFFFFFFFFF';
int8
---------------------
9223372036854775807
(1 row)
SELECT int8 '0x8000000000000000';
ERROR: value "0x8000000000000000" is out of range for type bigint
LINE 1: SELECT int8 '0x8000000000000000';
^
SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000000';
int8
----------------------
-9223372036854775808
(1 row)
SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000001';
ERROR: value "-0b1000000000000000000000000000000000000000000000000000000000000001" is out of range for type bigint
LINE 1: SELECT int8 '-0b10000000000000000000000000000000000000000000...
^
SELECT int8 '-0o1000000000000000000000';
int8
----------------------
-9223372036854775808
(1 row)
SELECT int8 '-0o1000000000000000000001';
ERROR: value "-0o1000000000000000000001" is out of range for type bigint
LINE 1: SELECT int8 '-0o1000000000000000000001';
^
SELECT int8 '-0x8000000000000000';
int8
----------------------
-9223372036854775808
(1 row)
SELECT int8 '-0x8000000000000001';
ERROR: value "-0x8000000000000001" is out of range for type bigint
LINE 1: SELECT int8 '-0x8000000000000001';
^

View File

@ -3,14 +3,167 @@
-- Test various combinations of numeric types and functions.
--
--
-- Trailing junk in numeric literals
-- numeric literals
--
SELECT 0b100101;
?column?
----------
37
(1 row)
SELECT 0o273;
?column?
----------
187
(1 row)
SELECT 0x42F;
?column?
----------
1071
(1 row)
-- cases near int4 overflow
SELECT 0b1111111111111111111111111111111;
?column?
------------
2147483647
(1 row)
SELECT 0b10000000000000000000000000000000;
?column?
------------
2147483648
(1 row)
SELECT 0o17777777777;
?column?
------------
2147483647
(1 row)
SELECT 0o20000000000;
?column?
------------
2147483648
(1 row)
SELECT 0x7FFFFFFF;
?column?
------------
2147483647
(1 row)
SELECT 0x80000000;
?column?
------------
2147483648
(1 row)
SELECT -0b10000000000000000000000000000000;
?column?
-------------
-2147483648
(1 row)
SELECT -0b10000000000000000000000000000001;
?column?
-------------
-2147483649
(1 row)
SELECT -0o20000000000;
?column?
-------------
-2147483648
(1 row)
SELECT -0o20000000001;
?column?
-------------
-2147483649
(1 row)
SELECT -0x80000000;
?column?
-------------
-2147483648
(1 row)
SELECT -0x80000001;
?column?
-------------
-2147483649
(1 row)
-- cases near int8 overflow
SELECT 0b111111111111111111111111111111111111111111111111111111111111111;
?column?
---------------------
9223372036854775807
(1 row)
SELECT 0b1000000000000000000000000000000000000000000000000000000000000000;
ERROR: invalid input syntax for type numeric: "0b1000000000000000000000000000000000000000000000000000000000000000"
LINE 1: SELECT 0b100000000000000000000000000000000000000000000000000...
^
SELECT 0o777777777777777777777;
?column?
---------------------
9223372036854775807
(1 row)
SELECT 0o1000000000000000000000;
ERROR: invalid input syntax for type numeric: "0o1000000000000000000000"
LINE 1: SELECT 0o1000000000000000000000;
^
SELECT 0x7FFFFFFFFFFFFFFF;
?column?
---------------------
9223372036854775807
(1 row)
SELECT 0x8000000000000000;
ERROR: invalid input syntax for type numeric: "0x8000000000000000"
LINE 1: SELECT 0x8000000000000000;
^
SELECT -0b1000000000000000000000000000000000000000000000000000000000000000;
?column?
----------------------
-9223372036854775808
(1 row)
SELECT -0b1000000000000000000000000000000000000000000000000000000000000001;
ERROR: invalid input syntax for type numeric: "-0b1000000000000000000000000000000000000000000000000000000000000001"
LINE 1: SELECT -0b10000000000000000000000000000000000000000000000000...
^
SELECT -0o1000000000000000000000;
?column?
----------------------
-9223372036854775808
(1 row)
SELECT -0o1000000000000000000001;
ERROR: invalid input syntax for type numeric: "-0o1000000000000000000001"
LINE 1: SELECT -0o1000000000000000000001;
^
SELECT -0x8000000000000000;
?column?
----------------------
-9223372036854775808
(1 row)
SELECT -0x8000000000000001;
ERROR: invalid input syntax for type numeric: "-0x8000000000000001"
LINE 1: SELECT -0x8000000000000001;
^
-- error cases
SELECT 123abc;
ERROR: trailing junk after numeric literal at or near "123a"
LINE 1: SELECT 123abc;
^
SELECT 0x0o;
ERROR: trailing junk after numeric literal at or near "0x"
ERROR: trailing junk after numeric literal at or near "0x0o"
LINE 1: SELECT 0x0o;
^
SELECT 1_2_3;
@ -45,6 +198,42 @@ PREPARE p1 AS SELECT $1a;
ERROR: trailing junk after parameter at or near "$1a"
LINE 1: PREPARE p1 AS SELECT $1a;
^
SELECT 0b;
ERROR: invalid binary integer at or near "0b"
LINE 1: SELECT 0b;
^
SELECT 1b;
ERROR: trailing junk after numeric literal at or near "1b"
LINE 1: SELECT 1b;
^
SELECT 0b0x;
ERROR: trailing junk after numeric literal at or near "0b0x"
LINE 1: SELECT 0b0x;
^
SELECT 0o;
ERROR: invalid octal integer at or near "0o"
LINE 1: SELECT 0o;
^
SELECT 1o;
ERROR: trailing junk after numeric literal at or near "1o"
LINE 1: SELECT 1o;
^
SELECT 0o0x;
ERROR: trailing junk after numeric literal at or near "0o0x"
LINE 1: SELECT 0o0x;
^
SELECT 0x;
ERROR: invalid hexadecimal integer at or near "0x"
LINE 1: SELECT 0x;
^
SELECT 1x;
ERROR: trailing junk after numeric literal at or near "1x"
LINE 1: SELECT 1x;
^
SELECT 0x0y;
ERROR: trailing junk after numeric literal at or near "0x0y"
LINE 1: SELECT 0x0y;
^
--
-- Test implicit type conversions
-- This fails for Postgres v6.1 (and earlier?)

View File

@ -110,3 +110,29 @@ FROM (VALUES (-2.5::numeric),
(0.5::numeric),
(1.5::numeric),
(2.5::numeric)) t(x);
-- non-decimal literals
SELECT int2 '0b100101';
SELECT int2 '0o273';
SELECT int2 '0x42F';
SELECT int2 '0b';
SELECT int2 '0o';
SELECT int2 '0x';
-- cases near overflow
SELECT int2 '0b111111111111111';
SELECT int2 '0b1000000000000000';
SELECT int2 '0o77777';
SELECT int2 '0o100000';
SELECT int2 '0x7FFF';
SELECT int2 '0x8000';
SELECT int2 '-0b1000000000000000';
SELECT int2 '-0b1000000000000001';
SELECT int2 '-0o100000';
SELECT int2 '-0o100001';
SELECT int2 '-0x8000';
SELECT int2 '-0x8001';

View File

@ -170,3 +170,29 @@ FROM (VALUES (0::int4, 0::int4),
SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
-- non-decimal literals
SELECT int4 '0b100101';
SELECT int4 '0o273';
SELECT int4 '0x42F';
SELECT int4 '0b';
SELECT int4 '0o';
SELECT int4 '0x';
-- cases near overflow
SELECT int4 '0b1111111111111111111111111111111';
SELECT int4 '0b10000000000000000000000000000000';
SELECT int4 '0o17777777777';
SELECT int4 '0o20000000000';
SELECT int4 '0x7FFFFFFF';
SELECT int4 '0x80000000';
SELECT int4 '-0b10000000000000000000000000000000';
SELECT int4 '-0b10000000000000000000000000000001';
SELECT int4 '-0o20000000000';
SELECT int4 '-0o20000000001';
SELECT int4 '-0x80000000';
SELECT int4 '-0x80000001';

View File

@ -251,3 +251,29 @@ FROM (VALUES (0::int8, 0::int8),
SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow
SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow
-- non-decimal literals
SELECT int8 '0b100101';
SELECT int8 '0o273';
SELECT int8 '0x42F';
SELECT int8 '0b';
SELECT int8 '0o';
SELECT int8 '0x';
-- cases near overflow
SELECT int8 '0b111111111111111111111111111111111111111111111111111111111111111';
SELECT int8 '0b1000000000000000000000000000000000000000000000000000000000000000';
SELECT int8 '0o777777777777777777777';
SELECT int8 '0o1000000000000000000000';
SELECT int8 '0x7FFFFFFFFFFFFFFF';
SELECT int8 '0x8000000000000000';
SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000000';
SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000001';
SELECT int8 '-0o1000000000000000000000';
SELECT int8 '-0o1000000000000000000001';
SELECT int8 '-0x8000000000000000';
SELECT int8 '-0x8000000000000001';

View File

@ -3,10 +3,46 @@
-- Test various combinations of numeric types and functions.
--
--
-- Trailing junk in numeric literals
-- numeric literals
--
SELECT 0b100101;
SELECT 0o273;
SELECT 0x42F;
-- cases near int4 overflow
SELECT 0b1111111111111111111111111111111;
SELECT 0b10000000000000000000000000000000;
SELECT 0o17777777777;
SELECT 0o20000000000;
SELECT 0x7FFFFFFF;
SELECT 0x80000000;
SELECT -0b10000000000000000000000000000000;
SELECT -0b10000000000000000000000000000001;
SELECT -0o20000000000;
SELECT -0o20000000001;
SELECT -0x80000000;
SELECT -0x80000001;
-- cases near int8 overflow
SELECT 0b111111111111111111111111111111111111111111111111111111111111111;
SELECT 0b1000000000000000000000000000000000000000000000000000000000000000;
SELECT 0o777777777777777777777;
SELECT 0o1000000000000000000000;
SELECT 0x7FFFFFFFFFFFFFFF;
SELECT 0x8000000000000000;
SELECT -0b1000000000000000000000000000000000000000000000000000000000000000;
SELECT -0b1000000000000000000000000000000000000000000000000000000000000001;
SELECT -0o1000000000000000000000;
SELECT -0o1000000000000000000001;
SELECT -0x8000000000000000;
SELECT -0x8000000000000001;
-- error cases
SELECT 123abc;
SELECT 0x0o;
SELECT 1_2_3;
@ -18,6 +54,19 @@ SELECT 0.0e;
SELECT 0.0e+a;
PREPARE p1 AS SELECT $1a;
SELECT 0b;
SELECT 1b;
SELECT 0b0x;
SELECT 0o;
SELECT 1o;
SELECT 0o0x;
SELECT 0x;
SELECT 1x;
SELECT 0x0y;
--
-- Test implicit type conversions
-- This fails for Postgres v6.1 (and earlier?)