Annotation of embedaddon/php/ext/sqlite/libsqlite/src/tokenize.c, revision 1.1

1.1     ! misho       1: /*
        !             2: ** 2001 September 15
        !             3: **
        !             4: ** The author disclaims copyright to this source code.  In place of
        !             5: ** a legal notice, here is a blessing:
        !             6: **
        !             7: **    May you do good and not evil.
        !             8: **    May you find forgiveness for yourself and forgive others.
        !             9: **    May you share freely, never taking more than you give.
        !            10: **
        !            11: *************************************************************************
        !            12: ** An tokenizer for SQL
        !            13: **
        !            14: ** This file contains C code that splits an SQL input string up into
        !            15: ** individual tokens and sends those tokens one-by-one over to the
        !            16: ** parser for analysis.
        !            17: **
        !            18: ** $Id: tokenize.c 195361 2005-09-07 15:11:33Z iliaa $
        !            19: */
        !            20: #include "sqliteInt.h"
        !            21: #include "os.h"
        !            22: #include <ctype.h>
        !            23: #include <stdlib.h>
        !            24: 
        !            25: /*
        !            26: ** All the keywords of the SQL language are stored as in a hash
        !            27: ** table composed of instances of the following structure.
        !            28: */
        !            29: typedef struct Keyword Keyword;
        !            30: struct Keyword {
        !            31:   char *zName;             /* The keyword name */
        !            32:   u8 tokenType;            /* Token value for this keyword */
        !            33:   u8 len;                  /* Length of this keyword */
        !            34:   u8 iNext;                /* Index in aKeywordTable[] of next with same hash */
        !            35: };
        !            36: 
        !            37: /*
        !            38: ** These are the keywords
        !            39: */
        !            40: static Keyword aKeywordTable[] = {
        !            41:   { "ABORT",             TK_ABORT,        },
        !            42:   { "AFTER",             TK_AFTER,        },
        !            43:   { "ALL",               TK_ALL,          },
        !            44:   { "AND",               TK_AND,          },
        !            45:   { "AS",                TK_AS,           },
        !            46:   { "ASC",               TK_ASC,          },
        !            47:   { "ATTACH",            TK_ATTACH,       },
        !            48:   { "BEFORE",            TK_BEFORE,       },
        !            49:   { "BEGIN",             TK_BEGIN,        },
        !            50:   { "BETWEEN",           TK_BETWEEN,      },
        !            51:   { "BY",                TK_BY,           },
        !            52:   { "CASCADE",           TK_CASCADE,      },
        !            53:   { "CASE",              TK_CASE,         },
        !            54:   { "CHECK",             TK_CHECK,        },
        !            55:   { "CLUSTER",           TK_CLUSTER,      },
        !            56:   { "COLLATE",           TK_COLLATE,      },
        !            57:   { "COMMIT",            TK_COMMIT,       },
        !            58:   { "CONFLICT",          TK_CONFLICT,     },
        !            59:   { "CONSTRAINT",        TK_CONSTRAINT,   },
        !            60:   { "COPY",              TK_COPY,         },
        !            61:   { "CREATE",            TK_CREATE,       },
        !            62:   { "CROSS",             TK_JOIN_KW,      },
        !            63:   { "DATABASE",          TK_DATABASE,     },
        !            64:   { "DEFAULT",           TK_DEFAULT,      },
        !            65:   { "DEFERRED",          TK_DEFERRED,     },
        !            66:   { "DEFERRABLE",        TK_DEFERRABLE,   },
        !            67:   { "DELETE",            TK_DELETE,       },
        !            68:   { "DELIMITERS",        TK_DELIMITERS,   },
        !            69:   { "DESC",              TK_DESC,         },
        !            70:   { "DETACH",            TK_DETACH,       },
        !            71:   { "DISTINCT",          TK_DISTINCT,     },
        !            72:   { "DROP",              TK_DROP,         },
        !            73:   { "END",               TK_END,          },
        !            74:   { "EACH",              TK_EACH,         },
        !            75:   { "ELSE",              TK_ELSE,         },
        !            76:   { "EXCEPT",            TK_EXCEPT,       },
        !            77:   { "EXPLAIN",           TK_EXPLAIN,      },
        !            78:   { "FAIL",              TK_FAIL,         },
        !            79:   { "FOR",               TK_FOR,          },
        !            80:   { "FOREIGN",           TK_FOREIGN,      },
        !            81:   { "FROM",              TK_FROM,         },
        !            82:   { "FULL",              TK_JOIN_KW,      },
        !            83:   { "GLOB",              TK_GLOB,         },
        !            84:   { "GROUP",             TK_GROUP,        },
        !            85:   { "HAVING",            TK_HAVING,       },
        !            86:   { "IGNORE",            TK_IGNORE,       },
        !            87:   { "IMMEDIATE",         TK_IMMEDIATE,    },
        !            88:   { "IN",                TK_IN,           },
        !            89:   { "INDEX",             TK_INDEX,        },
        !            90:   { "INITIALLY",         TK_INITIALLY,    },
        !            91:   { "INNER",             TK_JOIN_KW,      },
        !            92:   { "INSERT",            TK_INSERT,       },
        !            93:   { "INSTEAD",           TK_INSTEAD,      },
        !            94:   { "INTERSECT",         TK_INTERSECT,    },
        !            95:   { "INTO",              TK_INTO,         },
        !            96:   { "IS",                TK_IS,           },
        !            97:   { "ISNULL",            TK_ISNULL,       },
        !            98:   { "JOIN",              TK_JOIN,         },
        !            99:   { "KEY",               TK_KEY,          },
        !           100:   { "LEFT",              TK_JOIN_KW,      },
        !           101:   { "LIKE",              TK_LIKE,         },
        !           102:   { "LIMIT",             TK_LIMIT,        },
        !           103:   { "MATCH",             TK_MATCH,        },
        !           104:   { "NATURAL",           TK_JOIN_KW,      },
        !           105:   { "NOT",               TK_NOT,          },
        !           106:   { "NOTNULL",           TK_NOTNULL,      },
        !           107:   { "NULL",              TK_NULL,         },
        !           108:   { "OF",                TK_OF,           },
        !           109:   { "OFFSET",            TK_OFFSET,       },
        !           110:   { "ON",                TK_ON,           },
        !           111:   { "OR",                TK_OR,           },
        !           112:   { "ORDER",             TK_ORDER,        },
        !           113:   { "OUTER",             TK_JOIN_KW,      },
        !           114:   { "PRAGMA",            TK_PRAGMA,       },
        !           115:   { "PRIMARY",           TK_PRIMARY,      },
        !           116:   { "RAISE",             TK_RAISE,        },
        !           117:   { "REFERENCES",        TK_REFERENCES,   },
        !           118:   { "REPLACE",           TK_REPLACE,      },
        !           119:   { "RESTRICT",          TK_RESTRICT,     },
        !           120:   { "RIGHT",             TK_JOIN_KW,      },
        !           121:   { "ROLLBACK",          TK_ROLLBACK,     },
        !           122:   { "ROW",               TK_ROW,          },
        !           123:   { "SELECT",            TK_SELECT,       },
        !           124:   { "SET",               TK_SET,          },
        !           125:   { "STATEMENT",         TK_STATEMENT,    },
        !           126:   { "TABLE",             TK_TABLE,        },
        !           127:   { "TEMP",              TK_TEMP,         },
        !           128:   { "TEMPORARY",         TK_TEMP,         },
        !           129:   { "THEN",              TK_THEN,         },
        !           130:   { "TRANSACTION",       TK_TRANSACTION,  },
        !           131:   { "TRIGGER",           TK_TRIGGER,      },
        !           132:   { "UNION",             TK_UNION,        },
        !           133:   { "UNIQUE",            TK_UNIQUE,       },
        !           134:   { "UPDATE",            TK_UPDATE,       },
        !           135:   { "USING",             TK_USING,        },
        !           136:   { "VACUUM",            TK_VACUUM,       },
        !           137:   { "VALUES",            TK_VALUES,       },
        !           138:   { "VIEW",              TK_VIEW,         },
        !           139:   { "WHEN",              TK_WHEN,         },
        !           140:   { "WHERE",             TK_WHERE,        },
        !           141: };
        !           142: 
        !           143: /*
        !           144: ** This is the hash table
        !           145: */
        !           146: #define KEY_HASH_SIZE 101
        !           147: static u8 aiHashTable[KEY_HASH_SIZE];
        !           148: 
        !           149: 
        !           150: /*
        !           151: ** This function looks up an identifier to determine if it is a
        !           152: ** keyword.  If it is a keyword, the token code of that keyword is 
        !           153: ** returned.  If the input is not a keyword, TK_ID is returned.
        !           154: */
        !           155: int sqliteKeywordCode(const char *z, int n){
        !           156:   int h, i;
        !           157:   Keyword *p;
        !           158:   static char needInit = 1;
        !           159:   if( needInit ){
        !           160:     /* Initialize the keyword hash table */
        !           161:     sqliteOsEnterMutex();
        !           162:     if( needInit ){
        !           163:       int nk;
        !           164:       nk = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
        !           165:       for(i=0; i<nk; i++){
        !           166:         aKeywordTable[i].len = strlen(aKeywordTable[i].zName);
        !           167:         h = sqliteHashNoCase(aKeywordTable[i].zName, aKeywordTable[i].len);
        !           168:         h %= KEY_HASH_SIZE;
        !           169:         aKeywordTable[i].iNext = aiHashTable[h];
        !           170:         aiHashTable[h] = i+1;
        !           171:       }
        !           172:       needInit = 0;
        !           173:     }
        !           174:     sqliteOsLeaveMutex();
        !           175:   }
        !           176:   h = sqliteHashNoCase(z, n) % KEY_HASH_SIZE;
        !           177:   for(i=aiHashTable[h]; i; i=p->iNext){
        !           178:     p = &aKeywordTable[i-1];
        !           179:     if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){
        !           180:       return p->tokenType;
        !           181:     }
        !           182:   }
        !           183:   return TK_ID;
        !           184: }
        !           185: 
        !           186: 
        !           187: /*
        !           188: ** If X is a character that can be used in an identifier and
        !           189: ** X&0x80==0 then isIdChar[X] will be 1.  If X&0x80==0x80 then
        !           190: ** X is always an identifier character.  (Hence all UTF-8
        !           191: ** characters can be part of an identifier).  isIdChar[X] will
        !           192: ** be 0 for every character in the lower 128 ASCII characters
        !           193: ** that cannot be used as part of an identifier.
        !           194: **
        !           195: ** In this implementation, an identifier can be a string of
        !           196: ** alphabetic characters, digits, and "_" plus any character
        !           197: ** with the high-order bit set.  The latter rule means that
        !           198: ** any sequence of UTF-8 characters or characters taken from
        !           199: ** an extended ISO8859 character set can form an identifier.
        !           200: */
        !           201: static const char isIdChar[] = {
        !           202: /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
        !           203:     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x */
        !           204:     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 1x */
        !           205:     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 2x */
        !           206:     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  /* 3x */
        !           207:     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 4x */
        !           208:     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  /* 5x */
        !           209:     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 6x */
        !           210:     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 7x */
        !           211: };
        !           212: 
        !           213: 
        !           214: /*
        !           215: ** Return the length of the token that begins at z[0]. 
        !           216: ** Store the token type in *tokenType before returning.
        !           217: */
        !           218: static int sqliteGetToken(const unsigned char *z, int *tokenType){
        !           219:   int i;
        !           220:   switch( *z ){
        !           221:     case ' ': case '\t': case '\n': case '\f': case '\r': {
        !           222:       for(i=1; isspace(z[i]); i++){}
        !           223:       *tokenType = TK_SPACE;
        !           224:       return i;
        !           225:     }
        !           226:     case '-': {
        !           227:       if( z[1]=='-' ){
        !           228:         for(i=2; z[i] && z[i]!='\n'; i++){}
        !           229:         *tokenType = TK_COMMENT;
        !           230:         return i;
        !           231:       }
        !           232:       *tokenType = TK_MINUS;
        !           233:       return 1;
        !           234:     }
        !           235:     case '(': {
        !           236:       *tokenType = TK_LP;
        !           237:       return 1;
        !           238:     }
        !           239:     case ')': {
        !           240:       *tokenType = TK_RP;
        !           241:       return 1;
        !           242:     }
        !           243:     case ';': {
        !           244:       *tokenType = TK_SEMI;
        !           245:       return 1;
        !           246:     }
        !           247:     case '+': {
        !           248:       *tokenType = TK_PLUS;
        !           249:       return 1;
        !           250:     }
        !           251:     case '*': {
        !           252:       *tokenType = TK_STAR;
        !           253:       return 1;
        !           254:     }
        !           255:     case '/': {
        !           256:       if( z[1]!='*' || z[2]==0 ){
        !           257:         *tokenType = TK_SLASH;
        !           258:         return 1;
        !           259:       }
        !           260:       for(i=3; z[i] && (z[i]!='/' || z[i-1]!='*'); i++){}
        !           261:       if( z[i] ) i++;
        !           262:       *tokenType = TK_COMMENT;
        !           263:       return i;
        !           264:     }
        !           265:     case '%': {
        !           266:       *tokenType = TK_REM;
        !           267:       return 1;
        !           268:     }
        !           269:     case '=': {
        !           270:       *tokenType = TK_EQ;
        !           271:       return 1 + (z[1]=='=');
        !           272:     }
        !           273:     case '<': {
        !           274:       if( z[1]=='=' ){
        !           275:         *tokenType = TK_LE;
        !           276:         return 2;
        !           277:       }else if( z[1]=='>' ){
        !           278:         *tokenType = TK_NE;
        !           279:         return 2;
        !           280:       }else if( z[1]=='<' ){
        !           281:         *tokenType = TK_LSHIFT;
        !           282:         return 2;
        !           283:       }else{
        !           284:         *tokenType = TK_LT;
        !           285:         return 1;
        !           286:       }
        !           287:     }
        !           288:     case '>': {
        !           289:       if( z[1]=='=' ){
        !           290:         *tokenType = TK_GE;
        !           291:         return 2;
        !           292:       }else if( z[1]=='>' ){
        !           293:         *tokenType = TK_RSHIFT;
        !           294:         return 2;
        !           295:       }else{
        !           296:         *tokenType = TK_GT;
        !           297:         return 1;
        !           298:       }
        !           299:     }
        !           300:     case '!': {
        !           301:       if( z[1]!='=' ){
        !           302:         *tokenType = TK_ILLEGAL;
        !           303:         return 2;
        !           304:       }else{
        !           305:         *tokenType = TK_NE;
        !           306:         return 2;
        !           307:       }
        !           308:     }
        !           309:     case '|': {
        !           310:       if( z[1]!='|' ){
        !           311:         *tokenType = TK_BITOR;
        !           312:         return 1;
        !           313:       }else{
        !           314:         *tokenType = TK_CONCAT;
        !           315:         return 2;
        !           316:       }
        !           317:     }
        !           318:     case ',': {
        !           319:       *tokenType = TK_COMMA;
        !           320:       return 1;
        !           321:     }
        !           322:     case '&': {
        !           323:       *tokenType = TK_BITAND;
        !           324:       return 1;
        !           325:     }
        !           326:     case '~': {
        !           327:       *tokenType = TK_BITNOT;
        !           328:       return 1;
        !           329:     }
        !           330:     case '\'': case '"': {
        !           331:       int delim = z[0];
        !           332:       for(i=1; z[i]; i++){
        !           333:         if( z[i]==delim ){
        !           334:           if( z[i+1]==delim ){
        !           335:             i++;
        !           336:           }else{
        !           337:             break;
        !           338:           }
        !           339:         }
        !           340:       }
        !           341:       if( z[i] ) i++;
        !           342:       *tokenType = TK_STRING;
        !           343:       return i;
        !           344:     }
        !           345:     case '.': {
        !           346:       *tokenType = TK_DOT;
        !           347:       return 1;
        !           348:     }
        !           349:     case '0': case '1': case '2': case '3': case '4':
        !           350:     case '5': case '6': case '7': case '8': case '9': {
        !           351:       *tokenType = TK_INTEGER;
        !           352:       for(i=1; isdigit(z[i]); i++){}
        !           353:       if( z[i]=='.' && isdigit(z[i+1]) ){
        !           354:         i += 2;
        !           355:         while( isdigit(z[i]) ){ i++; }
        !           356:         *tokenType = TK_FLOAT;
        !           357:       }
        !           358:       if( (z[i]=='e' || z[i]=='E') &&
        !           359:            ( isdigit(z[i+1]) 
        !           360:             || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
        !           361:            )
        !           362:       ){
        !           363:         i += 2;
        !           364:         while( isdigit(z[i]) ){ i++; }
        !           365:         *tokenType = TK_FLOAT;
        !           366:       }
        !           367:       return i;
        !           368:     }
        !           369:     case '[': {
        !           370:       for(i=1; z[i] && z[i-1]!=']'; i++){}
        !           371:       *tokenType = TK_ID;
        !           372:       return i;
        !           373:     }
        !           374:     case '?': {
        !           375:       *tokenType = TK_VARIABLE;
        !           376:       return 1;
        !           377:     }
        !           378:     default: {
        !           379:       if( (*z&0x80)==0 && !isIdChar[*z] ){
        !           380:         break;
        !           381:       }
        !           382:       for(i=1; (z[i]&0x80)!=0 || isIdChar[z[i]]; i++){}
        !           383:       *tokenType = sqliteKeywordCode((char*)z, i);
        !           384:       return i;
        !           385:     }
        !           386:   }
        !           387:   *tokenType = TK_ILLEGAL;
        !           388:   return 1;
        !           389: }
        !           390: 
        !           391: /*
        !           392: ** Run the parser on the given SQL string.  The parser structure is
        !           393: ** passed in.  An SQLITE_ status code is returned.  If an error occurs
        !           394: ** and pzErrMsg!=NULL then an error message might be written into 
        !           395: ** memory obtained from malloc() and *pzErrMsg made to point to that
        !           396: ** error message.  Or maybe not.
        !           397: */
        !           398: int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
        !           399:   int nErr = 0;
        !           400:   int i;
        !           401:   void *pEngine;
        !           402:   int tokenType;
        !           403:   int lastTokenParsed = -1;
        !           404:   sqlite *db = pParse->db;
        !           405:   extern void *sqliteParserAlloc(void*(*)(int));
        !           406:   extern void sqliteParserFree(void*, void(*)(void*));
        !           407:   extern int sqliteParser(void*, int, Token, Parse*);
        !           408: 
        !           409:   db->flags &= ~SQLITE_Interrupt;
        !           410:   pParse->rc = SQLITE_OK;
        !           411:   i = 0;
        !           412:   pEngine = sqliteParserAlloc((void*(*)(int))malloc);
        !           413:   if( pEngine==0 ){
        !           414:     sqliteSetString(pzErrMsg, "out of memory", (char*)0);
        !           415:     return 1;
        !           416:   }
        !           417:   pParse->sLastToken.dyn = 0;
        !           418:   pParse->zTail = zSql;
        !           419:   while( sqlite_malloc_failed==0 && zSql[i]!=0 ){
        !           420:     assert( i>=0 );
        !           421:     pParse->sLastToken.z = &zSql[i];
        !           422:     assert( pParse->sLastToken.dyn==0 );
        !           423:     pParse->sLastToken.n = sqliteGetToken((unsigned char*)&zSql[i], &tokenType);
        !           424:     i += pParse->sLastToken.n;
        !           425:     switch( tokenType ){
        !           426:       case TK_SPACE:
        !           427:       case TK_COMMENT: {
        !           428:         if( (db->flags & SQLITE_Interrupt)!=0 ){
        !           429:           pParse->rc = SQLITE_INTERRUPT;
        !           430:           sqliteSetString(pzErrMsg, "interrupt", (char*)0);
        !           431:           goto abort_parse;
        !           432:         }
        !           433:         break;
        !           434:       }
        !           435:       case TK_ILLEGAL: {
        !           436:         sqliteSetNString(pzErrMsg, "unrecognized token: \"", -1, 
        !           437:            pParse->sLastToken.z, pParse->sLastToken.n, "\"", 1, 0);
        !           438:         nErr++;
        !           439:         goto abort_parse;
        !           440:       }
        !           441:       case TK_SEMI: {
        !           442:         pParse->zTail = &zSql[i];
        !           443:         /* Fall thru into the default case */
        !           444:       }
        !           445:       default: {
        !           446:         sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);
        !           447:         lastTokenParsed = tokenType;
        !           448:         if( pParse->rc!=SQLITE_OK ){
        !           449:           goto abort_parse;
        !           450:         }
        !           451:         break;
        !           452:       }
        !           453:     }
        !           454:   }
        !           455: abort_parse:
        !           456:   if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){
        !           457:     if( lastTokenParsed!=TK_SEMI ){
        !           458:       sqliteParser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
        !           459:       pParse->zTail = &zSql[i];
        !           460:     }
        !           461:     sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
        !           462:   }
        !           463:   sqliteParserFree(pEngine, free);
        !           464:   if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
        !           465:     sqliteSetString(&pParse->zErrMsg, sqlite_error_string(pParse->rc),
        !           466:                     (char*)0);
        !           467:   }
        !           468:   if( pParse->zErrMsg ){
        !           469:     if( pzErrMsg && *pzErrMsg==0 ){
        !           470:       *pzErrMsg = pParse->zErrMsg;
        !           471:     }else{
        !           472:       sqliteFree(pParse->zErrMsg);
        !           473:     }
        !           474:     pParse->zErrMsg = 0;
        !           475:     if( !nErr ) nErr++;
        !           476:   }
        !           477:   if( pParse->pVdbe && pParse->nErr>0 ){
        !           478:     sqliteVdbeDelete(pParse->pVdbe);
        !           479:     pParse->pVdbe = 0;
        !           480:   }
        !           481:   if( pParse->pNewTable ){
        !           482:     sqliteDeleteTable(pParse->db, pParse->pNewTable);
        !           483:     pParse->pNewTable = 0;
        !           484:   }
        !           485:   if( pParse->pNewTrigger ){
        !           486:     sqliteDeleteTrigger(pParse->pNewTrigger);
        !           487:     pParse->pNewTrigger = 0;
        !           488:   }
        !           489:   if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
        !           490:     pParse->rc = SQLITE_ERROR;
        !           491:   }
        !           492:   return nErr;
        !           493: }
        !           494: 
        !           495: /*
        !           496: ** Token types used by the sqlite_complete() routine.  See the header
        !           497: ** comments on that procedure for additional information.
        !           498: */
        !           499: #define tkEXPLAIN 0
        !           500: #define tkCREATE  1
        !           501: #define tkTEMP    2
        !           502: #define tkTRIGGER 3
        !           503: #define tkEND     4
        !           504: #define tkSEMI    5
        !           505: #define tkWS      6
        !           506: #define tkOTHER   7
        !           507: 
        !           508: /*
        !           509: ** Return TRUE if the given SQL string ends in a semicolon.
        !           510: **
        !           511: ** Special handling is require for CREATE TRIGGER statements.
        !           512: ** Whenever the CREATE TRIGGER keywords are seen, the statement
        !           513: ** must end with ";END;".
        !           514: **
        !           515: ** This implementation uses a state machine with 7 states:
        !           516: **
        !           517: **   (0) START     At the beginning or end of an SQL statement.  This routine
        !           518: **                 returns 1 if it ends in the START state and 0 if it ends
        !           519: **                 in any other state.
        !           520: **
        !           521: **   (1) EXPLAIN   The keyword EXPLAIN has been seen at the beginning of 
        !           522: **                 a statement.
        !           523: **
        !           524: **   (2) CREATE    The keyword CREATE has been seen at the beginning of a
        !           525: **                 statement, possibly preceeded by EXPLAIN and/or followed by
        !           526: **                 TEMP or TEMPORARY
        !           527: **
        !           528: **   (3) NORMAL    We are in the middle of statement which ends with a single
        !           529: **                 semicolon.
        !           530: **
        !           531: **   (4) TRIGGER   We are in the middle of a trigger definition that must be
        !           532: **                 ended by a semicolon, the keyword END, and another semicolon.
        !           533: **
        !           534: **   (5) SEMI      We've seen the first semicolon in the ";END;" that occurs at
        !           535: **                 the end of a trigger definition.
        !           536: **
        !           537: **   (6) END       We've seen the ";END" of the ";END;" that occurs at the end
        !           538: **                 of a trigger difinition.
        !           539: **
        !           540: ** Transitions between states above are determined by tokens extracted
        !           541: ** from the input.  The following tokens are significant:
        !           542: **
        !           543: **   (0) tkEXPLAIN   The "explain" keyword.
        !           544: **   (1) tkCREATE    The "create" keyword.
        !           545: **   (2) tkTEMP      The "temp" or "temporary" keyword.
        !           546: **   (3) tkTRIGGER   The "trigger" keyword.
        !           547: **   (4) tkEND       The "end" keyword.
        !           548: **   (5) tkSEMI      A semicolon.
        !           549: **   (6) tkWS        Whitespace
        !           550: **   (7) tkOTHER     Any other SQL token.
        !           551: **
        !           552: ** Whitespace never causes a state transition and is always ignored.
        !           553: */
        !           554: int sqlite_complete(const char *zSql){
        !           555:   u8 state = 0;   /* Current state, using numbers defined in header comment */
        !           556:   u8 token;       /* Value of the next token */
        !           557: 
        !           558:   /* The following matrix defines the transition from one state to another
        !           559:   ** according to what token is seen.  trans[state][token] returns the
        !           560:   ** next state.
        !           561:   */
        !           562:   static const u8 trans[7][8] = {
        !           563:                      /* Token:                                                */
        !           564:      /* State:       **  EXPLAIN  CREATE  TEMP  TRIGGER  END  SEMI  WS  OTHER */
        !           565:      /* 0   START: */ {       1,      2,    3,       3,   3,    0,  0,     3, },
        !           566:      /* 1 EXPLAIN: */ {       3,      2,    3,       3,   3,    0,  1,     3, },
        !           567:      /* 2  CREATE: */ {       3,      3,    2,       4,   3,    0,  2,     3, },
        !           568:      /* 3  NORMAL: */ {       3,      3,    3,       3,   3,    0,  3,     3, },
        !           569:      /* 4 TRIGGER: */ {       4,      4,    4,       4,   4,    5,  4,     4, },
        !           570:      /* 5    SEMI: */ {       4,      4,    4,       4,   6,    5,  5,     4, },
        !           571:      /* 6     END: */ {       4,      4,    4,       4,   4,    0,  6,     4, },
        !           572:   };
        !           573: 
        !           574:   while( *zSql ){
        !           575:     switch( *zSql ){
        !           576:       case ';': {  /* A semicolon */
        !           577:         token = tkSEMI;
        !           578:         break;
        !           579:       }
        !           580:       case ' ':
        !           581:       case '\r':
        !           582:       case '\t':
        !           583:       case '\n':
        !           584:       case '\f': {  /* White space is ignored */
        !           585:         token = tkWS;
        !           586:         break;
        !           587:       }
        !           588:       case '/': {   /* C-style comments */
        !           589:         if( zSql[1]!='*' ){
        !           590:           token = tkOTHER;
        !           591:           break;
        !           592:         }
        !           593:         zSql += 2;
        !           594:         while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; }
        !           595:         if( zSql[0]==0 ) return 0;
        !           596:         zSql++;
        !           597:         token = tkWS;
        !           598:         break;
        !           599:       }
        !           600:       case '-': {   /* SQL-style comments from "--" to end of line */
        !           601:         if( zSql[1]!='-' ){
        !           602:           token = tkOTHER;
        !           603:           break;
        !           604:         }
        !           605:         while( *zSql && *zSql!='\n' ){ zSql++; }
        !           606:         if( *zSql==0 ) return state==0;
        !           607:         token = tkWS;
        !           608:         break;
        !           609:       }
        !           610:       case '[': {   /* Microsoft-style identifiers in [...] */
        !           611:         zSql++;
        !           612:         while( *zSql && *zSql!=']' ){ zSql++; }
        !           613:         if( *zSql==0 ) return 0;
        !           614:         token = tkOTHER;
        !           615:         break;
        !           616:       }
        !           617:       case '"':     /* single- and double-quoted strings */
        !           618:       case '\'': {
        !           619:         int c = *zSql;
        !           620:         zSql++;
        !           621:         while( *zSql && *zSql!=c ){ zSql++; }
        !           622:         if( *zSql==0 ) return 0;
        !           623:         token = tkOTHER;
        !           624:         break;
        !           625:       }
        !           626:       default: {
        !           627:         if( isIdChar[(u8)*zSql] ){
        !           628:           /* Keywords and unquoted identifiers */
        !           629:           int nId;
        !           630:           for(nId=1; isIdChar[(u8)zSql[nId]]; nId++){}
        !           631:           switch( *zSql ){
        !           632:             case 'c': case 'C': {
        !           633:               if( nId==6 && sqliteStrNICmp(zSql, "create", 6)==0 ){
        !           634:                 token = tkCREATE;
        !           635:               }else{
        !           636:                 token = tkOTHER;
        !           637:               }
        !           638:               break;
        !           639:             }
        !           640:             case 't': case 'T': {
        !           641:               if( nId==7 && sqliteStrNICmp(zSql, "trigger", 7)==0 ){
        !           642:                 token = tkTRIGGER;
        !           643:               }else if( nId==4 && sqliteStrNICmp(zSql, "temp", 4)==0 ){
        !           644:                 token = tkTEMP;
        !           645:               }else if( nId==9 && sqliteStrNICmp(zSql, "temporary", 9)==0 ){
        !           646:                 token = tkTEMP;
        !           647:               }else{
        !           648:                 token = tkOTHER;
        !           649:               }
        !           650:               break;
        !           651:             }
        !           652:             case 'e':  case 'E': {
        !           653:               if( nId==3 && sqliteStrNICmp(zSql, "end", 3)==0 ){
        !           654:                 token = tkEND;
        !           655:               }else if( nId==7 && sqliteStrNICmp(zSql, "explain", 7)==0 ){
        !           656:                 token = tkEXPLAIN;
        !           657:               }else{
        !           658:                 token = tkOTHER;
        !           659:               }
        !           660:               break;
        !           661:             }
        !           662:             default: {
        !           663:               token = tkOTHER;
        !           664:               break;
        !           665:             }
        !           666:           }
        !           667:           zSql += nId-1;
        !           668:         }else{
        !           669:           /* Operators and special symbols */
        !           670:           token = tkOTHER;
        !           671:         }
        !           672:         break;
        !           673:       }
        !           674:     }
        !           675:     state = trans[state][token];
        !           676:     zSql++;
        !           677:   }
        !           678:   return state==0;
        !           679: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>