1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Allow "_" characters to appear between any two digits in an integer, real or hexadecimal SQL literal.

FossilOrigin-Name: 0e6700f43f133510c8049b2c5a2610cb3be29da7ed4d39b1fa65dc22c4cf529b
This commit is contained in:
dan
2024-02-27 10:52:41 +00:00
12 changed files with 318 additions and 37 deletions

View File

@@ -1935,6 +1935,12 @@ filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
SPAN /* The span operator */
ERROR /* An expression containing an error */
.
term(A) ::= QNUMBER(X). {
A=tokenExpr(pParse,@X,X);
sqlite3DequoteNumber(pParse, A);
}
/* There must be no more than 255 tokens defined above. If this grammar
** is extended with new rules and tokens, they must either be so few in
** number that TK_SPAN is no more than 255, or else the new tokens must

View File

@@ -609,6 +609,8 @@
# define SQLITE_OMIT_ALTERTABLE
#endif
#define SQLITE_DIGIT_SEPARATOR '_'
/*
** Return true (non-zero) if the input is an integer that is too large
** to fit in 32-bits. This macro is used inside of various testcase()
@@ -4795,6 +4797,7 @@ int sqlite3ErrorToParser(sqlite3*,int);
void sqlite3Dequote(char*);
void sqlite3DequoteExpr(Expr*);
void sqlite3DequoteToken(Token*);
void sqlite3DequoteNumber(Parse*, Expr*);
void sqlite3TokenInit(Token*,char*);
int sqlite3KeywordCode(const unsigned char*, int);
int sqlite3RunParser(Parse*, const char*);

View File

@@ -437,27 +437,58 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_INTEGER;
#ifndef SQLITE_OMIT_HEX_INTEGER
if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
for(i=3; sqlite3Isxdigit(z[i]); i++){}
return i;
}
for(i=3; 1; i++){
if( sqlite3Isxdigit(z[i])==0 ){
if( z[i]==SQLITE_DIGIT_SEPARATOR ){
*tokenType = TK_QNUMBER;
}else{
break;
}
}
}
}else
#endif
for(i=0; sqlite3Isdigit(z[i]); i++){}
{
for(i=0; 1; i++){
if( sqlite3Isdigit(z[i])==0 ){
if( z[i]==SQLITE_DIGIT_SEPARATOR ){
*tokenType = TK_QNUMBER;
}else{
break;
}
}
}
#ifndef SQLITE_OMIT_FLOATING_POINT
if( z[i]=='.' ){
i++;
while( sqlite3Isdigit(z[i]) ){ i++; }
*tokenType = TK_FLOAT;
}
if( (z[i]=='e' || z[i]=='E') &&
( sqlite3Isdigit(z[i+1])
|| ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
)
){
i += 2;
while( sqlite3Isdigit(z[i]) ){ i++; }
*tokenType = TK_FLOAT;
}
if( z[i]=='.' ){
if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT;
for(i++; 1; i++){
if( sqlite3Isdigit(z[i])==0 ){
if( z[i]==SQLITE_DIGIT_SEPARATOR ){
*tokenType = TK_QNUMBER;
}else{
break;
}
}
}
}
if( (z[i]=='e' || z[i]=='E') &&
( sqlite3Isdigit(z[i+1])
|| ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
)
){
if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT;
for(i+=2; 1; i++){
if( sqlite3Isdigit(z[i])==0 ){
if( z[i]==SQLITE_DIGIT_SEPARATOR ){
*tokenType = TK_QNUMBER;
}else{
break;
}
}
}
}
#endif
}
while( IdChar(z[i]) ){
*tokenType = TK_ILLEGAL;
i++;
@@ -622,10 +653,13 @@ int sqlite3RunParser(Parse *pParse, const char *zSql){
if( tokenType>=TK_WINDOW ){
assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
|| tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
|| tokenType==TK_QNUMBER
);
#else
if( tokenType>=TK_SPACE ){
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL
|| tokenType==TK_QNUMBER
);
#endif /* SQLITE_OMIT_WINDOWFUNC */
if( AtomicLoad(&db->u1.isInterrupted) ){
pParse->rc = SQLITE_INTERRUPT;
@@ -658,7 +692,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql){
assert( n==6 );
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
}else{
}else if( tokenType!=TK_QNUMBER ){
Token x;
x.z = zSql;
x.n = n;

View File

@@ -311,6 +311,34 @@ void sqlite3DequoteExpr(Expr *p){
sqlite3Dequote(p->u.zToken);
}
/*
** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken
** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those
** that contain '_' characters that must be removed before further processing.
*/
void sqlite3DequoteNumber(Parse *pParse, Expr *p){
if( p ){
const char *pIn = p->u.zToken;
char *pOut = p->u.zToken;
int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X'));
assert( p->op==TK_QNUMBER );
p->op = TK_INTEGER;
do {
if( *pIn!=SQLITE_DIGIT_SEPARATOR ){
*pOut++ = *pIn;
if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT;
}else{
if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1])))
|| (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1])))
){
sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken);
}
}
}while( *pIn++ );
if( bHex ) p->op = TK_INTEGER;
}
}
/*
** If the input token p is quoted, try to adjust the token to remove
** the quotes. This is not always possible: