]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix deficiencies in sqlite_complete() pointed out by R. Dennis Cote. (CVS 955)
authordrh <drh@noemail.net>
Sun, 4 May 2003 17:58:25 +0000 (17:58 +0000)
committerdrh <drh@noemail.net>
Sun, 4 May 2003 17:58:25 +0000 (17:58 +0000)
FossilOrigin-Name: 54b33a5ed9f7a89435c2f1395a3177e8c778bb8a

manifest
manifest.uuid
src/main.c
src/tokenize.c
test/main.test

index 1c0bb018fa9dabbcb396ff0f13150e075cb1c883..baccd627ce187a12502161a4118c43de69acd283 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Added\sshell\scommand\s".databases"\sto\slist\sname\sand\sfile\sof\sopen\sones.\nAdded\sseveral\smissing\sshell\scommands.\s(CVS\s954)
-D 2003-05-04T07:31:10
+C Fix\sdeficiencies\sin\ssqlite_complete()\spointed\sout\sby\sR.\sDennis\sCote.\s(CVS\s955)
+D 2003-05-04T17:58:26
 F Makefile.in 004acec253ecdde985c8ecd5b7c9accdb210378f
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -33,7 +33,7 @@ F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
 F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
 F src/insert.c c230a8c216f6788095d46f0e270406a93aae45af
-F src/main.c 5265058c9a598b4714dc9e528152b81fcb31e7ad
+F src/main.c 16e68905793b118552a9cf43a9f77ca1d9e395a9
 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
 F src/os.c 94b618c0c0a76210e53857d77c96d2f042dc33b1
 F src/os.h 9e5bbddff123187295e3d00d49af06192cd1cd49
@@ -54,7 +54,7 @@ F src/test1.c 4596acd9d9f2a49fda0160a8a6dee5bfc7c6c325
 F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
 F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
 F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
-F src/tokenize.c 067d1a477a94af7712ca74e09aaa6bd0f7299527
+F src/tokenize.c 65b99ca0c4b8bc89f9d4530762288eea7c5bca62
 F src/trigger.c 8ee811986080de60d9d883ad96daffea82014f27
 F src/update.c dc3b680b4cf2cb6d839950b68d632850746639b9
 F src/util.c 87635cfdfffa056a8d3147719357aa442374f78c
@@ -90,7 +90,7 @@ F test/ioerr.test 5dbaf09f96b56ee01cf3edd762b96eb4ad2c9ca4
 F test/join.test c97267c19294bf1fa4e81087edad179828bced88
 F test/limit.test 9ffb965a0f5bf7152187ef3d8d1249b96e5620bf
 F test/lock.test 388a3a10962d2d571c0c1821cc35bf069ee73473
-F test/main.test a028743affca67670e24c97527d1f4ad4bc2aad3
+F test/main.test 6a851b5992c4881a725a3d9647e629199df8de9d
 F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd
 F test/memdb.test 3acf1453a5705d1aa524d3027667ca3d9c77c69f
 F test/memleak.test a18e6810cae96d2f6f5136920267adbefc8e1e90
@@ -165,7 +165,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
 F www/sqlite.tcl ffde644361e1d8e2a44a235ff23ad3b43d640df2
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 741a5a8d3975fb5db18914b7879b12aead59279b
-R dd130a1f7dd3e8a6528c790b087edc44
-U jplyon
-Z 8e9e207bfa04e253d2be1e2c73c48742
+P dd57d6ae6a247824e44a6073bc7e73ecb3c500fd
+R afd858f78403413353ccd46911cf14bc
+U drh
+Z 317ec3dddb2f5501f438a98dd3716c3d
index f23493adb54f94b4542b0357a9bd5ddd6f48b900..209be605fb645eb00a4794a8cb5851d2fc184703 100644 (file)
@@ -1 +1 @@
-dd57d6ae6a247824e44a6073bc7e73ecb3c500fd
\ No newline at end of file
+54b33a5ed9f7a89435c2f1395a3177e8c778bb8a
\ No newline at end of file
index f02c61de225773dfda7135874ddd5a73c4c03dd3..d7c71f7e51f63ba6cc652991eed2b66e352f1564 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.129 2003/04/29 16:20:46 drh Exp $
+** $Id: main.c,v 1.130 2003/05/04 17:58:26 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -553,132 +553,6 @@ void sqlite_close(sqlite *db){
   sqliteFree(db);
 }
 
-/*
-** 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;".
-*/
-int sqlite_complete(const char *zSql){
-  int isComplete = 1;
-  int requireEnd = 0;
-  int seenText = 0;
-  int seenCreate = 0;
-  while( *zSql ){
-    switch( *zSql ){
-      case ';': {
-        isComplete = 1;
-        seenText = 1;
-        seenCreate = 0;
-        break;
-      }
-      case ' ':
-      case '\r':
-      case '\t':
-      case '\n':
-      case '\f': {
-        break;
-      }
-      case '[': {
-        isComplete = 0;
-        seenText = 1;
-        seenCreate = 0;
-        zSql++;
-        while( *zSql && *zSql!=']' ){ zSql++; }
-        if( *zSql==0 ) return 0;
-        break;
-      }
-      case '"':
-      case '\'': {
-        int c = *zSql;
-        isComplete = 0;
-        seenText = 1;
-        seenCreate = 0;
-        zSql++;
-        while( *zSql && *zSql!=c ){ zSql++; }
-        if( *zSql==0 ) return 0;
-        break;
-      }
-      case '/': {
-        if( zSql[1]!='*' ){
-          isComplete = 0;
-          seenText = 1;
-          seenCreate = 0;
-          break;
-        }
-        zSql += 2;
-        while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; }
-        if( zSql[0]==0 ) return 0;
-        zSql += 2;
-        break;
-      }
-      case '-': {
-        if( zSql[1]!='-' ){
-          isComplete = 0;
-          seenCreate = 0;
-          break;
-        }
-        while( *zSql && *zSql!='\n' ){ zSql++; }
-        if( *zSql==0 ) return seenText && isComplete && requireEnd==0;
-        break;
-      }
-      case 'c':
-      case 'C': {
-        seenText = 1;
-        if( !isComplete ) break;
-        isComplete = 0;
-        if( sqliteStrNICmp(zSql, "create", 6)!=0 ) break;
-        if( !isspace(zSql[6]) ) break;
-        zSql += 5;
-        seenCreate = 1;
-        while( isspace(zSql[1]) ) zSql++;
-        if( sqliteStrNICmp(&zSql[1],"trigger", 7)!=0 ) break;
-        zSql += 7;
-        requireEnd++;
-        break;
-      }
-      case 't':
-      case 'T': {
-        seenText = 1;
-        if( !seenCreate ) break;
-        seenCreate = 0;
-        isComplete = 0;
-        if( sqliteStrNICmp(zSql, "trigger", 7)!=0 ) break;
-        if( !isspace(zSql[7]) ) break;
-        zSql += 6;
-        requireEnd++;
-        break;
-      }
-      case 'e':
-      case 'E': {
-        seenCreate = 0;
-        seenText = 1;
-        if( !isComplete ) break;
-        isComplete = 0;
-        if( requireEnd==0 ) break;
-        if( sqliteStrNICmp(zSql, "end", 3)!=0 ) break;
-        zSql += 2;
-        while( isspace(zSql[1]) ) zSql++;
-        if( zSql[1]==';' ){
-          zSql++;
-          isComplete = 1;
-          requireEnd--;
-        }
-        break;
-      }
-      default: {
-        seenCreate = 0;
-        seenText = 1;
-        isComplete = 0;
-        break;
-      }
-    }
-    zSql++;
-  }
-  return /* seenText && */ isComplete && requireEnd==0;
-}
-
 /*
 ** Rollback all database files.
 */
index 290c84a2ae010d485c07793c4e4586320ab21327..652a96596da867043e1552baf07a77828818efff 100644 (file)
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.58 2003/04/21 18:48:47 drh Exp $
+** $Id: tokenize.c,v 1.59 2003/05/04 17:58:26 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -496,3 +496,189 @@ abort_parse:
   }
   return nErr;
 }
+
+/*
+** Token types used by the sqlite_complete() routine.  See the header
+** comments on that procedure for additional information.
+*/
+#define tkEXPLAIN 0
+#define tkCREATE  1
+#define tkTEMP    2
+#define tkTRIGGER 3
+#define tkEND     4
+#define tkSEMI    5
+#define tkWS      6
+#define tkOTHER   7
+
+/*
+** 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) EXPLAIN   The keyword EXPLAIN has been seen at the beginning of 
+**                 a statement.
+**
+**   (2) CREATE    The keyword CREATE has been seen at the beginning of a
+**                 statement, possibly preceeded by EXPLAIN and/or followed by
+**                 TEMP or TEMPORARY
+**
+**   (3) NORMAL    We are in the middle of statement which ends with a single
+**                 semicolon.
+**
+**   (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) tkEXPLAIN   The "explain" keyword.
+**   (1) tkCREATE    The "create" keyword.
+**   (2) tkTEMP      The "temp" or "temporary" keyword.
+**   (3) tkTRIGGER   The "trigger" keyword.
+**   (4) tkEND       The "end" keyword.
+**   (5) tkSEMI      A semicolon.
+**   (6) tkWS        Whitespace
+**   (7) tkOTHER     Any other SQL token.
+**
+** Whitespace never causes a state transition and is always ignored.
+*/
+int sqlite_complete(const char *zSql){
+  u8 state = 0;   /* Current state, using numbers defined in header comment */
+  u8 token;       /* Value of the next token */
+
+  /* The following matrix defines the transition from one state to another
+  ** according to what token is seen.  trans[state][token] returns the
+  ** next state.
+  */
+  static const u8 trans[7][8] = {
+                     /* Token:
+     /* State:       **  EXPLAIN  CREATE  TEMP  TRIGGER  END  SEMI  WS  OTHER */
+     /* 0   START: */ {       1,      2,    3,       3,   3,    0,  0,     3, },
+     /* 1 EXPLAIN: */ {       3,      2,    3,       3,   3,    0,  1,     3, },
+     /* 2  CREATE: */ {       3,      3,    2,       4,   3,    0,  2,     3, },
+     /* 3  NORMAL: */ {       3,      3,    3,       3,   3,    0,  3,     3, },
+     /* 4 TRIGGER: */ {       4,      4,    4,       4,   4,    5,  4,     4, },
+     /* 5    SEMI: */ {       4,      4,    4,       4,   6,    5,  5,     4, },
+     /* 6     END: */ {       4,      4,    4,       4,   4,    0,  6,     4, },
+  };
+
+  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: {
+        if( isIdChar[*zSql] ){
+          /* Keywords and unquoted identifiers */
+          int nId;
+          for(nId=1; isIdChar[zSql[nId]]; nId++){}
+          switch( *zSql ){
+            case 'c': case 'C': {
+              if( nId==6 && sqliteStrNICmp(zSql, "create", 6)==0 ){
+                token = tkCREATE;
+              }else{
+                token = tkOTHER;
+              }
+              break;
+            }
+            case 't': case 'T': {
+              if( nId==7 && sqliteStrNICmp(zSql, "trigger", 7)==0 ){
+                token = tkTRIGGER;
+              }else if( nId==4 && sqliteStrNICmp(zSql, "temp", 4)==0 ){
+                token = tkTEMP;
+              }else if( nId==9 && sqliteStrNICmp(zSql, "temporary", 9)==0 ){
+                token = tkTEMP;
+              }else{
+                token = tkOTHER;
+              }
+              break;
+            }
+            case 'e':  case 'E': {
+              if( nId==3 && sqliteStrNICmp(zSql, "end", 3)==0 ){
+                token = tkEND;
+              }else if( nId==7 && sqliteStrNICmp(zSql, "explain", 7)==0 ){
+                token = tkEXPLAIN;
+              }else{
+                token = tkOTHER;
+              }
+              break;
+            }
+            default: {
+              token = tkOTHER;
+              break;
+            }
+          }
+          zSql += nId-1;
+        }else{
+          /* Operators and special symbols */
+          token = tkOTHER;
+        }
+        break;
+      }
+    }
+    state = trans[state][token];
+    zSql++;
+  }
+  return state==0;
+}
index 9efbe310636c1f82f9fe2320946a18100fbc65a8..2b98ace0c3d1084630619d9a1f89f12d651dd663 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is exercising the code in main.c.
 #
-# $Id: main.test,v 1.13 2003/04/26 02:31:54 drh Exp $
+# $Id: main.test,v 1.14 2003/05/04 17:58:27 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -138,19 +138,46 @@ do_test main-1.26 {
        UPDATE pqr SET a=5;
   }
 } {0}
-do_test main-1.27 {
+do_test main-1.27.1 {
   db complete {
     CREATE -- a comment
     TRIGGERX xyz AFTER DELETE backend BEGIN
        UPDATE pqr SET a=5;
   }
 } {1}
+do_test main-1.27.2 {
+  db complete {
+    CREATE/**/TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {0}
+do_test main-1.27.3 {
+  db complete {
+    /* */ EXPLAIN -- A comment
+    CREATE/**/TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {0}
+do_test main-1.27.4 {
+  db complete {
+    BOGUS token
+    CREATE  TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {1}
+do_test main-1.27.5 {
+  db complete {
+    EXPLAIN 
+    CREATE TEMP TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {0}
 do_test main-1.28 {
   db complete {
     CREATE TEMP TRIGGER xyz AFTER DELETE backend BEGIN
        UPDATE pqr SET a=5;
   }
-} {1}
+} {0}
 do_test main-1.29 {
   db complete {
     CREATE TRIGGER xyz AFTER DELETE backend BEGIN
@@ -168,6 +195,11 @@ do_test main-1.31 {
      CREATE TABLE /* In comment ; */ hi;
   }
 } {1}
+do_test main-1.31 {
+  db complete {
+     CREATE TABLE /* In comment ; */;
+  }
+} {1}
 do_test main-1.32 {
   db complete {
      stuff;
@@ -197,6 +229,9 @@ do_test main-1.34 {
 do_test main-1.35 {
   db complete {hi /**/ there;}
 } {1}
+do_test main-1.36 {
+  db complete {hi there/***/;}
+} {1}
 
 
 # Try to open a database with a corrupt database file.