static void IdChars (void) /*@modifies nextToken@*/ { IdChar (); InitReduce (IDCHARS1); while (ltoken_getCode (nextToken) != LLT_EOL) { IdChar (); InitReduce (IDCHARS2); } }
static void IdChars (void) { IdChar (); InitReduce (IDCHARS1); while (ltoken_getCode (nextToken) != LST_EOL) { IdChar (); InitReduce (IDCHARS2); } }
CTempString CObjectIStreamAsn::ScanEndOfId(bool isId) { if ( isId ) { for ( size_t i = 1; ; ++i ) { char c = m_Input.PeekCharNoEOF(i); if ( !IdChar(c) && (c != '-' || !IdChar(m_Input.PeekChar(i + 1))) ) { const char* ptr = m_Input.GetCurrentPos(); m_Input.SkipChars(i); return CTempString(ptr, i); } } } return CTempString(); }
void CObjectIStreamAsn::ReadNull(void) { if ( SkipWhiteSpace() == 'N' && m_Input.PeekCharNoEOF(1) == 'U' && m_Input.PeekCharNoEOF(2) == 'L' && m_Input.PeekCharNoEOF(3) == 'L' && !IdChar(m_Input.PeekCharNoEOF(4)) ) { m_Input.SkipChars(4); } else ThrowError(fFormatError, "'NULL' expected"); }
gboolean _string_is_identifier (const gchar *str) { const gchar *ptr; gchar *endptr; gchar c; if (!str || !(*str)) return FALSE; if ((*str == '"') || (*str == '`')) ptr = str + 1; else ptr = str; for (; IdChar(*ptr) || (*ptr == '*') || (*ptr == '.') || (*ptr == '-') || ((*ptr == '"') && (ptr[1] == '"')) || (((*ptr == '"') || (*ptr == '`')) && ptr[1] == 0); ptr++) { if ((*ptr == '"') && (ptr[1] == '"')) ptr++; } if (*ptr) return FALSE; if (((*str == '"') && (ptr[-1] == '"')) || ((*str == '`') && (ptr[-1] == '`'))) return TRUE; /* @str is composed only of character that can be used in an identifier */ g_ascii_strtod (str, &endptr); if (!*endptr) /* @str is a number */ return FALSE; return TRUE; }
/* ** Return the length (in bytes) of the token that begins at z[0]. ** Store the token type in *tokenType before returning. */ int sqlite3GetToken(const unsigned char *z, int *tokenType){ int i, c; switch( aiClass[*z] ){ /* Switch on the character-class of the first byte ** of the token. See the comment on the CC_ defines ** above. */ case CC_SPACE: { testcase( z[0]==' ' ); testcase( z[0]=='\t' ); testcase( z[0]=='\n' ); testcase( z[0]=='\f' ); testcase( z[0]=='\r' ); for(i=1; sqlite3Isspace(z[i]); i++){} *tokenType = TK_SPACE; return i; } case CC_MINUS: { if( z[1]=='-' ){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } *tokenType = TK_MINUS; return 1; } case CC_LP: { *tokenType = TK_LP; return 1; } case CC_RP: { *tokenType = TK_RP; return 1; } case CC_SEMI: { *tokenType = TK_SEMI; return 1; } case CC_PLUS: { *tokenType = TK_PLUS; return 1; } case CC_STAR: { *tokenType = TK_STAR; return 1; } case CC_SLASH: { if( z[1]!='*' || z[2]==0 ){ *tokenType = TK_SLASH; return 1; } for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } case CC_PERCENT: { *tokenType = TK_REM; return 1; } case CC_EQ: { *tokenType = TK_EQ; return 1 + (z[1]=='='); } case CC_LT: { if( (c=z[1])=='=' ){ *tokenType = TK_LE; return 2; }else if( c=='>' ){ *tokenType = TK_NE; return 2; }else if( c=='<' ){ *tokenType = TK_LSHIFT; return 2; }else{ *tokenType = TK_LT; return 1; } } case CC_GT: { if( (c=z[1])=='=' ){ *tokenType = TK_GE; return 2; }else if( c=='>' ){ *tokenType = TK_RSHIFT; return 2; }else{ *tokenType = TK_GT; return 1; } } case CC_BANG: { if( z[1]!='=' ){ *tokenType = TK_ILLEGAL; return 1; }else{ *tokenType = TK_NE; return 2; } } case CC_PIPE: { if( z[1]!='|' ){ *tokenType = TK_BITOR; return 1; }else{ *tokenType = TK_CONCAT; return 2; } } case CC_COMMA: { *tokenType = TK_COMMA; return 1; } case CC_AND: { *tokenType = TK_BITAND; return 1; } case CC_TILDA: { *tokenType = TK_BITNOT; return 1; } case CC_QUOTE: { int delim = z[0]; testcase( delim=='`' ); testcase( delim=='\'' ); testcase( delim=='"' ); for(i=1; (c=z[i])!=0; i++){ if( c==delim ){ if( z[i+1]==delim ){ i++; }else{ break; } } } if( c=='\'' ){ *tokenType = TK_STRING; return i+1; }else if( c!=0 ){ *tokenType = TK_ID; return i+1; }else{ *tokenType = TK_ILLEGAL; return i; } } case CC_DOT: { #ifndef SQLITE_OMIT_FLOATING_POINT if( !sqlite3Isdigit(z[1]) ) #endif { *tokenType = TK_DOT; return 1; } /* If the next character is a digit, this is a floating point ** number that begins with ".". Fall thru into the next case */ } case CC_DIGIT: { testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); *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; } #endif for(i=0; sqlite3Isdigit(z[i]); i++){} #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; } #endif while( IdChar(z[i]) ){ *tokenType = TK_ILLEGAL; i++; } return i; } case CC_QUOTE2: { for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} *tokenType = c==']' ? TK_ID : TK_ILLEGAL; return i; } case CC_VARNUM: { *tokenType = TK_VARIABLE; for(i=1; sqlite3Isdigit(z[i]); i++){} return i; } case CC_DOLLAR: case CC_VARALPHA: { int n = 0; testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' ); testcase( z[0]=='#' ); *tokenType = TK_VARIABLE; for(i=1; (c=z[i])!=0; i++){ if( IdChar(c) ){ n++; #ifndef SQLITE_OMIT_TCL_VARIABLE }else if( c=='(' && n>0 ){ do{ i++; }while( (c=z[i])!=0 && !sqlite3Isspace(c) && c!=')' ); if( c==')' ){ i++; }else{ *tokenType = TK_ILLEGAL; } break; }else if( c==':' && z[i+1]==':' ){ i++; #endif }else{ break; } } if( n==0 ) *tokenType = TK_ILLEGAL; return i; } case CC_KYWD: { for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} if( IdChar(z[i]) ){ /* This token started out using characters that can appear in keywords, ** but z[i] is a character not allowed within keywords, so this must ** be an identifier instead */ i++; break; } *tokenType = TK_ID; return keywordCode((char*)z, i, tokenType); } case CC_X: { #ifndef SQLITE_OMIT_BLOB_LITERAL testcase( z[0]=='x' ); testcase( z[0]=='X' ); if( z[1]=='\'' ){ *tokenType = TK_BLOB; for(i=2; sqlite3Isxdigit(z[i]); i++){} if( z[i]!='\'' || i%2 ){ *tokenType = TK_ILLEGAL; while( z[i] && z[i]!='\'' ){ i++; } } if( z[i] ) i++; return i; } #endif /* If it is not a BLOB literal, then it must be an ID, since no ** SQL keywords start with the letter 'x'. Fall through */ } case CC_ID: { i = 1; break; } case CC_NUL: { *tokenType = TK_ILLEGAL; return 0; } default: { *tokenType = TK_ILLEGAL; return 1; } } while( IdChar(z[i]) ){ i++; } *tokenType = TK_ID; return i; }
/* Make the IdChar function accessible from ctime.c and alter.c */ int sqlite3IsIdChar(u8 c){ return IdChar(c); }
/* ** Return the length of the token that begins at z[0]. ** Store the token type in *tokenType before returning. */ static int getToken(const unsigned char *z, int *tokenType){ int i, c; switch( *z ){ case ' ': case '\t': case '\n': case '\f': case '\r': { for(i=1; isspace(z[i]); i++){} *tokenType = TK_SPACE; return i; } case '-': { if( z[1]=='-' ){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_COMMENT; return i; } *tokenType = TK_MINUS; return 1; } case '(': { *tokenType = TK_LP; return 1; } case ')': { *tokenType = TK_RP; return 1; } case ';': { *tokenType = TK_SEMI; return 1; } case '+': { *tokenType = TK_PLUS; return 1; } case '*': { *tokenType = TK_STAR; return 1; } case '/': { if( z[1]!='*' || z[2]==0 ){ *tokenType = TK_SLASH; return 1; } for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; *tokenType = TK_COMMENT; return i; } case '%': { *tokenType = TK_REM; return 1; } case '=': { *tokenType = TK_EQ; return 1 + (z[1]=='='); } case '<': { if( (c=z[1])=='=' ){ *tokenType = TK_LE; return 2; }else if( c=='>' ){ *tokenType = TK_NE; return 2; }else if( c=='<' ){ *tokenType = TK_LSHIFT; return 2; }else{ *tokenType = TK_LT; return 1; } } case '>': { if( (c=z[1])=='=' ){ *tokenType = TK_GE; return 2; }else if( c=='>' ){ *tokenType = TK_RSHIFT; return 2; }else{ *tokenType = TK_GT; return 1; } } case '!': { if( z[1]!='=' ){ *tokenType = TK_ILLEGAL; return 2; }else{ *tokenType = TK_NE; return 2; } } case '|': { if( z[1]!='|' ){ *tokenType = TK_BITOR; return 1; }else{ *tokenType = TK_CONCAT; return 2; } } case ',': { *tokenType = TK_COMMA; return 1; } case '&': { *tokenType = TK_BITAND; return 1; } case '~': { *tokenType = TK_BITNOT; return 1; } case '`': case '\'': case '"': { int delim = z[0]; for(i=1; (c=z[i])!=0; i++){ if( c==delim ){ if( z[i+1]==delim ){ i++; }else{ break; } } } if( c ){ *tokenType = TK_STRING; return i+1; }else{ *tokenType = TK_ILLEGAL; return i; } } case '.': { #ifndef SQLITE_OMIT_FLOATING_POINT if( !isdigit(z[1]) ) #endif { *tokenType = TK_DOT; return 1; } /* If the next character is a digit, this is a floating point ** number that begins with ".". Fall thru into the next case */ } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { *tokenType = TK_INTEGER; for(i=0; isdigit(z[i]); i++){} #ifndef SQLITE_OMIT_FLOATING_POINT if( z[i]=='.' ){ i++; while( isdigit(z[i]) ){ i++; } *tokenType = TK_FLOAT; } if( (z[i]=='e' || z[i]=='E') && ( isdigit(z[i+1]) || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2])) ) ){ i += 2; while( isdigit(z[i]) ){ i++; } *tokenType = TK_FLOAT; } #endif return i; } case '[': { for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} *tokenType = TK_ID; return i; } case '?': { *tokenType = TK_VARIABLE; for(i=1; isdigit(z[i]); i++){} return i; } case '#': { for(i=1; isdigit(z[i]); i++){} if( i>1 ){ /* Parameters of the form #NNN (where NNN is a number) are used ** internally by sqlite3NestedParse. */ *tokenType = TK_REGISTER; return i; } /* Fall through into the next case if the '#' is not followed by ** a digit. Try to match #AAAA where AAAA is a parameter name. */ } #ifndef SQLITE_OMIT_TCL_VARIABLE case '$': #endif case '@': case ':': { /* For compatibility with MS SQL Server */ int n = 0; if ( z[0] == '@' && z[1] == '@') { *tokenType = TK_VARIABLE1; i = 2; } else { *tokenType = TK_VARIABLE; i = 1; } for(; (c=z[i])!=0; i++){ if( IdChar(c) ){ n++; #ifndef SQLITE_OMIT_TCL_VARIABLE }else if( c=='(' && n>0 ){ do{ i++; }while( (c=z[i])!=0 && !isspace(c) && c!=')' ); if( c==')' ){ i++; }else{ *tokenType = TK_ILLEGAL; } break; }else if( c==':' && z[i+1]==':' ){ i++; #endif } else{ break; } } if( n==0 ) *tokenType = TK_ILLEGAL; return i; } #ifndef SQLITE_OMIT_BLOB_LITERAL case 'x': case 'X': { if( (c=z[1])=='\'' || c=='"' ){ int delim = c; *tokenType = TK_BLOB; for(i=2; (c=z[i])!=0; i++){ if( c==delim ){ if( i%2 ) *tokenType = TK_ILLEGAL; break; } if( !isxdigit(c) ){ *tokenType = TK_ILLEGAL; return i; } } if( c ) i++; return i; } /* Otherwise fall through to the next case */ } #endif default: { if( !IdChar(*z) ){ break; } for(i=1; IdChar(z[i]) || isdigit(z[i]) ; i++){} *tokenType = keywordCode((char*)z, i); return i; } } *tokenType = TK_ILLEGAL; return 1; }
/* ** Return the length of the token that begins at z[0]. ** Store the token type in *tokenType before returning. */ static int getToken(const unsigned char *z, int *tokenType){ int i, c; switch( *z ){ case ' ': case '\t': case '\n': case '\f': case '\r': { for(i=1; isspace(z[i]); i++){} *tokenType = TK_SPACE; return i; } case '-': { if( z[1]=='-' ){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_COMMENT; return i; } *tokenType = TK_MINUS; return 1; } case '(': { *tokenType = TK_LP; return 1; } case ')': { *tokenType = TK_RP; return 1; } case ';': { *tokenType = TK_SEMI; return 1; } case '+': { *tokenType = TK_PLUS; return 1; } case '*': { *tokenType = TK_STAR; return 1; } case '/': { if( z[1]!='*' || z[2]==0 ){ *tokenType = TK_SLASH; return 1; } for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; *tokenType = TK_COMMENT; return i; } case '%': { *tokenType = TK_REM; return 1; } case '=': { *tokenType = TK_EQ; return 1 + (z[1]=='='); } case '<': { if( (c=z[1])=='=' ){ *tokenType = TK_LE; return 2; }else if( c=='>' ){ *tokenType = TK_NE; return 2; }else if( c=='<' ){ *tokenType = TK_LSHIFT; return 2; }else{ *tokenType = TK_LT; return 1; } } case '>': { if( (c=z[1])=='=' ){ *tokenType = TK_GE; return 2; }else if( c=='>' ){ *tokenType = TK_RSHIFT; return 2; }else{ *tokenType = TK_GT; return 1; } } case '!': { if( z[1]!='=' ){ *tokenType = TK_ILLEGAL; return 2; }else{ *tokenType = TK_NE; return 2; } } case '|': { if( z[1]!='|' ){ *tokenType = TK_BITOR; return 1; }else{ *tokenType = TK_CONCAT; return 2; } } case ',': { *tokenType = TK_COMMA; return 1; } case '&': { *tokenType = TK_BITAND; return 1; } case '~': { *tokenType = TK_BITNOT; return 1; } case '#': { for(i=1; isdigit(z[i]) || (i==1 && z[1]=='-'); i++){} *tokenType = TK_REGISTER; return i; } case '\'': case '"': { int delim = z[0]; for(i=1; (c=z[i])!=0; i++){ if( c==delim ){ if( z[i+1]==delim ){ i++; }else{ break; } } } if( c ) i++; *tokenType = TK_STRING; return i; } case '.': { *tokenType = TK_DOT; return 1; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { *tokenType = TK_INTEGER; for(i=1; isdigit(z[i]); i++){} #ifndef SQLITE_OMIT_FLOATING_POINT if( z[i]=='.' && isdigit(z[i+1]) ){ i += 2; while( isdigit(z[i]) ){ i++; } *tokenType = TK_FLOAT; } if( (z[i]=='e' || z[i]=='E') && ( isdigit(z[i+1]) || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2])) ) ){ i += 2; while( isdigit(z[i]) ){ i++; } *tokenType = TK_FLOAT; } #endif return i; } case '[': { for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} *tokenType = TK_ID; return i; } case '?': { *tokenType = TK_VARIABLE; for(i=1; isdigit(z[i]); i++){} return i; } case ':': { for(i=1; IdChar(z[i]); i++){} *tokenType = i>1 ? TK_VARIABLE : TK_ILLEGAL; return i; } #ifndef SQLITE_OMIT_TCL_VARIABLE case '$': { *tokenType = TK_VARIABLE; if( z[1]=='{' ){ int nBrace = 1; for(i=2; (c=z[i])!=0 && nBrace; i++){ if( c=='{' ){ nBrace++; }else if( c=='}' ){ nBrace--; } } if( c==0 ) *tokenType = TK_ILLEGAL; }else{ int n = 0; for(i=1; (c=z[i])!=0; i++){ if( isalnum(c) || c=='_' ){ n++; }else if( c=='(' && n>0 ){ do{ i++; }while( (c=z[i])!=0 && !isspace(c) && c!=')' ); if( c==')' ){ i++; }else{ *tokenType = TK_ILLEGAL; } break; }else if( c==':' && z[i+1]==':' ){ i++; }else{ break; } } if( n==0 ) *tokenType = TK_ILLEGAL; } return i; } #endif #ifndef SQLITE_OMIT_BLOB_LITERAL case 'x': case 'X': { if( (c=z[1])=='\'' || c=='"' ){ int delim = c; *tokenType = TK_BLOB; for(i=2; (c=z[i])!=0; i++){ if( c==delim ){ if( i%2 ) *tokenType = TK_ILLEGAL; break; } if( !isxdigit(c) ){ *tokenType = TK_ILLEGAL; return i; } } if( c ) i++; return i; } /* Otherwise fall through to the next case */ } #endif default: { if( !IdChar(*z) ){ break; } for(i=1; IdChar(z[i]); i++){} *tokenType = keywordCode((char*)z, i); return i; } } *tokenType = TK_ILLEGAL; return 1; }
/* ** Return TRUE if the given SQL string ends in a semicolon. ** ** Special handling is require for CREATE TRIGGER statements. ** Whenever the CREATE TRIGGER keywords are seen, the statement ** must end with ";END;". ** ** This implementation uses a state machine with 7 states: ** ** (0) START At the beginning or end of an SQL statement. This routine ** returns 1 if it ends in the START state and 0 if it ends ** in any other state. ** ** (1) NORMAL We are in the middle of statement which ends with a single ** semicolon. ** ** (2) EXPLAIN The keyword EXPLAIN has been seen at the beginning of ** a statement. ** ** (3) CREATE The keyword CREATE has been seen at the beginning of a ** statement, possibly preceeded by EXPLAIN and/or followed by ** TEMP or TEMPORARY ** ** (4) TRIGGER We are in the middle of a trigger definition that must be ** ended by a semicolon, the keyword END, and another semicolon. ** ** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at ** the end of a trigger definition. ** ** (6) END We've seen the ";END" of the ";END;" that occurs at the end ** of a trigger difinition. ** ** Transitions between states above are determined by tokens extracted ** from the input. The following tokens are significant: ** ** (0) tkSEMI A semicolon. ** (1) tkWS Whitespace ** (2) tkOTHER Any other SQL token. ** (3) tkEXPLAIN The "explain" keyword. ** (4) tkCREATE The "create" keyword. ** (5) tkTEMP The "temp" or "temporary" keyword. ** (6) tkTRIGGER The "trigger" keyword. ** (7) tkEND The "end" keyword. ** ** Whitespace never causes a state transition and is always ignored. ** ** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed ** to recognize the end of a trigger can be omitted. All we have to do ** is look for a semicolon that is not part of an string or comment. */ int sqlite3_complete(const char *zSql){ u8 state = 0; /* Current state, using numbers defined in header comment */ u8 token; /* Value of the next token */ #ifndef SQLITE_OMIT_TRIGGER /* A complex statement machine used to detect the end of a CREATE TRIGGER ** statement. This is the normal case. */ static const u8 trans[7][8] = { /* Token: */ /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ /* 0 START: */ { 0, 0, 1, 2, 3, 1, 1, 1, }, /* 1 NORMAL: */ { 0, 1, 1, 1, 1, 1, 1, 1, }, /* 2 EXPLAIN: */ { 0, 2, 1, 1, 3, 1, 1, 1, }, /* 3 CREATE: */ { 0, 3, 1, 1, 1, 3, 4, 1, }, /* 4 TRIGGER: */ { 5, 4, 4, 4, 4, 4, 4, 4, }, /* 5 SEMI: */ { 5, 5, 4, 4, 4, 4, 4, 6, }, /* 6 END: */ { 0, 6, 4, 4, 4, 4, 4, 4, }, }; #else /* If triggers are not suppored by this compile then the statement machine ** used to detect the end of a statement is much simplier */ static const u8 trans[2][3] = { /* Token: */ /* State: ** SEMI WS OTHER */ /* 0 START: */ { 0, 0, 1, }, /* 1 NORMAL: */ { 0, 1, 1, }, }; #endif /* SQLITE_OMIT_TRIGGER */ while( *zSql ){ switch( *zSql ){ case ';': { /* A semicolon */ token = tkSEMI; break; } case ' ': case '\r': case '\t': case '\n': case '\f': { /* White space is ignored */ token = tkWS; break; } case '/': { /* C-style comments */ if( zSql[1]!='*' ){ token = tkOTHER; break; } zSql += 2; while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; } if( zSql[0]==0 ) return 0; zSql++; token = tkWS; break; } case '-': { /* SQL-style comments from "--" to end of line */ if( zSql[1]!='-' ){ token = tkOTHER; break; } while( *zSql && *zSql!='\n' ){ zSql++; } if( *zSql==0 ) return state==0; token = tkWS; break; } case '[': { /* Microsoft-style identifiers in [...] */ zSql++; while( *zSql && *zSql!=']' ){ zSql++; } if( *zSql==0 ) return 0; token = tkOTHER; break; } case '"': /* single- and double-quoted strings */ case '\'': { int c = *zSql; zSql++; while( *zSql && *zSql!=c ){ zSql++; } if( *zSql==0 ) return 0; token = tkOTHER; break; } default: { int c; if( IdChar((u8)*zSql) ){ /* Keywords and unquoted identifiers */ int nId; for(nId=1; IdChar(zSql[nId]); nId++){} #ifdef SQLITE_OMIT_TRIGGER token = tkOTHER; #else switch( *zSql ){ case 'c': case 'C': { if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){ token = tkCREATE; }else{ token = tkOTHER; } break; } case 't': case 'T': { if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){ token = tkTRIGGER; }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){ token = tkTEMP; }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){ token = tkTEMP; }else{ token = tkOTHER; } break; } case 'e': case 'E': { if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){ token = tkEND; }else #ifndef SQLITE_OMIT_EXPLAIN if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){ token = tkEXPLAIN; }else #endif { token = tkOTHER; } break; } default: { token = tkOTHER; break; } } #endif /* SQLITE_OMIT_TRIGGER */ zSql += nId-1; }else{ /* Operators and special symbols */ token = tkOTHER; } break; } } state = trans[state][token]; zSql++; } return state==0; }
/* ** Return the length of the token that begins at z[0]. ** Store the token type in *tokenType before returning. */ int sqlite3GetToken(const unsigned char *z, int *tokenType){ int i, c; switch( *z ){ case ' ': case '\t': case '\n': case '\f': case '\r': { testcase( z[0]==' ' ); testcase( z[0]=='\t' ); testcase( z[0]=='\n' ); testcase( z[0]=='\f' ); testcase( z[0]=='\r' ); for(i=1; sqlite3Isspace(z[i]); i++){} *tokenType = TK_SPACE; return i; } case '-': { if( z[1]=='-' ){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } *tokenType = TK_MINUS; return 1; } case '(': { *tokenType = TK_LP; return 1; } case ')': { *tokenType = TK_RP; return 1; } case ';': { *tokenType = TK_SEMI; return 1; } case '+': { *tokenType = TK_PLUS; return 1; } case '*': { *tokenType = TK_STAR; return 1; } case '/': { if( z[1]!='*' || z[2]==0 ){ *tokenType = TK_SLASH; return 1; } for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } case '%': { *tokenType = TK_REM; return 1; } case '=': { *tokenType = TK_EQ; return 1 + (z[1]=='='); } case '<': { if( (c=z[1])=='=' ){ *tokenType = TK_LE; return 2; }else if( c=='>' ){ *tokenType = TK_NE; return 2; }else if( c=='<' ){ *tokenType = TK_LSHIFT; return 2; }else{ *tokenType = TK_LT; return 1; } } case '>': { if( (c=z[1])=='=' ){ *tokenType = TK_GE; return 2; }else if( c=='>' ){ *tokenType = TK_RSHIFT; return 2; }else{ *tokenType = TK_GT; return 1; } } case '!': { if( z[1]!='=' ){ *tokenType = TK_ILLEGAL; return 2; }else{ *tokenType = TK_NE; return 2; } } case '|': { if( z[1]!='|' ){ *tokenType = TK_BITOR; return 1; }else{ *tokenType = TK_CONCAT; return 2; } } case ',': { *tokenType = TK_COMMA; return 1; } case '&': { *tokenType = TK_BITAND; return 1; } case '~': { *tokenType = TK_BITNOT; return 1; } case '`': case '\'': case '"': { int delim = z[0]; testcase( delim=='`' ); testcase( delim=='\'' ); testcase( delim=='"' ); for(i=1; (c=z[i])!=0; i++){ if( c==delim ){ if( z[i+1]==delim ){ i++; }else{ break; } } } if( c=='\'' ){ *tokenType = TK_STRING; return i+1; }else if( c!=0 ){ *tokenType = TK_ID; return i+1; }else{ *tokenType = TK_ILLEGAL; return i; } } case '.': { #ifndef SQLITE_OMIT_FLOATING_POINT if( !sqlite3Isdigit(z[1]) ) #endif { *tokenType = TK_DOT; return 1; } /* If the next character is a digit, this is a floating point ** number that begins with ".". Fall thru into the next case */ } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); *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; } #endif for(i=0; sqlite3Isdigit(z[i]); i++){} #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; } #endif while( IdChar(z[i]) ){ *tokenType = TK_ILLEGAL; i++; } return i; } case '[': { for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} *tokenType = c==']' ? TK_ID : TK_ILLEGAL; return i; } case '?': { *tokenType = TK_VARIABLE; for(i=1; sqlite3Isdigit(z[i]); i++){} return i; } #ifndef SQLITE_OMIT_TCL_VARIABLE case '$': #endif case '@': /* For compatibility with MS SQL Server */ case '#': case ':': { int n = 0; testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' ); testcase( z[0]=='#' ); *tokenType = TK_VARIABLE; for(i=1; (c=z[i])!=0; i++){ if( IdChar(c) ){ n++; #ifndef SQLITE_OMIT_TCL_VARIABLE }else if( c=='(' && n>0 ){ do{ i++; }while( (c=z[i])!=0 && !sqlite3Isspace(c) && c!=')' ); if( c==')' ){ i++; }else{ *tokenType = TK_ILLEGAL; } break; }else if( c==':' && z[i+1]==':' ){ i++; #endif }else{ break; } } if( n==0 ) *tokenType = TK_ILLEGAL; return i; } #ifndef SQLITE_OMIT_BLOB_LITERAL case 'x': case 'X': { testcase( z[0]=='x' ); testcase( z[0]=='X' ); if( z[1]=='\'' ){ *tokenType = TK_BLOB; for(i=2; sqlite3Isxdigit(z[i]); i++){} if( z[i]!='\'' || i%2 ){ *tokenType = TK_ILLEGAL; while( z[i] && z[i]!='\'' ){ i++; } } if( z[i] ) i++; return i; } /* Otherwise fall through to the next case */ } #endif default: { if( !IdChar(*z) ){ break; } for(i=1; IdChar(z[i]); i++){} *tokenType = keywordCode((char*)z, i); return i; } } *tokenType = TK_ILLEGAL; return 1; }
/* ** Return TRUE if the given SQL string ends in a semicolon. ** Special handling is require for CREATE TRIGGER statements. Whenever the CREATE TRIGGER keywords are seen, the statement must end with ";END;". ** This implementation uses a state machine with 8 states: ** ** (0) INVALID We have not yet seen a non-whitespace character. ** (1) START At the beginning or end of an SQL statement. This routine returns 1 if it ends in the START state and 0 if it ends ** in any other state. ** (2) NORMAL We are in the middle of statement which ends with a single semicolon. ** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of a statement. ** (4) CREATE The keyword CREATE has been seen at the beginning of a statement, possibly preceeded by EXPLAIN and/or followed by ** TEMP or TEMPORARY ** (5) TRIGGER We are in the middle of a trigger definition that must be ended by a semicolon, the keyword END, and another semicolon. ** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at the end of a trigger definition. ** (7) END We've seen the ";END" of the ";END;" that occurs at the end of a trigger difinition. ** ** Transitions between states above are determined by tokens extracted from the input. The following tokens are significant: ** ** (0) tkSEMI A semicolon. ** (1) tkWS Whitespace. ** (2) tkOTHER Any other SQL token. ** (3) tkEXPLAIN The "explain" keyword. ** (4) tkCREATE The "create" keyword. ** (5) tkTEMP The "temp" or "temporary" keyword. ** (6) tkTRIGGER The "trigger" keyword. ** (7) tkEND The "end" keyword. ** ** Whitespace never causes a state transition and is always ignored. This means that a SQL string of all whitespace is invalid. ** ** If we compile with SYSTEM_OMIT_TRIGGER, all of the computation needed to recognize the end of a trigger can be omitted. All we have to do ** is look for a semicolon that is not part of an string or comment. */ int parse_complete(const char *zSql) { u8 state = 0; // Current state, using numbers defined in header comment u8 token; // Value of the next token #ifndef PARSE_OMIT_TRIGGER // A complex statement machine used to detect the end of a CREATE TRIGGER statement. This is the normal case. static const u8 trans[8][8] = { /* Token: */ /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ /* 0 INVALID: */ { 1, 0, 2, 3, 4, 2, 2, 2, }, /* 1 START: */ { 1, 1, 2, 3, 4, 2, 2, 2, }, /* 2 NORMAL: */ { 1, 2, 2, 2, 2, 2, 2, 2, }, /* 3 EXPLAIN: */ { 1, 3, 3, 2, 4, 2, 2, 2, }, /* 4 CREATE: */ { 1, 4, 2, 2, 2, 4, 5, 2, }, /* 5 TRIGGER: */ { 6, 5, 5, 5, 5, 5, 5, 5, }, /* 6 SEMI: */ { 6, 6, 5, 5, 5, 5, 5, 7, }, /* 7 END: */ { 1, 7, 5, 5, 5, 5, 5, 5, }, }; #else // If triggers are not supported by this compile then the statement machine used to detect the end of a statement is much simplier static const u8 trans[3][3] = { /* Token: */ /* State: ** SEMI WS OTHER */ /* 0 INVALID: */ { 1, 0, 2, }, /* 1 START: */ { 1, 1, 2, }, /* 2 NORMAL: */ { 1, 2, 2, }, }; #endif while (*zSql) { switch (*zSql) { case ';': { // A semicolon token = tkSEMI; break; } case ' ': case '\r': case '\t': case '\n': case '\f': { // White space is ignored token = tkWS; break; } case '/': { // C-style comments if (zSql[1]!='*') { token = tkOTHER; break; } zSql += 2; while (zSql[0] && (zSql[0]!='*' || zSql[1]!='/')) zSql++; if (zSql[0]==0) return 0; zSql++; token = tkWS; break; } case '-': { // SQL-style comments from "--" to end of line if (zSql[1]!='-') { token = tkOTHER; break; } while (*zSql && *zSql!='\n') zSql++; if (*zSql==0) return (state==1); token = tkWS; break; } case '[': { // Microsoft-style identifiers in [...] zSql++; while (*zSql && *zSql!=']') zSql++; if (*zSql==0) return 0; token = tkOTHER; break; } case '`': case '"': case '\'': { // Grave-accent quoted symbols used by MySQL or single- and double-quoted strings int c = *zSql; zSql++; while (*zSql && *zSql!=c) zSql++; if (*zSql==0) return 0; token = tkOTHER; break; } default: { #ifdef SYSTEM_EBCDIC unsigned char c; #endif if (IdChar((u8)*zSql)) { // Keywords and unquoted identifiers int nId; for (nId=1; IdChar(zSql[nId]); nId++) {} #ifdef PARSE_OMIT_TRIGGER token = tkOTHER; #else switch (*zSql) { case 'c': case 'C': { if (nId==6 && systemStrNICmp(zSql, "create", 6)==0) token = tkCREATE; else token = tkOTHER; break; } case 't': case 'T': { if (nId==7 && systemStrNICmp(zSql, "trigger", 7)==0) token = tkTRIGGER; else if (nId==4 && systemStrNICmp(zSql, "temp", 4)==0) token = tkTEMP; else if (nId==9 && systemStrNICmp(zSql, "temporary", 9)==0) token = tkTEMP; else token = tkOTHER; break; } case 'e': case 'E': { if (nId==3 && systemStrNICmp(zSql, "end", 3)==0) token = tkEND; else #ifndef SYSTEM_OMIT_EXPLAIN if (nId==7 && systemStrNICmp(zSql, "explain", 7)==0) token = tkEXPLAIN; else #endif token = tkOTHER; break; } default: { token = tkOTHER; break; } } #endif /* PARSE_OMIT_TRIGGER */ zSql += nId-1; } else // Operators and special symbols token = tkOTHER; break; } } state = trans[state][token]; zSql++; } return (state==1); }