Fix for ticket #41: Better handling of CREATE TRIGGER in the sqlite_complete()
function. (CVS 567)

FossilOrigin-Name: f45c4b767a6b1451787836060235ff7499dea0de
diff --git a/manifest b/manifest
index 8dcb603..7039832 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Beginning\sto\sclean\sup\sthe\strigger\scode.\s\sStill\slots\sof\swork\sto\sdo.\s(CVS\s566)
-D 2002-05-15T12:45:43
+C Fix\sfor\sticket\s#41:\sBetter\shandling\sof\sCREATE\sTRIGGER\sin\sthe\ssqlite_complete()\nfunction.\s(CVS\s567)
+D 2002-05-15T14:17:45
 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
 F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -28,13 +28,13 @@
 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
 F src/insert.c 26ca700fb4055c34970ebac1d9a9a067bebef3b0
-F src/main.c 9e9ba7d8491928c46c6a70d9b7c30bbf7e60d499
+F src/main.c de486c893c0a34a91aa5660c3ab5ce3d08746eaf
 F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
 F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
 F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
 F src/pager.c ba5740104cc27b342cd43eebfdc44d60f64a3ded
 F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
-F src/parse.y 164789531d0c6a2c28fb4baded14afc1be4bd4aa
+F src/parse.y 12d6f6c0d12c868b366e3758e62578e686623394
 F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
 F src/select.c 1b623a7d826ec7c245bc542b665d61724da2a62d
@@ -73,7 +73,7 @@
 F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
 F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
 F test/lock.test 3fcfd46a73119f6a18094673328a32c7b3047a8f
-F test/main.test e121fed34ebf67f595290776162e322b08470127
+F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85
 F test/malloc.test 70fdd0812e2a57eb746aaf015350f58bb8eee0b1
 F test/minmax.test fb6ab400271ae1f5bc88617c2882f2f081ea8e6d
 F test/misc1.test a03214118429b40ca5548bc1fae0ebd5c34dabe6
@@ -134,7 +134,7 @@
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 690f9a163173c4c7af7e8e92e942cee4184c7974
-R 2c7fa77b8b0294150616cb3ad535e3be
+P b10346818b25940c6dc85e94de8e36d20954161c
+R 8c54c69d6079e01fd5f95accd48faacf
 U drh
-Z 2f79a3bdfbaced77f180a80585eae438
+Z f5897975828e35ff1e0f4db008fa1bf2
diff --git a/manifest.uuid b/manifest.uuid
index d6e41fb..7dfc43d 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-b10346818b25940c6dc85e94de8e36d20954161c
\ No newline at end of file
+f45c4b767a6b1451787836060235ff7499dea0de
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
index 3d277e8..7ec9cd1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,10 +14,11 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.73 2002/05/15 11:44:14 drh Exp $
+** $Id: main.c,v 1.74 2002/05/15 14:17:45 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
+#include <ctype.h>
 
 /*
 ** This is the callback routine for the code that initializes the
@@ -476,14 +477,22 @@
 
 /*
 ** 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 = 0;
+  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 ' ':
@@ -494,53 +503,88 @@
       }
       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!='\'' ){ zSql++; }
-        if( *zSql==0 ) return 0;
-        break;
-      }
-      case '"': {
-        isComplete = 0;
-        zSql++;
-        while( *zSql && *zSql!='"' ){ zSql++; }
+        while( *zSql && *zSql!=c ){ zSql++; }
         if( *zSql==0 ) return 0;
         break;
       }
       case '-': {
         if( zSql[1]!='-' ){
           isComplete = 0;
+          seenCreate = 0;
           break;
         }
         while( *zSql && *zSql!='\n' ){ zSql++; }
-        if( *zSql==0 ) return isComplete;
+        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: {
-        if (seenCreate && !sqliteStrNICmp(zSql, "trigger", 7)){
-          while (sqliteStrNICmp(zSql, "end", 3)){
-            if (!*++zSql) return 0;
-          }
-        }
-        if (!sqliteStrNICmp(zSql, "create", 6)) {
-          zSql = zSql + 5;
-          seenCreate = 1;
-        }else{
-          seenCreate = 0;
-        }
+        seenCreate = 0;
+        seenText = 1;
         isComplete = 0;
         break;
       }
     }
     zSql++;
   }
-  return isComplete;
+  return seenText && isComplete && requireEnd==0;
 }
 
 /*
diff --git a/src/parse.y b/src/parse.y
index b790746..bc60a78 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.64 2002/05/15 08:30:14 danielk1977 Exp $
+** @(#) $Id: parse.y,v 1.65 2002/05/15 14:17:45 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -120,6 +120,7 @@
 id(A) ::= PRAGMA(X).     {A = X;}
 id(A) ::= REPLACE(X).    {A = X;}
 id(A) ::= TEMP(X).       {A = X;}
+id(A) ::= TRIGGER(X).    {A = X;}
 id(A) ::= VACUUM(X).     {A = X;}
 id(A) ::= VIEW(X).       {A = X;}
 
@@ -692,4 +693,3 @@
 cmd ::= DROP TRIGGER ids(X). {
     sqliteDropTrigger(pParse,&X,0);
 }
-
diff --git a/test/main.test b/test/main.test
index 4eceab0..fdcfcbc 100644
--- a/test/main.test
+++ b/test/main.test
@@ -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.10 2002/05/10 13:14:08 drh Exp $
+# $Id: main.test,v 1.11 2002/05/15 14:17:45 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -70,6 +70,95 @@
 do_test main-1.15 {
   db complete {SELECT a-b FROM t1 }
 } {0}
+do_test main-1.16 {
+  db complete {
+    CREATE TABLE abc(x,y);
+  }
+} {1}
+do_test main-1.17 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr;
+  }
+} {0}
+do_test main-1.18 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr; END;
+  }
+} {1}
+do_test main-1.19 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE abc BEGIN
+       UPDATE pqr;
+       unknown command;
+  }
+} {0}
+do_test main-1.20 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr;
+  }
+} {0}
+do_test main-1.21 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE end BEGIN
+       SELECT a, b FROM end;
+  }
+} {0}
+do_test main-1.22 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE end BEGIN
+       SELECT a, b FROM end;
+    END;
+  }
+} {1}
+do_test main-1.23 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE end BEGIN
+       SELECT a, b FROM end;
+    END;
+    SELECT a, b FROM end;
+  }
+} {1}
+do_test main-1.24 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE [;end;] BEGIN
+       UPDATE pqr;
+  }
+} {0}
+do_test main-1.25 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=[;end;];;;
+  }
+} {0}
+do_test main-1.26 {
+  db complete {
+    CREATE -- a comment
+    TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {0}
+do_test main-1.27 {
+  db complete {
+    CREATE -- a comment
+    TRIGGERX xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {1}
+do_test main-1.28 {
+  db complete {
+    CREATE TEMP TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+  }
+} {1}
+do_test main-1.29 {
+  db complete {
+    CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+       UPDATE pqr SET a=5;
+       EXPLAIN select * from xyz;
+  }
+} {0}
+
 
 # Try to open a database with a corrupt database file.
 #