diff --git a/manifest b/manifest index 4041a7b6de..5d19b5ec92 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C :-)\s(CVS\s56) -D 2000-06-06T03:31:22 +C added\sIN\sand\sBETWEEN\soperators\s(CVS\s57) +D 2000-06-06T13:54:15 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -9,26 +9,27 @@ F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47 F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20 F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf -F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc -F src/expr.c 278ff688ac72602ad985f4ccadcae665d245b13b +F src/delete.c 8c733bb82a1b84126116d03dcdccf433c0856f5d +F src/expr.c 1bedf5f426ee1e1609ef1758985b7ce0581987b8 F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6 -F src/parse.y 9c459212441d4786bb011945d6a587568368e202 -F src/select.c d90d577aa7687c860f2ce22dacabdbecb600f609 +F src/parse.y 51ef63a49e73ced4ef3e81d7b1f9fd825d776837 +F src/select.c a1891cfce003a98a57374c01fa5875366c8d9be2 F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268 -F src/sqliteInt.h 6f31db9b08bc7aec193c84f6f08b0f6c7ce9f270 +F src/sqliteInt.h 821b435a18e1c9d0fddcd7bfeefcf5f3fe925c6e F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7 F src/tokenize.c f190e16ebb82dd60497796022f1e56e2e0527975 -F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78 +F src/update.c 18746f920f989b3d19d96c08263c92584823cd35 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315 -F src/vdbe.c 7d00f9ff05cf1287ee21c2a73d9ba0ebdf07b4b6 +F src/vdbe.c 1ab61ada503b99d6b3224c9d40ed9bac855fe317 F src/vdbe.h 49e0f9c6742860e224cd81d1278059f5d029dfb6 -F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251 +F src/where.c c9b90e7672f4662a83ef9a27a193020d69fe034c F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7 F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226 F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944 -F test/expr.test db6984d2a6e86118dfce68edade6539495f29022 +F test/expr.test 52be5592143a88479e0006dfd7e2023e43294636 +F test/in.test 17cd46a9ca0e5d4a804483e6fb496458494858e6 F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf F test/select1.test a0b00df77e85adff75c338e487718c5d31f69e3a @@ -48,7 +49,7 @@ F www/c_interface.tcl 8867d76ddd416d2fbd41e4cb3de8efa9cef105a5 F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77 F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd -P bd8b2645cc16759571c8c622dce4752bd35c04cf -R 712a80d7b1f5a98574f5dc5cddb6e092 +P b52dd82fe32c38c999aef4f07d046d0428336965 +R d1b48bd641877701893f6074a4e397f8 U drh -Z b053febfd62b58b2d6d5fa997754fe2c +Z 3b39b6691fb1f454562ec994b26db409 diff --git a/manifest.uuid b/manifest.uuid index 9ac7f98a32..247d360575 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b52dd82fe32c38c999aef4f07d046d0428336965 \ No newline at end of file +54d198189b58366e4e40139102bc6de94ac55e18 \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index 498c35f22f..3f64043064 100644 --- a/src/delete.c +++ b/src/delete.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.2 2000/06/02 01:17:37 drh Exp $ +** $Id: delete.c,v 1.3 2000/06/06 13:54:15 drh Exp $ */ #include "sqliteInt.h" @@ -43,6 +43,7 @@ void sqliteDeleteFrom( int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ + int base; /* Index of the first available table cursor */ /* Locate the table which we want to update. This table has to be ** put in an IdList structure because some of the subroutines will @@ -70,6 +71,7 @@ void sqliteDeleteFrom( /* Resolve the field names in all the expressions. */ if( pWhere ){ + sqliteExprResolveInSelect(pParse, pWhere); if( sqliteExprResolveIds(pParse, pTabList, pWhere) ){ goto delete_from_cleanup; } @@ -102,27 +104,28 @@ void sqliteDeleteFrom( /* Delete every item identified in the list. */ + base = pParse->nTab; sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Open, i, 1, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base+i, 1, pIdx->zName, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); if( pTab->pIndex ){ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, base, 0, 0, 0); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ int j; sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); for(j=0; jnField; j++){ - sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0); + sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiField[j], 0, 0); } sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); - sqliteVdbeAddOp(v, OP_DeleteIdx, i, 0, 0, 0); + sqliteVdbeAddOp(v, OP_DeleteIdx, base+i, 0, 0, 0); } } - sqliteVdbeAddOp(v, OP_Delete, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Delete, base, 0, 0, 0); sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); diff --git a/src/expr.c b/src/expr.c index 857d7277c6..ab07a54f63 100644 --- a/src/expr.c +++ b/src/expr.c @@ -23,7 +23,7 @@ ************************************************************************* ** This file contains C code routines used for processing expressions ** -** $Id: expr.c,v 1.8 2000/06/06 03:31:22 drh Exp $ +** $Id: expr.c,v 1.9 2000/06/06 13:54:15 drh Exp $ */ #include "sqliteInt.h" @@ -52,6 +52,38 @@ static int isConstant(Expr *p){ return 1; } +/* +** Walk the expression tree and process operators of the form: +** +** expr IN (SELECT ...) +** +** These operators have to be processed before field names are +** resolved because each such operator increments pParse->nTab +** to reserve a cursor number for its own use. But pParse->nTab +** needs to be constant once we begin resolving field names. +** +** Actually, the processing of IN-SELECT is only started by this +** routine. This routine allocates a cursor number to the IN-SELECT +** and then moves on. The code generation is done by +** sqliteExprResolveIds() which must be called afterwards. +*/ +void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){ + if( pExpr==0 ) return; + if( pExpr->op==TK_IN && pExpr->pSelect!=0 ){ + pExpr->iTable = pParse->nTab++; + }else{ + if( pExpr->pLeft ) sqliteExprResolveInSelect(pParse, pExpr->pLeft); + if( pExpr->pRight ) sqliteExprResolveInSelect(pParse, pExpr->pRight); + if( pExpr->pList ){ + int i; + ExprList *pList = pExpr->pList; + for(i=0; inExpr; i++){ + sqliteExprResolveInSelect(pParse, pList->a[i].pExpr); + } + } + } +} + /* ** This routine walks an expression tree and resolves references to ** table fields. Nodes of the form ID.ID or ID resolve into an @@ -186,11 +218,10 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into a temporary - ** table. The cursor number of the temporary table is stored in - ** iTable. + ** table. The cursor number of the temporary table has already + ** been put in iTable by sqliteExprResolveInSelect(). */ - pExpr->iTable = pParse->nTab++; - sqliteVdbeAddOp(v, OP_Open, pExpr->iTable, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Open, pExpr->iTable, 1, 0, 0); if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) ); }else if( pExpr->pList ){ /* Case 2: expr IN (exprlist) @@ -201,15 +232,15 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ int i, iSet; for(i=0; ipList->nExpr; i++){ Expr *pE2 = pExpr->pList->a[i].pExpr; - if( sqliteExprCheck(pParse, pE2, 0, 0) ){ - return 1; - } if( !isConstant(pE2) ){ sqliteSetString(&pParse->zErrMsg, "right-hand side of IN operator must be constant", 0); pParse->nErr++; return 1; } + if( sqliteExprCheck(pParse, pE2, 0, 0) ){ + return 1; + } } iSet = pExpr->iTable = pParse->nSet++; for(i=0; ipList->nExpr; i++){ @@ -248,12 +279,12 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ /* For all else, just recursively walk the tree */ default: { - if( pExpr->pLeft - && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){ + if( pExpr->pLeft + && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){ return 1; } if( pExpr->pRight - && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){ + && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){ return 1; } if( pExpr->pList ){ @@ -512,7 +543,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ } case TK_IN: { int addr; - sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Integer, 1, 0, 0, 0); sqliteExprCode(pParse, pExpr->pLeft); addr = sqliteVdbeCurrentAddr(v); if( pExpr->pSelect ){ @@ -520,7 +551,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ }else{ sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+2, 0, 0); } - sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_AddImm, -1, 0, 0, 0); break; } case TK_BETWEEN: { diff --git a/src/parse.y b/src/parse.y index 8ac4bddcec..ab16c8b4cf 100644 --- a/src/parse.y +++ b/src/parse.y @@ -26,7 +26,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.11 2000/06/06 01:50:43 drh Exp $ +** @(#) $Id: parse.y,v 1.12 2000/06/06 13:54:15 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -108,7 +108,7 @@ ccons ::= NOT NULL. ccons ::= PRIMARY KEY sortorder. {sqliteCreateIndex(pParse,0,0,0,0,0);} ccons ::= UNIQUE. -ccons ::= CHECK expr. +ccons ::= CHECK LP expr RP. // For the time being, the only constraint we care about is the primary // key. @@ -301,7 +301,15 @@ expr(A) ::= expr(X) GE expr(Y). {A = sqliteExpr(TK_GE, X, Y, 0);} expr(A) ::= expr(X) NE expr(Y). {A = sqliteExpr(TK_NE, X, Y, 0);} expr(A) ::= expr(X) EQ expr(Y). {A = sqliteExpr(TK_EQ, X, Y, 0);} expr(A) ::= expr(X) LIKE expr(Y). {A = sqliteExpr(TK_LIKE, X, Y, 0);} +expr(A) ::= expr(X) NOT LIKE expr(Y). { + A = sqliteExpr(TK_LIKE, X, Y, 0); + A = sqliteExpr(TK_NOT, A, 0, 0); +} expr(A) ::= expr(X) GLOB expr(Y). {A = sqliteExpr(TK_GLOB,X,Y,0);} +expr(A) ::= expr(X) NOT GLOB expr(Y). { + A = sqliteExpr(TK_GLOB, X, Y, 0); + A = sqliteExpr(TK_NOT, A, 0, 0); +} expr(A) ::= expr(X) PLUS expr(Y). {A = sqliteExpr(TK_PLUS, X, Y, 0);} expr(A) ::= expr(X) MINUS expr(Y). {A = sqliteExpr(TK_MINUS, X, Y, 0);} expr(A) ::= expr(X) STAR expr(Y). {A = sqliteExpr(TK_STAR, X, Y, 0);} @@ -321,6 +329,13 @@ expr(A) ::= expr(W) BETWEEN expr(X) AND expr(Y). { A = sqliteExpr(TK_BETWEEN, W, 0, 0); A->pList = pList; } +expr(A) ::= expr(W) NOT BETWEEN expr(X) AND expr(Y). { + ExprList *pList = sqliteExprListAppend(0, X, 0); + pList = sqliteExprListAppend(pList, Y, 0); + A = sqliteExpr(TK_BETWEEN, W, 0, 0); + A->pList = pList; + A = sqliteExpr(TK_NOT, A, 0, 0); +} expr(A) ::= expr(X) IN LP exprlist(Y) RP. { A = sqliteExpr(TK_IN, X, 0, 0); A->pList = Y; @@ -329,6 +344,16 @@ expr(A) ::= expr(X) IN LP select(Y) RP. { A = sqliteExpr(TK_IN, X, 0, 0); A->pSelect = Y; } +expr(A) ::= expr(X) NOT IN LP exprlist(Y) RP. { + A = sqliteExpr(TK_IN, X, 0, 0); + A->pList = Y; + A = sqliteExpr(TK_NOT, A, 0, 0); +} +expr(A) ::= expr(X) NOT IN LP select(Y) RP. { + A = sqliteExpr(TK_IN, X, 0, 0); + A->pSelect = Y; + A = sqliteExpr(TK_NOT, A, 0, 0); +} diff --git a/src/select.c b/src/select.c index eb84495cab..cf2587e9e4 100644 --- a/src/select.c +++ b/src/select.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements. ** -** $Id: select.c,v 1.9 2000/06/06 01:50:43 drh Exp $ +** $Id: select.c,v 1.10 2000/06/06 13:54:15 drh Exp $ */ #include "sqliteInt.h" @@ -165,6 +165,15 @@ int sqliteSelect( /* Resolve the field names and do a semantics check on all the expressions. */ + for(i=0; inExpr; i++){ + sqliteExprResolveInSelect(pParse, pEList->a[i].pExpr); + } + if( pWhere ) sqliteExprResolveInSelect(pParse, pWhere); + if( pOrderBy ){ + for(i=0; inExpr; i++){ + sqliteExprResolveInSelect(pParse, pOrderBy->a[i].pExpr); + } + } for(i=0; inExpr; i++){ if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){ return 1; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 17332b6d37..42590f8281 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.16 2000/06/06 01:50:43 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.17 2000/06/06 13:54:15 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" @@ -314,3 +314,4 @@ char *sqliteTableNameFromToken(Token*); int sqliteExprCheck(Parse*, Expr*, int, int*); int sqliteFuncId(Token*); int sqliteExprResolveIds(Parse*, IdList*, Expr*); +void sqliteExprResolveInSelect(Parse*, Expr*); diff --git a/src/update.c b/src/update.c index 323c4254ae..ca11425418 100644 --- a/src/update.c +++ b/src/update.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.3 2000/06/03 18:06:53 drh Exp $ +** $Id: update.c,v 1.4 2000/06/06 13:54:16 drh Exp $ */ #include "sqliteInt.h" @@ -45,6 +45,7 @@ void sqliteUpdate( Vdbe *v; /* The virtual database engine */ Index *pIdx; /* For looping over indices */ int nIdx; /* Number of indices that need updating */ + int base; /* Index of first available table cursor */ Index **apIdx = 0; /* An array of indices that need updating too */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th field of the table. @@ -80,6 +81,12 @@ void sqliteUpdate( ** WHERE clause and in the new values. Also find the field index ** for each field to be updated in the pChanges array. */ + if( pWhere ){ + sqliteExprResolveInSelect(pParse, pWhere); + } + for(i=0; inExpr; i++){ + sqliteExprResolveInSelect(pParse, pChanges->a[i].pExpr); + } if( pWhere ){ if( sqliteExprResolveIds(pParse, pTabList, pWhere) ){ goto update_cleanup; @@ -155,9 +162,10 @@ void sqliteUpdate( ** open every index that needs updating. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0); + base = pParse->nTab; + sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0); for(i=0; izName, 0); + sqliteVdbeAddOp(v, OP_Open, base+i+1, 1, apIdx[i]->zName, 0); } /* Loop over every record that needs updating. We have to load @@ -168,7 +176,7 @@ void sqliteUpdate( end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, base, 0, 0, 0); /* Delete the old indices for the current record. */ @@ -176,10 +184,10 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); pIdx = apIdx[i]; for(j=0; jnField; j++){ - sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0); + sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiField[j], 0, 0); } sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); - sqliteVdbeAddOp(v, OP_DeleteIdx, i+1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_DeleteIdx, base+i+1, 0, 0, 0); } /* Compute a completely new data for this record. @@ -187,7 +195,7 @@ void sqliteUpdate( for(i=0; inCol; i++){ j = aXRef[i]; if( j<0 ){ - sqliteVdbeAddOp(v, OP_Field, 0, i, 0, 0); + sqliteVdbeAddOp(v, OP_Field, base, i, 0, 0); }else{ sqliteExprCode(pParse, pChanges->a[j].pExpr); } @@ -202,13 +210,13 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0); } sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); - sqliteVdbeAddOp(v, OP_PutIdx, i+1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, base+i+1, 0, 0, 0); } /* Write the new data back into the database. */ sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, base, 0, 0, 0); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. diff --git a/src/vdbe.c b/src/vdbe.c index a563d983b5..6d31c1b9b1 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -41,7 +41,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.19 2000/06/06 03:31:22 drh Exp $ +** $Id: vdbe.c,v 1.20 2000/06/06 13:54:16 drh Exp $ */ #include "sqliteInt.h" #include @@ -795,7 +795,7 @@ int sqliteVdbeList( azField[3] = zP2; azField[5] = 0; rc = SQLITE_OK; - if( pzErrMsg ){ *pzErrMsg = 0; } + /* if( pzErrMsg ){ *pzErrMsg = 0; } */ for(i=0; rc==SQLITE_OK && inOp; i++){ sprintf(zAddr,"%d",i); sprintf(zP1,"%d", p->aOp[i].p1); @@ -878,7 +878,7 @@ int sqliteVdbeExec( p->trace = stderr; } #endif - if( pzErrMsg ){ *pzErrMsg = 0; } + /* if( pzErrMsg ){ *pzErrMsg = 0; } */ for(pc=0; rc==SQLITE_OK && pcnOp && pc>=0; pc++){ pOp = &p->aOp[pc]; if( p->trace ){ diff --git a/src/where.c b/src/where.c index 69c7e16127..bfdd2a7829 100644 --- a/src/where.c +++ b/src/where.c @@ -25,7 +25,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.6 2000/06/05 18:54:47 drh Exp $ +** $Id: where.c,v 1.7 2000/06/06 13:54:16 drh Exp $ */ #include "sqliteInt.h" @@ -348,7 +348,7 @@ WhereInfo *sqliteWhereBegin( } pWInfo->iContinue = cont; if( pushKey && !haveKey ){ - sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Key, base, 0, 0, 0); } sqliteFree(aOrder); return pWInfo; diff --git a/test/expr.test b/test/expr.test index febe9fb21c..2fb44af6cb 100644 --- a/test/expr.test +++ b/test/expr.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing expressions. # -# $Id: expr.test,v 1.4 2000/06/03 19:19:42 drh Exp $ +# $Id: expr.test,v 1.5 2000/06/06 13:54:16 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -143,6 +143,8 @@ test_expr expr-5.5 {t1='abc', t2='A%C'} {t1 LIKE t2} 1 test_expr expr-5.6 {t1='abxyzzyc', t2='A%C'} {t1 LIKE t2} 1 test_expr expr-5.7 {t1='abxyzzy', t2='A%C'} {t1 LIKE t2} 0 test_expr expr-5.8 {t1='abxyzzycx', t2='A%C'} {t1 LIKE t2} 0 +test_expr expr-5.9 {t1='abc', t2='xyz'} {t1 NOT LIKE t2} 1 +test_expr expr-5.10 {t1='abc', t2='ABC'} {t1 NOT LIKE t2} 0 test_expr expr-6.1 {t1='abc', t2='xyz'} {t1 GLOB t2} 0 test_expr expr-6.2 {t1='abc', t2='ABC'} {t1 GLOB t2} 0 @@ -154,7 +156,8 @@ test_expr expr-6.7 {t1='abc', t2='a*c'} {t1 GLOB t2} 1 test_expr expr-6.8 {t1='abxyzzyc', t2='a*c'} {t1 GLOB t2} 1 test_expr expr-6.9 {t1='abxyzzy', t2='a*c'} {t1 GLOB t2} 0 test_expr expr-6.10 {t1='abxyzzycx', t2='a*c'} {t1 GLOB t2} 0 - +test_expr expr-6.11 {t1='abc', t2='xyz'} {t1 NOT GLOB t2} 1 +test_expr expr-6.12 {t1='abc', t2='a?c'} {t1 NOT GLOB t2} 0 # The sqliteExprIfFalse and sqliteExprIfTrue routines are only # executed as part of a WHERE clause. Create a table suitable diff --git a/test/in.test b/test/in.test new file mode 100644 index 0000000000..c30879ef50 --- /dev/null +++ b/test/in.test @@ -0,0 +1,150 @@ +# Copyright (c) 1999, 2000 D. Richard Hipp +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# Author contact information: +# drh@hwaci.com +# http://www.hwaci.com/drh/ +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the IN and BETWEEN operator. +# +# $Id: in.test,v 1.1 2000/06/06 13:54:16 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Generate the test data we will need for the first squences of tests. +# +do_test in-1.0 { + set fd [open data1.txt w] + for {set i 1} {$i<=10} {incr i} { + puts $fd "$i\t[expr {int(pow(2,$i))}]" + } + close $fd + execsql { + CREATE TABLE t1(a int, b int); + COPY t1 FROM 'data1.txt'; + } + file delete -force data1.txt + execsql {SELECT count(*) FROM t1} +} {10} + +# Do basic testing of BETWEEN. +# +do_test in-1.1 { + execsql {SELECT a FROM t1 WHERE b BETWEEN 10 AND 50 ORDER BY a} +} {4 5} +do_test in-1.2 { + execsql {SELECT a FROM t1 WHERE b NOT BETWEEN 10 AND 50 ORDER BY a} +} {1 2 3 6 7 8 9 10} +do_test in-1.3 { + execsql {SELECT a FROM t1 WHERE b BETWEEN a AND a*5 ORDER BY a} +} {1 2 3 4} +do_test in-1.4 { + execsql {SELECT a FROM t1 WHERE b NOT BETWEEN a AND a*5 ORDER BY a} +} {5 6 7 8 9 10} +do_test in-1.6 { + execsql {SELECT a FROM t1 WHERE b BETWEEN a AND a*5 OR b=512 ORDER BY a} +} {1 2 3 4 9} +do_test in-1.7 { + execsql {SELECT a+ 100*(a BETWEEN 1 and 3) FROM t1 ORDER BY b} +} {101 102 103 4 5 6 7 8 9 10} + + +# Testing of the IN operator using static lists on the right-hand side. +# +do_test in-2.1 { + execsql {SELECT a FROM t1 WHERE b IN (8,12,16,24,32) ORDER BY a} +} {3 4 5} +do_test in-2.2 { + execsql {SELECT a FROM t1 WHERE b NOT IN (8,12,16,24,32) ORDER BY a} +} {1 2 6 7 8 9 10} +do_test in-2.3 { + execsql {SELECT a FROM t1 WHERE b IN (8,12,16,24,32) OR b=512 ORDER BY a} +} {3 4 5 9} +do_test in-2.4 { + execsql {SELECT a FROM t1 WHERE b NOT IN (8,12,16,24,32) OR b=512 ORDER BY a} +} {1 2 6 7 8 9 10} +do_test in-2.5 { + execsql {SELECT a+100*(b IN (8,16,24)) FROM t1 ORDER BY b} +} {1 2 103 104 5 6 7 8 9 10} + +do_test in-2.6 { + set v [catch {execsql {SELECT a FROM t1 WHERE b IN (b+10,20)}} msg] + lappend v $msg +} {1 {right-hand side of IN operator must be constant}} +do_test in-2.7 { + set v [catch {execsql {SELECT a FROM t1 WHERE b IN (max(5,10,b),20)}} msg] + lappend v $msg +} {1 {right-hand side of IN operator must be constant}} +do_test in-2.8 { + execsql {SELECT a FROM t1 WHERE b IN (8*2,64/2) ORDER BY b} +} {4 5} +do_test in-2.9 { + set v [catch {execsql {SELECT a FROM t1 WHERE b IN (xyz(5,10),20)}} msg] + lappend v $msg +} {1 {no such function: xyz}} +do_test in-2.10 { + set v [catch {execsql {SELECT a FROM t1 WHERE min(0,b IN (a,30))}} msg] + lappend v $msg +} {1 {right-hand side of IN operator must be constant}} +do_test in-2.11 { + set v [catch {execsql {SELECT a FROM t1 WHERE c IN (10,20)}} msg] + lappend v $msg +} {1 {no such field: c}} + +# Testing the IN operator where the right-hand side is a SELECT +# +do_test in-3.1 { + execsql { + SELECT a FROM t1 + WHERE b IN (SELECT b FROM t1 WHERE a<5) + ORDER BY a + } +} {1 2 3 4} +do_test in-3.2 { + execsql { + SELECT a FROM t1 + WHERE b IN (SELECT b FROM t1 WHERE a<5) OR b==512 + ORDER BY a + } +} {1 2 3 4 9} +do_test in-3.3 { + execsql { + SELECT a + 100*(b IN (SELECT b FROM t1 WHERE a<5)) FROM t1 ORDER BY b + } +} {101 102 103 104 5 6 7 8 9 10} + +# Make sure the UPDATE and DELETE commands work with IN-SELECT +# +do_test in-4.1 { + execsql { + UPDATE t1 SET b=b*2 + WHERE b IN (SELECT b FROM t1 WHERE a>8) + } + execsql {SELECT b FROM t1 ORDER BY b} +} {2 4 8 16 32 64 128 256 1024 2048} +do_test in-4.2 { + execsql { + DELETE FROM t1 WHERE b IN (SELECT b FROM t1 WHERE a>8) + } + execsql {SELECT a FROM t1 ORDER BY a} +} {1 2 3 4 5 6 7 8} + + +finish_test