Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6fcda9a

Browse files
committed
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
1 parent 60684dd commit 6fcda9a

File tree

16 files changed

+1028
-118
lines changed

16 files changed

+1028
-118
lines changed

doc/src/sgml/syntax.sgml

+34
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,40 @@ $function$
694694
</literallayout>
695695
</para>
696696

697+
<para>
698+
Additionally, non-decimal integer constants can be used in these forms:
699+
<synopsis>
700+
0x<replaceable>hexdigits</replaceable>
701+
0o<replaceable>octdigits</replaceable>
702+
0b<replaceable>bindigits</replaceable>
703+
</synopsis>
704+
<replaceable>hexdigits</replaceable> is one or more hexadecimal digits
705+
(0-9, A-F), <replaceable>octdigits</replaceable> is one or more octal
706+
digits (0-7), <replaceable>bindigits</replaceable> is one or more binary
707+
digits (0 or 1). Hexadecimal digits and the radix prefixes can be in
708+
upper or lower case. Note that only integers can have non-decimal forms,
709+
not numbers with fractional parts.
710+
</para>
711+
712+
<para>
713+
These are some examples of this:
714+
<literallayout>0b100101
715+
0B10011001
716+
0o273
717+
0O755
718+
0x42f
719+
0XFFFF
720+
</literallayout>
721+
</para>
722+
723+
<note>
724+
<para>
725+
Nondecimal integer constants are currently only supported in the range
726+
of the <type>bigint</type> type (see <xref
727+
linkend="datatype-numeric-table"/>).
728+
</para>
729+
</note>
730+
697731
<para>
698732
<indexterm><primary>integer</primary></indexterm>
699733
<indexterm><primary>bigint</primary></indexterm>

src/backend/catalog/information_schema.sql

+3-3
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ RETURN
119119
WHEN 1700 /*numeric*/ THEN
120120
CASE WHEN $2 = -1
121121
THEN null
122-
ELSE (($2 - 4) >> 16) & 65535
122+
ELSE (($2 - 4) >> 16) & 0xFFFF
123123
END
124124
WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
125125
WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
@@ -147,7 +147,7 @@ RETURN
147147
WHEN $1 IN (1700) THEN
148148
CASE WHEN $2 = -1
149149
THEN null
150-
ELSE ($2 - 4) & 65535
150+
ELSE ($2 - 4) & 0xFFFF
151151
END
152152
ELSE null
153153
END;
@@ -163,7 +163,7 @@ RETURN
163163
WHEN $1 IN (1083, 1114, 1184, 1266) /* time, timestamp, same + tz */
164164
THEN CASE WHEN $2 < 0 THEN 6 ELSE $2 END
165165
WHEN $1 IN (1186) /* interval */
166-
THEN CASE WHEN $2 < 0 OR $2 & 65535 = 65535 THEN 6 ELSE $2 & 65535 END
166+
THEN CASE WHEN $2 < 0 OR $2 & 0xFFFF = 0xFFFF THEN 6 ELSE $2 & 0xFFFF END
167167
ELSE null
168168
END;
169169

src/backend/catalog/sql_features.txt

+1
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ T652 SQL-dynamic statements in SQL routines NO
527527
T653 SQL-schema statements in external routines YES
528528
T654 SQL-dynamic statements in external routines NO
529529
T655 Cyclically dependent routines YES
530+
T661 Non-decimal integer literals YES SQL:202x draft
530531
T811 Basic SQL/JSON constructor functions NO
531532
T812 SQL/JSON: JSON_OBJECTAGG NO
532533
T813 SQL/JSON: JSON_ARRAYAGG with ORDER BY NO

src/backend/parser/parse_node.c

+36-1
Original file line numberDiff line numberDiff line change
@@ -385,11 +385,46 @@ make_const(ParseState *pstate, A_Const *aconst)
385385
{
386386
/* could be an oversize integer as well as a float ... */
387387

388+
int base = 10;
389+
char *startptr;
390+
int sign;
391+
char *testvalue;
388392
int64 val64;
389393
char *endptr;
390394

395+
startptr = aconst->val.fval.fval;
396+
if (startptr[0] == '-')
397+
{
398+
sign = -1;
399+
startptr++;
400+
}
401+
else
402+
sign = +1;
403+
if (startptr[0] == '0')
404+
{
405+
if (startptr[1] == 'b' || startptr[1] == 'B')
406+
{
407+
base = 2;
408+
startptr += 2;
409+
}
410+
else if (startptr[1] == 'o' || startptr[1] == 'O')
411+
{
412+
base = 8;
413+
startptr += 2;
414+
}
415+
if (startptr[1] == 'x' || startptr[1] == 'X')
416+
{
417+
base = 16;
418+
startptr += 2;
419+
}
420+
}
421+
422+
if (sign == +1)
423+
testvalue = startptr;
424+
else
425+
testvalue = psprintf("-%s", startptr);
391426
errno = 0;
392-
val64 = strtoi64(aconst->val.fval.fval, &endptr, 10);
427+
val64 = strtoi64(testvalue, &endptr, base);
393428
if (errno == 0 && *endptr == '\0')
394429
{
395430
/*

src/backend/parser/scan.l

+76-25
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner);
124124
static void addlitchar(unsigned char ychar, core_yyscan_t yyscanner);
125125
static char *litbufdup(core_yyscan_t yyscanner);
126126
static unsigned char unescape_single_char(unsigned char c, core_yyscan_t yyscanner);
127-
static int process_integer_literal(const char *token, YYSTYPE *lval);
127+
static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
128128
static void addunicode(pg_wchar c, yyscan_t yyscanner);
129129

130130
#define yyerror(msg) scanner_yyerror(msg, yyscanner)
@@ -385,25 +385,40 @@ operator {op_chars}+
385385
* Unary minus is not part of a number here. Instead we pass it separately to
386386
* the parser, and there it gets coerced via doNegate().
387387
*
388-
* {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
388+
* {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
389389
*
390390
* {realfail} is added to prevent the need for scanner
391391
* backup when the {real} rule fails to match completely.
392392
*/
393-
digit [0-9]
394-
395-
integer {digit}+
396-
decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
397-
decimalfail {digit}+\.\.
398-
real ({integer}|{decimal})[Ee][-+]?{digit}+
399-
realfail ({integer}|{decimal})[Ee][-+]
400-
401-
integer_junk {integer}{ident_start}
402-
decimal_junk {decimal}{ident_start}
393+
decdigit [0-9]
394+
hexdigit [0-9A-Fa-f]
395+
octdigit [0-7]
396+
bindigit [0-1]
397+
398+
decinteger {decdigit}+
399+
hexinteger 0[xX]{hexdigit}+
400+
octinteger 0[oO]{octdigit}+
401+
bininteger 0[bB]{bindigit}+
402+
403+
hexfail 0[xX]
404+
octfail 0[oO]
405+
binfail 0[bB]
406+
407+
numeric (({decinteger}\.{decinteger}?)|(\.{decinteger}))
408+
numericfail {decdigit}+\.\.
409+
410+
real ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
411+
realfail ({decinteger}|{numeric})[Ee][-+]
412+
413+
decinteger_junk {decinteger}{ident_start}
414+
hexinteger_junk {hexinteger}{ident_start}
415+
octinteger_junk {octinteger}{ident_start}
416+
bininteger_junk {bininteger}{ident_start}
417+
numeric_junk {numeric}{ident_start}
403418
real_junk {real}{ident_start}
404419

405-
param \${integer}
406-
param_junk \${integer}{ident_start}
420+
param \${decinteger}
421+
param_junk \${decinteger}{ident_start}
407422

408423
other .
409424

@@ -983,20 +998,44 @@ other .
983998
yyerror("trailing junk after parameter");
984999
}
9851000

986-
{integer} {
1001+
{decinteger} {
1002+
SET_YYLLOC();
1003+
return process_integer_literal(yytext, yylval, 10);
1004+
}
1005+
{hexinteger} {
1006+
SET_YYLLOC();
1007+
return process_integer_literal(yytext, yylval, 16);
1008+
}
1009+
{octinteger} {
1010+
SET_YYLLOC();
1011+
return process_integer_literal(yytext, yylval, 8);
1012+
}
1013+
{bininteger} {
1014+
SET_YYLLOC();
1015+
return process_integer_literal(yytext, yylval, 2);
1016+
}
1017+
{hexfail} {
1018+
SET_YYLLOC();
1019+
yyerror("invalid hexadecimal integer");
1020+
}
1021+
{octfail} {
9871022
SET_YYLLOC();
988-
return process_integer_literal(yytext, yylval);
1023+
yyerror("invalid octal integer");
9891024
}
990-
{decimal} {
1025+
{binfail} {
1026+
SET_YYLLOC();
1027+
yyerror("invalid binary integer");
1028+
}
1029+
{numeric} {
9911030
SET_YYLLOC();
9921031
yylval->str = pstrdup(yytext);
9931032
return FCONST;
9941033
}
995-
{decimalfail} {
1034+
{numericfail} {
9961035
/* throw back the .., and treat as integer */
9971036
yyless(yyleng - 2);
9981037
SET_YYLLOC();
999-
return process_integer_literal(yytext, yylval);
1038+
return process_integer_literal(yytext, yylval, 10);
10001039
}
10011040
{real} {
10021041
SET_YYLLOC();
@@ -1007,11 +1046,23 @@ other .
10071046
SET_YYLLOC();
10081047
yyerror("trailing junk after numeric literal");
10091048
}
1010-
{integer_junk} {
1049+
{decinteger_junk} {
1050+
SET_YYLLOC();
1051+
yyerror("trailing junk after numeric literal");
1052+
}
1053+
{hexinteger_junk} {
1054+
SET_YYLLOC();
1055+
yyerror("trailing junk after numeric literal");
1056+
}
1057+
{octinteger_junk} {
1058+
SET_YYLLOC();
1059+
yyerror("trailing junk after numeric literal");
1060+
}
1061+
{bininteger_junk} {
10111062
SET_YYLLOC();
10121063
yyerror("trailing junk after numeric literal");
10131064
}
1014-
{decimal_junk} {
1065+
{numeric_junk} {
10151066
SET_YYLLOC();
10161067
yyerror("trailing junk after numeric literal");
10171068
}
@@ -1307,17 +1358,17 @@ litbufdup(core_yyscan_t yyscanner)
13071358
}
13081359

13091360
/*
1310-
* Process {integer}. Note this will also do the right thing with {decimal},
1311-
* ie digits and a decimal point.
1361+
* Process {decinteger}, {hexinteger}, etc. Note this will also do the right
1362+
* thing with {numeric}, ie digits and a decimal point.
13121363
*/
13131364
static int
1314-
process_integer_literal(const char *token, YYSTYPE *lval)
1365+
process_integer_literal(const char *token, YYSTYPE *lval, int base)
13151366
{
13161367
int val;
13171368
char *endptr;
13181369

13191370
errno = 0;
1320-
val = strtoint(token, &endptr, 10);
1371+
val = strtoint(base == 10 ? token : token + 2, &endptr, base);
13211372
if (*endptr != '\0' || errno == ERANGE)
13221373
{
13231374
/* integer too large (or contains decimal pt), treat it as a float */

0 commit comments

Comments
 (0)