mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Fix deficiencies in sqlite_complete() pointed out by R. Dennis Cote. (CVS 955)
FossilOrigin-Name: 54b33a5ed9f7a89435c2f1395a3177e8c778bb8a
This commit is contained in:
18
manifest
18
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
|
||||
|
@@ -1 +1 @@
|
||||
dd57d6ae6a247824e44a6073bc7e73ecb3c500fd
|
||||
54b33a5ed9f7a89435c2f1395a3177e8c778bb8a
|
128
src/main.c
128
src/main.c
@@ -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.
|
||||
*/
|
||||
|
188
src/tokenize.c
188
src/tokenize.c
@@ -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;
|
||||
}
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user