diff --git a/manifest b/manifest index ff6a910d3c..dd6f7aa3b0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C added\sAgg\sopcodes\sto\sthe\svdbe\s(CVS\s54) -D 2000-06-05T21:39:49 +C :-)\s(CVS\s55) +D 2000-06-06T01:50:43 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -10,20 +10,20 @@ F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20 F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc -F src/expr.c 7e87558f88e7a52a902d78446843bda25015531e +F src/expr.c b309517f59017f3a7c0db76a54bc3bc00e8fea38 F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6 -F src/parse.y 79b403240985c71d3f80abe3943e57482287428f -F src/select.c ab379f969283819ac81b72d0bf7c7aa3fc9389db +F src/parse.y 9c459212441d4786bb011945d6a587568368e202 +F src/select.c d90d577aa7687c860f2ce22dacabdbecb600f609 F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268 -F src/sqliteInt.h c01eef2760cebee09c8e1710faf2ebd6fc8a16b6 +F src/sqliteInt.h 6f31db9b08bc7aec193c84f6f08b0f6c7ce9f270 F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7 -F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1 +F src/tokenize.c f190e16ebb82dd60497796022f1e56e2e0527975 F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315 -F src/vdbe.c e14cf771e4f487feb433fc6c0c6bd9536167a953 -F src/vdbe.h 7b3cd6b122b72ce8d2f5ab40069748d32f1d9df0 +F src/vdbe.c 03177425b174d865f2c37b188a9df8a37f4eaff2 +F src/vdbe.h 49e0f9c6742860e224cd81d1278059f5d029dfb6 F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251 F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7 F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226 @@ -48,7 +48,7 @@ F www/c_interface.tcl 8867d76ddd416d2fbd41e4cb3de8efa9cef105a5 F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77 F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd -P 3dbfa558ef01ac9249a3f40da49bc71cc7b040bc -R 2c42d01480de541004757564bdb21603 +P e9ed5d2a3639161fb58856275ba9495f21d366bf +R ad06a10e10e9d51600332d684a457b6e U drh -Z 3c51c450bc15ba9b865ce0cc0b812208 +Z b21141bb852afc7c732c3c3467253d37 diff --git a/manifest.uuid b/manifest.uuid index 36ad03c666..fc58e72f7b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e9ed5d2a3639161fb58856275ba9495f21d366bf \ No newline at end of file +bd8b2645cc16759571c8c622dce4752bd35c04cf \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5fd5bbb346..1b3e8527f5 100644 --- a/src/expr.c +++ b/src/expr.c @@ -23,10 +23,35 @@ ************************************************************************* ** This file contains C code routines used for processing expressions ** -** $Id: expr.c,v 1.6 2000/06/05 18:54:46 drh Exp $ +** $Id: expr.c,v 1.7 2000/06/06 01:50:43 drh Exp $ */ #include "sqliteInt.h" +/* +** Walk an expression tree. Return 1 if the expression is constant +** and 0 if it involves variables. +*/ +static int isConstant(Expr *p){ + switch( p->op ){ + case TK_ID: + case TK_FIELD: + case TK_DOT: + return 0; + default: { + if( p->pLeft && !isConstant(p->pLeft) ) return 0; + if( p->pRight && !isConstant(p->pRight) ) return 0; + if( p->pList ){ + int i; + for(i=0; ipList->nExpr; i++){ + if( !isConstant(p->pList->a[i].pExpr) ) return 0; + } + } + break; + } + } + return 1; +} + /* ** This routine walks an expression tree and resolves references to ** table fields. Nodes of the form ID.ID or ID resolve into an @@ -36,7 +61,18 @@ ** value. The iField value is changed to the index of the field of the ** referenced table. ** -** This routine also looks for SELECTs that are part of an expression. +** We also check for instances of the IN operator. IN comes in two +** forms: +** +** expr IN (exprlist) +** and +** expr IN (SELECT ...) +** +** The first form is handled by creating a set holding the list +** of allowed values. The second form causes the SELECT to generate +** a temporary table. +** +** This routine also looks for scalar SELECTs that are part of an expression. ** If it finds any, it generates code to write the value of that select ** into a memory cell. ** @@ -137,9 +173,70 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ break; } + case TK_IN: { + Vdbe *v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) return 1; + if( pExpr->pSelect ){ + /* 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. + */ + pExpr->iTable = pParse->nTab++; + sqliteVdbeAddOp(v, OP_Open, pExpr->iTable, 0, 0, 0); + if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) ); + }else if( pExpr->pList ){ + /* Case 2: expr IN (exprlist) + ** + ** Create a set to put the exprlist values in. The Set id is stored + ** in iTable. + */ + 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; + } + } + iSet = pExpr->iTable = pParse->nSet++; + for(i=0; ipList->nExpr; i++){ + Expr *pE2 = pExpr->pList->a[i].pExpr; + switch( pE2->op ){ + case TK_FLOAT: + case TK_INTEGER: + case TK_STRING: { + int addr = sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0, 0, 0); + sqliteVdbeChangeP3(v, addr, pE2->token.z, pE2->token.n); + sqliteVdbeDequoteP3(v, addr); + break; + } + default: { + sqliteExprCode(pParse, pE2); + sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0, 0, 0); + break; + } + } + } + } + } + case TK_SELECT: { + /* This has to be a scalar SELECT. Generate code to put the + ** value of this select in a memory cell and record the number + ** of the memory cell in iField. + */ pExpr->iField = pParse->nMem++; - if( sqliteSelect(pParse, pExpr->pSelect, -1, pExpr->iField) ){ + if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iField) ){ return 1; } break; @@ -281,6 +378,13 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ if( nErr==0 && pExpr->pRight ){ nErr = sqliteExprCheck(pParse, pExpr->pRight, 0, 0); } + if( nErr==0 && pExpr->pList ){ + int n = pExpr->pList->nExpr; + int i; + for(i=0; nErr==0 && ipList->a[i].pExpr, 0, 0); + } + } break; } } @@ -402,6 +506,27 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iField, 0, 0, 0); break; } + case TK_IN: { + int addr; + sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0); + sqliteExprCode(pParse, pExpr->pLeft); + addr = sqliteVdbeCurrentAddr(v); + if( pExpr->pSelect ){ + sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+2, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+2, 0, 0); + } + sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0); + break; + } + case TK_BETWEEN: { + int lbl = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0); + sqliteExprIfFalse(pParse, pExpr, lbl); + sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0); + sqliteVdbeResolveLabel(v, lbl); + break; + } } return; } @@ -463,6 +588,26 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){ sqliteVdbeAddOp(v, op, 0, dest, 0, 0); break; } + case TK_IN: { + if( pExpr->pSelect ){ + sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, dest, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, dest, 0, 0); + } + break; + } + case TK_BETWEEN: { + int lbl = sqliteVdbeMakeLabel(v); + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[0].pExpr); + sqliteVdbeAddOp(v, OP_Lt, 0, lbl, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[1].pExpr); + sqliteVdbeAddOp(v, OP_Le, 0, dest, 0, 0); + sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Pop, 1, 0, 0, lbl); + break; + } default: { sqliteExprCode(pParse, pExpr); sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0); @@ -533,6 +678,27 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){ sqliteVdbeAddOp(v, op, 0, dest, 0, 0); break; } + case TK_IN: { + if( pExpr->pSelect ){ + sqliteVdbeAddOp(v, OP_NotFound, pExpr->iTable, dest, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_SetNotFound, pExpr->iTable, dest, 0, 0); + } + break; + } + case TK_BETWEEN: { + int addr; + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[0].pExpr); + addr = sqliteVdbeCurrentAddr(v); + sqliteVdbeAddOp(v, OP_Ge, 0, addr+3, 0, 0); + sqliteVdbeAddOp(v, OP_Pop, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, dest, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[1].pExpr); + sqliteVdbeAddOp(v, OP_Gt, 0, dest, 0, 0); + break; + } default: { sqliteExprCode(pParse, pExpr); sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0); diff --git a/src/parse.y b/src/parse.y index 125a140511..8ac4bddcec 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.10 2000/06/05 18:54:46 drh Exp $ +** @(#) $Id: parse.y,v 1.11 2000/06/06 01:50:43 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -133,7 +133,7 @@ cmd ::= DROP TABLE id(X). {sqliteDropTable(pParse,&X);} // The select statement // cmd ::= select(X). { - sqliteSelect(pParse, X, -1, -1); + sqliteSelect(pParse, X, SRT_Callback, 0); sqliteSelectDelete(X); } @@ -150,6 +150,7 @@ select(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) // %type distinct {int} distinct(A) ::= DISTINCT. {A = 1;} +distinct(A) ::= ALL. {A = 0;} distinct(A) ::= . {A = 0;} // selcollist is a list of expressions that are to become the return @@ -271,7 +272,7 @@ fieldlist(A) ::= ID(Y). {A = sqliteIdListAppend(0,&Y);} %left OR. %left AND. %right NOT. -%left EQ NE ISNULL NOTNULL IS LIKE GLOB. +%left EQ NE ISNULL NOTNULL IS LIKE GLOB BETWEEN IN. %left GT GE LT LE. %left PLUS MINUS. %left STAR SLASH PERCENT. @@ -300,8 +301,7 @@ 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) GLOB expr(Y). {A = sqliteExpr(TK_GLOB,X,Y,0);} -// expr(A) ::= expr(X) IS expr(Y). {A = sqliteExpr(TK_EQ, X, Y, 0);} +expr(A) ::= expr(X) GLOB expr(Y). {A = sqliteExpr(TK_GLOB,X,Y,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);} @@ -315,6 +315,22 @@ expr(A) ::= LP select(X) RP. { A = sqliteExpr(TK_SELECT, 0, 0, 0); A->pSelect = X; } +expr(A) ::= expr(W) 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; +} +expr(A) ::= expr(X) IN LP exprlist(Y) RP. { + A = sqliteExpr(TK_IN, X, 0, 0); + A->pList = Y; +} +expr(A) ::= expr(X) IN LP select(Y) RP. { + A = sqliteExpr(TK_IN, X, 0, 0); + A->pSelect = Y; +} + + %type exprlist {ExprList*} %destructor exprlist {sqliteExprListDelete($$);} diff --git a/src/select.c b/src/select.c index 5317452e6d..eb84495cab 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.8 2000/06/05 18:54:46 drh Exp $ +** $Id: select.c,v 1.9 2000/06/06 01:50:43 drh Exp $ */ #include "sqliteInt.h" @@ -71,12 +71,18 @@ void sqliteSelectDelete(Select *p){ /* ** Generate code for the given SELECT statement. ** -** If iDest<0 and iMem<0, then the results of the query are sent to -** the callback function. If iDest>=0 then the results are written to -** an open cursor with the index iDest. The calling function is -** responsible for having that cursor open. If iDest<0 and iMem>=0 -** then the result should be a single value which is then stored in -** memory location iMem of the virtual machine. +** The results are distributed in various ways depending on the +** value of eDest and iParm. +** +** eDest Value Result +** ------------ ------------------------------------------- +** SRT_Callback Invoke the callback for each row of the result. +** +** SRT_Mem Store first result in memory cell iParm +** +** SRT_Set Store results as keys of a table with cursor iParm +** +** SRT_Table Store results in a regular table with cursor iParm ** ** This routine returns the number of errors. If any errors are ** encountered, then an appropriate error message is left in @@ -88,8 +94,8 @@ void sqliteSelectDelete(Select *p){ int sqliteSelect( Parse *pParse, /* The parser context */ Select *p, /* The SELECT statement being coded. */ - int iDest, /* Write results to this cursor */ - int iMem /* Save result in this memory location, if >=0 */ + int eDest, /* One of SRT_Callback, SRT_Mem, SRT_Set, SRT_Table */ + int iParm /* Save result in this memory location, if >=0 */ ){ int i, j; WhereInfo *pWInfo; @@ -150,7 +156,7 @@ int sqliteSelect( /* If writing to memory, only a single column may be output. */ - if( iMem>=0 && pEList->nExpr>1 ){ + if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){ sqliteSetString(&pParse->zErrMsg, "only a single result allowed for " "a SELECT that is part of an expression", 0); pParse->nErr++; @@ -197,24 +203,15 @@ int sqliteSelect( } } - /* ORDER BY is ignored if - ** - ** (1) this is an aggregate query like count(*) - ** since only one row will be returned. - ** - ** (2) We are writing the result to another table, since the - ** order will get scrambled again after inserting. - ** - ** (3) We are writing to a memory cell, since there is only - ** one result. + /* ORDER BY is ignored if we are not invoking callbacks. */ - if( isAgg || iDest>=0 || iMem>=0 ){ + if( isAgg || eDest!=SRT_Callback ){ pOrderBy = 0; } /* Turn off distinct if this is an aggregate or writing to memory. */ - if( isAgg || iMem>=0 ){ + if( isAgg || eDest==SRT_Mem ){ isDistinct = 0; } @@ -236,7 +233,7 @@ int sqliteSelect( /* Identify column names if we will be using a callback. This ** step is skipped if the output is going to a table or a memory cell. */ - if( iDest<0 && iMem<0 ){ + if( eDest==SRT_Callback ){ sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0); for(i=0; inExpr; i++){ Expr *p; @@ -294,9 +291,9 @@ int sqliteSelect( /* Initialize the memory cell to NULL */ - if( iMem>=0 ){ + if( eDest==SRT_Mem ){ sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0); } /* Begin the database scan @@ -365,13 +362,16 @@ int sqliteSelect( } sqliteVdbeAddOp(v, op, p1, 0, 0, 0); } - }else if( iDest>=0 ){ + }else if( eDest==SRT_Table ){ sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0); - sqliteVdbeAddOp(v, OP_New, iDest, 0, 0, 0); + sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0); sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Put, iDest, 0, 0, 0); - }else if( iMem>=0 ){ - sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0); + }else if( eDest==SRT_Set ){ + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0); + }else if( eDest==SRT_Mem ){ + sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0); sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iBreak, 0, 0); }else{ sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); @@ -398,13 +398,16 @@ int sqliteSelect( ** exactly once. */ if( isAgg ){ - if( iDest>=0 ){ + if( eDest==SRT_Table ){ sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0); - sqliteVdbeAddOp(v, OP_New, iDest, 0, 0, 0); + sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0); sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Put, iDest, 0, 0, 0); - }else if( iMem>=0 ){ - sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0); + }else if( eDest==SRT_Set ){ + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0); + }else if( eDest==SRT_Mem ){ + sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0); }else{ sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index eca54c49f8..17332b6d37 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.15 2000/06/05 18:54:46 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.16 2000/06/06 01:50:43 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" @@ -226,6 +226,14 @@ struct Select { ExprList *pOrderBy; /* The ORDER BY clause */ }; +/* +** The results of a select can be distributed in several ways. +*/ +#define SRT_Callback 1 /* Invoke a callback with each row of result */ +#define SRT_Mem 2 /* Store result in a memory cell */ +#define SRT_Set 3 /* Store result in a table for use with "IN" */ +#define SRT_Table 4 /* Store result in a regular table */ + /* ** An SQL parser context */ @@ -244,6 +252,7 @@ struct Parse { int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated cursors */ int nMem; /* Number of memory cells used so far */ + int nSet; /* Number of sets used so far */ }; /* diff --git a/src/tokenize.c b/src/tokenize.c index 76e0b7a17a..989010e049 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -27,7 +27,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.5 2000/05/31 20:00:53 drh Exp $ +** $Id: tokenize.c,v 1.6 2000/06/06 01:50:43 drh Exp $ */ #include "sqliteInt.h" #include @@ -49,9 +49,11 @@ struct Keyword { ** These are the keywords */ static Keyword aKeywordTable[] = { + { "ALL", 0, TK_ALL, 0 }, { "AND", 0, TK_AND, 0 }, { "AS", 0, TK_AS, 0 }, { "ASC", 0, TK_ASC, 0 }, + { "BETWEEN", 0, TK_BETWEEN, 0 }, { "BY", 0, TK_BY, 0 }, { "CHECK", 0, TK_CHECK, 0 }, { "CONSTRAINT", 0, TK_CONSTRAINT, 0 }, @@ -66,6 +68,7 @@ static Keyword aKeywordTable[] = { { "EXPLAIN", 0, TK_EXPLAIN, 0 }, { "FROM", 0, TK_FROM, 0 }, { "GLOB", 0, TK_GLOB, 0 }, + { "IN", 0, TK_IN, 0 }, { "INDEX", 0, TK_INDEX, 0 }, { "INSERT", 0, TK_INSERT, 0 }, { "INTO", 0, TK_INTO, 0 }, diff --git a/src/vdbe.c b/src/vdbe.c index 04e8be71d2..d52e187c87 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.17 2000/06/05 21:39:49 drh Exp $ +** $Id: vdbe.c,v 1.18 2000/06/06 01:50:43 drh Exp $ */ #include "sqliteInt.h" #include @@ -106,6 +106,15 @@ struct Mem { }; typedef struct Mem Mem; +/* +** Allowed values for Stack.flags +*/ +#define STK_Null 0x0001 /* Value is NULL */ +#define STK_Str 0x0002 /* Value is a string */ +#define STK_Int 0x0004 /* Value is an integer */ +#define STK_Real 0x0008 /* Value is a real number */ +#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */ + /* ** An Agg structure describes and Aggregator. Each Agg consists of ** zero or more Aggregator elements (AggElem). Each AggElem contains @@ -131,13 +140,22 @@ struct AggElem { }; /* -** Allowed values for Stack.flags +** A Set structure is used for quick testing to see if a value +** is part of a small set. Sets are used to implement code like +** this: +** x.y IN ('hi','hoo','hum') */ -#define STK_Null 0x0001 /* Value is NULL */ -#define STK_Str 0x0002 /* Value is a string */ -#define STK_Int 0x0004 /* Value is an integer */ -#define STK_Real 0x0008 /* Value is a real number */ -#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */ +typedef struct Set Set; +typedef struct SetElem SetElem; +struct Set { + SetElem *pAll; /* All elements of this set */ + SetElem *apHash[41]; /* A hash table for all elements in this set */ +}; +struct SetElem { + SetElem *pHash; /* Next element with the same hash on zKey */ + SetElem *pNext; /* Next element in a list of them all */ + char zKey[1]; /* Value of this key */ +}; /* ** An instance of the virtual machine @@ -170,6 +188,8 @@ struct Vdbe { int nMem; /* Number of memory locations currently allocated */ Mem *aMem; /* The memory locations */ Agg agg; /* Aggregate information */ + int nSet; /* Number of sets allocated */ + Set *aSet; /* An array of sets */ }; /* @@ -435,6 +455,48 @@ static AggElem *_AggInFocus(Agg *p){ return pFocus; } +/* +** Erase all information from a Set +*/ +static void SetClear(Set *p){ + SetElem *pElem, *pNext; + for(pElem=p->pAll; pElem; pElem=pNext){ + pNext = pElem->pNext; + sqliteFree(pElem); + } + memset(p, 0, sizeof(*p)); +} + +/* +** Insert a new element into the set +*/ +static void SetInsert(Set *p, char *zKey){ + SetElem *pElem; + int h = sqliteHashNoCase(zKey, 0) % ArraySize(p->apHash); + for(pElem=p->apHash[h]; pElem; pElem=pElem->pHash){ + if( strcmp(pElem->zKey, zKey)==0 ) return; + } + pElem = sqliteMalloc( sizeof(pElem) + strlen(zKey) ); + if( pElem==0 ) return; + strcpy(pElem->zKey, zKey); + pElem->pNext = p->pAll; + p->pAll = pElem; + pElem->pHash = p->apHash[h]; + p->apHash[h] = pElem; +} + +/* +** Return TRUE if an element is in the set. Return FALSE if not. +*/ +static int SetTest(Set *p, char *zKey){ + SetElem *pElem; + int h = sqliteHashNoCase(zKey, 0) % ArraySize(p->apHash); + for(pElem=p->apHash[h]; pElem; pElem=pElem->pHash){ + if( strcmp(pElem->zKey, zKey)==0 ) return 1; + } + return 0; +} + /* ** Convert the given stack entity into a string if it isn't one ** already. Return non-zero if we run out of memory. @@ -628,6 +690,12 @@ static void Cleanup(Vdbe *p){ } p->nLineAlloc = 0; AggReset(&p->agg); + for(i=0; inSet; i++){ + SetClear(&p->aSet[i]); + } + sqliteFree(p->aSet); + p->aSet = 0; + p->nSet = 0; } /* @@ -662,25 +730,27 @@ void sqliteVdbeDelete(Vdbe *p){ */ static char *zOpName[] = { 0, "Open", "Close", "Fetch", "New", - "Put", "Distinct", "Delete", "Field", - "Key", "Rewind", "Next", "Destroy", - "Reorganize", "ResetIdx", "NextIdx", "PutIdx", - "DeleteIdx", "MemLoad", "MemStore", "ListOpen", - "ListWrite", "ListRewind", "ListRead", "ListClose", - "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", - "Sort", "SortNext", "SortKey", "SortCallback", - "SortClose", "FileOpen", "FileRead", "FileField", - "FileClose", "AggReset", "AggFocus", "AggIncr", - "AggNext", "AggSet", "AggGet", "MakeRecord", - "MakeKey", "Goto", "If", "Halt", - "ColumnCount", "ColumnName", "Callback", "Integer", - "String", "Null", "Pop", "Dup", - "Pull", "Add", "AddImm", "Subtract", - "Multiply", "Divide", "Min", "Max", - "Like", "Glob", "Eq", "Ne", - "Lt", "Le", "Gt", "Ge", - "IsNull", "NotNull", "Negative", "And", - "Or", "Not", "Concat", "Noop", + "Put", "Distinct", "Found", "NotFound", + "Delete", "Field", "Key", "Rewind", + "Next", "Destroy", "Reorganize", "ResetIdx", + "NextIdx", "PutIdx", "DeleteIdx", "MemLoad", + "MemStore", "ListOpen", "ListWrite", "ListRewind", + "ListRead", "ListClose", "SortOpen", "SortPut", + "SortMakeRec", "SortMakeKey", "Sort", "SortNext", + "SortKey", "SortCallback", "SortClose", "FileOpen", + "FileRead", "FileField", "FileClose", "AggReset", + "AggFocus", "AggIncr", "AggNext", "AggSet", + "AggGet", "SetInsert", "SetFound", "SetNotFound", + "SetClear", "MakeRecord", "MakeKey", "Goto", + "If", "Halt", "ColumnCount", "ColumnName", + "Callback", "Integer", "String", "Null", + "Pop", "Dup", "Pull", "Add", + "AddImm", "Subtract", "Multiply", "Divide", + "Min", "Max", "Like", "Glob", + "Eq", "Ne", "Lt", "Le", + "Gt", "Ge", "IsNull", "NotNull", + "Negative", "And", "Or", "Not", + "Concat", "Noop", }; /* @@ -1696,7 +1766,23 @@ int sqliteVdbeExec( ** does already exist, then fall thru. The record is not retrieved. ** The key is not popped from the stack. */ - case OP_Distinct: { + /* Opcode: Found P1 P2 * + ** + ** Use the top of the stack as a key. If a record with that key + ** does exist in table P1, then jump to P2. If the record + ** does not exist, then fall thru. The record is not retrieved. + ** The key is popped from the stack. + */ + /* Opcode: NotFound P1 P2 * + ** + ** Use the top of the stack as a key. If a record with that key + ** does exist in table P1, then jump to P2. If the record + ** does not exist, then fall thru. The record is not retrieved. + ** The key is popped from the stack. + */ + case OP_Distinct: + case OP_NotFound: + case OP_Found: { int i = pOp->p1; int tos = p->tos; int alreadyExists = 0; @@ -1711,8 +1797,13 @@ int sqliteVdbeExec( p->zStack[tos]); } } - if( !alreadyExists ){ - pc = pOp->p2 - 1; + if( pOp->opcode==OP_Found ){ + if( alreadyExists ) pc = pOp->p2 - 1; + }else{ + if( !alreadyExists ) pc = pOp->p2 - 1; + } + if( pOp->opcode!=OP_Distinct ){ + PopStack(p, 1); } break; } @@ -2749,6 +2840,78 @@ int sqliteVdbeExec( break; } + /* Opcode: SetClear P1 * * + ** + ** Remove all elements from the given Set. + */ + case OP_SetClear: { + int i = pOp->p1; + if( i>=0 && inSet ){ + SetClear(&p->aSet[i]); + } + break; + } + + /* Opcode: SetInsert P1 * P3 + ** + ** If Set p1 does not exist then create it. Then insert value + ** P3 into that set. If P3 is NULL, then insert the top of the + ** stack into the set. + */ + case OP_SetInsert: { + int i = pOp->p1; + if( p->nSet<=i ){ + p->aSet = sqliteRealloc(p->aSet, (i+1)*sizeof(p->aSet[0]) ); + if( p->aSet==0 ) goto no_mem; + memset(&p->aSet[p->nSet], 0, sizeof(p->aSet[0])*(i+1 - p->nSet)); + p->nSet = i+1; + } + if( pOp->p3 ){ + SetInsert(&p->aSet[i], pOp->p3); + }else{ + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + Stringify(p, tos); + SetInsert(&p->aSet[i], p->zStack[tos]); + PopStack(p, 1); + } + break; + } + + /* Opcode: SetFound P1 P2 * + ** + ** Pop the stack once and compare the value popped off with the + ** contents of set P1. If the element popped exists in set P1, + ** then jump to P2. Otherwise fall through. + */ + case OP_SetFound: { + int i = pOp->p1; + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + Stringify(p, tos); + if( i>=0 && inSet && SetTest(&p->aSet[i], p->zStack[tos]) ){ + pc = pOp->p2 - 1; + } + PopStack(p, 1); + } + + /* Opcode: SetNotFound P1 P2 * + ** + ** Pop the stack once and compare the value popped off with the + ** contents of set P1. If the element popped does not exists in + ** set P1, then jump to P2. Otherwise fall through. + */ + case OP_SetNotFound: { + int i = pOp->p1; + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + Stringify(p, tos); + if( i>=0 && inSet && !SetTest(&p->aSet[i], p->zStack[tos]) ){ + pc = pOp->p2 - 1; + } + PopStack(p, 1); + } + /* An other opcode is illegal... */ default: { diff --git a/src/vdbe.h b/src/vdbe.h index 9aaa43abb6..ce2f9734e6 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -27,7 +27,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.7 2000/06/05 21:39:49 drh Exp $ +** $Id: vdbe.h,v 1.8 2000/06/06 01:50:44 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -77,94 +77,101 @@ typedef struct VdbeOp VdbeOp; #define OP_New 4 #define OP_Put 5 #define OP_Distinct 6 -#define OP_Delete 7 -#define OP_Field 8 -#define OP_Key 9 -#define OP_Rewind 10 -#define OP_Next 11 +#define OP_Found 7 +#define OP_NotFound 8 +#define OP_Delete 9 +#define OP_Field 10 +#define OP_Key 11 +#define OP_Rewind 12 +#define OP_Next 13 -#define OP_Destroy 12 -#define OP_Reorganize 13 +#define OP_Destroy 14 +#define OP_Reorganize 15 -#define OP_ResetIdx 14 -#define OP_NextIdx 15 -#define OP_PutIdx 16 -#define OP_DeleteIdx 17 +#define OP_ResetIdx 16 +#define OP_NextIdx 17 +#define OP_PutIdx 18 +#define OP_DeleteIdx 19 -#define OP_MemLoad 18 -#define OP_MemStore 19 +#define OP_MemLoad 20 +#define OP_MemStore 21 -#define OP_ListOpen 20 -#define OP_ListWrite 21 -#define OP_ListRewind 22 -#define OP_ListRead 23 -#define OP_ListClose 24 +#define OP_ListOpen 22 +#define OP_ListWrite 23 +#define OP_ListRewind 24 +#define OP_ListRead 25 +#define OP_ListClose 26 -#define OP_SortOpen 25 -#define OP_SortPut 26 -#define OP_SortMakeRec 27 -#define OP_SortMakeKey 28 -#define OP_Sort 29 -#define OP_SortNext 30 -#define OP_SortKey 31 -#define OP_SortCallback 32 -#define OP_SortClose 33 +#define OP_SortOpen 27 +#define OP_SortPut 28 +#define OP_SortMakeRec 29 +#define OP_SortMakeKey 30 +#define OP_Sort 31 +#define OP_SortNext 32 +#define OP_SortKey 33 +#define OP_SortCallback 34 +#define OP_SortClose 35 -#define OP_FileOpen 34 -#define OP_FileRead 35 -#define OP_FileField 36 -#define OP_FileClose 37 +#define OP_FileOpen 36 +#define OP_FileRead 37 +#define OP_FileField 38 +#define OP_FileClose 39 -#define OP_AggReset 38 -#define OP_AggFocus 39 -#define OP_AggIncr 40 -#define OP_AggNext 41 -#define OP_AggSet 42 -#define OP_AggGet 43 +#define OP_AggReset 40 +#define OP_AggFocus 41 +#define OP_AggIncr 42 +#define OP_AggNext 43 +#define OP_AggSet 44 +#define OP_AggGet 45 -#define OP_MakeRecord 44 -#define OP_MakeKey 45 +#define OP_SetInsert 46 +#define OP_SetFound 47 +#define OP_SetNotFound 48 +#define OP_SetClear 49 -#define OP_Goto 46 -#define OP_If 47 -#define OP_Halt 48 +#define OP_MakeRecord 50 +#define OP_MakeKey 51 -#define OP_ColumnCount 49 -#define OP_ColumnName 50 -#define OP_Callback 51 +#define OP_Goto 52 +#define OP_If 53 +#define OP_Halt 54 -#define OP_Integer 52 -#define OP_String 53 -#define OP_Null 54 -#define OP_Pop 55 -#define OP_Dup 56 -#define OP_Pull 57 +#define OP_ColumnCount 55 +#define OP_ColumnName 56 +#define OP_Callback 57 -#define OP_Add 58 -#define OP_AddImm 59 -#define OP_Subtract 60 -#define OP_Multiply 61 -#define OP_Divide 62 -#define OP_Min 63 -#define OP_Max 64 -#define OP_Like 65 -#define OP_Glob 66 -#define OP_Eq 67 -#define OP_Ne 68 -#define OP_Lt 69 -#define OP_Le 70 -#define OP_Gt 71 -#define OP_Ge 72 -#define OP_IsNull 73 -#define OP_NotNull 74 -#define OP_Negative 75 -#define OP_And 76 -#define OP_Or 77 -#define OP_Not 78 -#define OP_Concat 79 -#define OP_Noop 80 +#define OP_Integer 58 +#define OP_String 59 +#define OP_Null 60 +#define OP_Pop 61 +#define OP_Dup 62 +#define OP_Pull 63 -#define OP_MAX 80 +#define OP_Add 64 +#define OP_AddImm 65 +#define OP_Subtract 66 +#define OP_Multiply 67 +#define OP_Divide 68 +#define OP_Min 69 +#define OP_Max 70 +#define OP_Like 71 +#define OP_Glob 72 +#define OP_Eq 73 +#define OP_Ne 74 +#define OP_Lt 75 +#define OP_Le 76 +#define OP_Gt 77 +#define OP_Ge 78 +#define OP_IsNull 79 +#define OP_NotNull 80 +#define OP_Negative 81 +#define OP_And 82 +#define OP_Or 83 +#define OP_Not 84 +#define OP_Concat 85 +#define OP_Noop 86 + +#define OP_MAX 86 /* ** Prototypes for the VDBE interface. See comments on the implementation