]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
:-) (CVS 55)
authordrh <drh@noemail.net>
Tue, 6 Jun 2000 01:50:43 +0000 (01:50 +0000)
committerdrh <drh@noemail.net>
Tue, 6 Jun 2000 01:50:43 +0000 (01:50 +0000)
FossilOrigin-Name: bd8b2645cc16759571c8c622dce4752bd35c04cf

manifest
manifest.uuid
src/expr.c
src/parse.y
src/select.c
src/sqliteInt.h
src/tokenize.c
src/vdbe.c
src/vdbe.h

index ff6a910d3cb1e856645c56a29db6e6e644541084..dd6f7aa3b00a6c88eb48e24ccf68cb68da74a293 100644 (file)
--- 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
index 36ad03c6660a051d58eeb61a9f582dcaa5cf68a6..fc58e72f7ba61bebdecd3393f3526483f7c12c9a 100644 (file)
@@ -1 +1 @@
-e9ed5d2a3639161fb58856275ba9495f21d366bf
\ No newline at end of file
+bd8b2645cc16759571c8c622dce4752bd35c04cf
\ No newline at end of file
index 5fd5bbb3468fbf5218f37f9492b08e627fa927f7..1b3e8527f5920eed58571632e2ea48a60e516d6b 100644 (file)
 *************************************************************************
 ** 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; i<p->pList->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
 ** 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; i<pExpr->pList->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; i<pExpr->pList->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 && i<n; i++){
+          nErr = sqliteExprCheck(pParse, pExpr->pList->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);
index 125a14051142981df37b89e2cf196cc2c4c1a1c4..8ac4bddcecc4fb645033e91d10823d01fe01db2c 100644 (file)
@@ -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($$);}
index 5317452e6dc4cc5766e6f150914e80d7e8c9ede4..eb84495cab298297845c938405c273b820b51836 100644 (file)
@@ -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; i<pEList->nExpr; 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);
     }
index eca54c49f88f7c08feb15cbf305459a50ada40c9..17332b6d37efa3bfef17abaca97b178c61130100 100644 (file)
@@ -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 */
 };
 
 /*
index 76e0b7a17a6883684e807d8513ee2ff55b5384bc..989010e0499221b44aebdfa0d6029c7720cf7432 100644 (file)
@@ -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 <ctype.h>
@@ -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 },
index 04e8be71d21996c082a41fe2ac3b6332753f4d78..d52e187c873bf0f8f60fd88c83a6f84d64fdcc25 100644 (file)
@@ -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 <unistd.h>
@@ -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; i<p->nSet; 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 && i<p->nSet ){
+          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 && i<p->nSet && 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 && i<p->nSet && !SetTest(&p->aSet[i], p->zStack[tos]) ){
+          pc = pOp->p2 - 1;
+        }
+        PopStack(p, 1);
+      }
+
       /* An other opcode is illegal...
       */
       default: {
index 9aaa43abb6485239ebcd174185fb96dcec61e504..ce2f9734e6eeed8b261807a94eb18bf3eb8becc7 100644 (file)
@@ -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_Destroy            12
-#define OP_Reorganize         13
-
-#define OP_ResetIdx           14
-#define OP_NextIdx            15
-#define OP_PutIdx             16
-#define OP_DeleteIdx          17
-
-#define OP_MemLoad            18
-#define OP_MemStore           19
-
-#define OP_ListOpen           20
-#define OP_ListWrite          21
-#define OP_ListRewind         22
-#define OP_ListRead           23
-#define OP_ListClose          24
-
-#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_FileOpen           34
-#define OP_FileRead           35
-#define OP_FileField          36
-#define OP_FileClose          37
-
-#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_MakeRecord         44
-#define OP_MakeKey            45
-
-#define OP_Goto               46
-#define OP_If                 47
-#define OP_Halt               48
-
-#define OP_ColumnCount        49
-#define OP_ColumnName         50
-#define OP_Callback           51
-
-#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_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_MAX                80
+#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            14
+#define OP_Reorganize         15
+
+#define OP_ResetIdx           16
+#define OP_NextIdx            17
+#define OP_PutIdx             18
+#define OP_DeleteIdx          19
+
+#define OP_MemLoad            20
+#define OP_MemStore           21
+
+#define OP_ListOpen           22
+#define OP_ListWrite          23
+#define OP_ListRewind         24
+#define OP_ListRead           25
+#define OP_ListClose          26
+
+#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           36
+#define OP_FileRead           37
+#define OP_FileField          38
+#define OP_FileClose          39
+
+#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_SetInsert          46
+#define OP_SetFound           47
+#define OP_SetNotFound        48
+#define OP_SetClear           49
+
+#define OP_MakeRecord         50
+#define OP_MakeKey            51
+
+#define OP_Goto               52
+#define OP_If                 53
+#define OP_Halt               54
+
+#define OP_ColumnCount        55
+#define OP_ColumnName         56
+#define OP_Callback           57
+
+#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_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