-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
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
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
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
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
** 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"
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.
*/
** 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"
}
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;
+}
# 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
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
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;
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.