]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
:-) (CVS 52)
authordrh <drh@noemail.net>
Mon, 5 Jun 2000 18:54:46 +0000 (18:54 +0000)
committerdrh <drh@noemail.net>
Mon, 5 Jun 2000 18:54:46 +0000 (18:54 +0000)
FossilOrigin-Name: c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa

14 files changed:
manifest
manifest.uuid
src/build.c
src/expr.c
src/insert.c
src/parse.y
src/select.c
src/sqliteInt.h
src/vdbe.c
src/vdbe.h
src/where.c
test/copy.test
test/select2.test
test/subselect.test [new file with mode: 0644]

index 1d1c1fcb902d0b6487cd26371fd44dbd74cd079b..6a1c2e8e2dcfaaef628d596851abfbb315b4aa02 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,38 +1,39 @@
-C separate\sSelect\sstructure\s(CVS\s51)
-D 2000-06-05T16:01:39
+C :-)\s(CVS\s52)
+D 2000-06-05T18:54:46
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
 F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
 F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
-F src/build.c 15c4f3844774baa882435223119a18c33810ee94
+F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20
 F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63
 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
 F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc
-F src/expr.c 793c15de4ce2911fa1a74999750bd3c0c9ca513f
-F src/insert.c ddae33b3dea1b4e743092d04240a20def9f88b72
+F src/expr.c 7e87558f88e7a52a902d78446843bda25015531e
+F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf
 F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6
-F src/parse.y 020e5da71e14b63860c97589700eb11bf4699981
-F src/select.c 98f417b72e2edd277602cc14eb5c23743e616e60
+F src/parse.y 79b403240985c71d3f80abe3943e57482287428f
+F src/select.c ab379f969283819ac81b72d0bf7c7aa3fc9389db
 F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb
 F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
-F src/sqliteInt.h da8e0abf204438b812e315279acce05122166440
+F src/sqliteInt.h c01eef2760cebee09c8e1710faf2ebd6fc8a16b6
 F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
 F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1
 F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78
 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
-F src/vdbe.c 108f0e58beee1361bcb8dc2a59c7d54e5f4360bc
-F src/vdbe.h f20a3140905c385237e0891122beccde779c78c7
-F src/where.c bed9a8360cbfbf712bdc397c8e22216a5e5f9800
+F src/vdbe.c b8b78d0eaae9aa68e96b9d37da7d4571905396ca
+F src/vdbe.h 5fd02cb52af0efa0165dd67b6e0036b853e1620f
+F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251
 F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
-F test/copy.test a06548d8dd6e8a006c42c9906fb200b7dee2cc17
+F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226
 F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944
 F test/expr.test db6984d2a6e86118dfce68edade6539495f29022
 F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
 F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf
 F test/select1.test a0b00df77e85adff75c338e487718c5d31f69e3a
-F test/select2.test ebb5497babf02982304841f2eacc985bcc17691e
+F test/select2.test 3cd3c0f9d67e98b1b54af5853679b4a111224410
+F test/subselect.test 6fbfeaa9e3e91c915618336f91163b60487546d2
 F test/table.test 85d6f410d127ec508c6640f02d7c40d218414e81
 F test/tester.tcl 44690d463c1dc83a4c76ccde07cc146a988600f6
 F test/update.test 69459302ea75cafac1479e60b0e36efb88123c0e
@@ -47,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 1cf2873d55b471bb3e397f90dc0868dd88c440a0
-R dfbce0b035783f6473b57dede04bcdd9
+P ce45dea902f9010a1c2c9ba3550dd789e7c15fcd
+R 1d9a27ba1e9706d5d412063b9a982ec3
 U drh
-Z 4195201472d7632b2259e7fe18f40c66
+Z 9480381052dbb4a62eb24ab4005b2230
index b3958a948e16d6d95e2c9d9325985cae438e81bc..54dd59194fd8059d0d18d96bd6d39357a8a34fcb 100644 (file)
@@ -1 +1 @@
-ce45dea902f9010a1c2c9ba3550dd789e7c15fcd
\ No newline at end of file
+c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa
\ No newline at end of file
index 5144f133109034c1f5c723fd797e8e72723763df..d88e57cd4549b5b46369c1dd7616772bb6ee4127 100644 (file)
@@ -33,7 +33,7 @@
 **     COPY
 **     VACUUM
 **
-** $Id: build.c,v 1.14 2000/06/03 18:06:52 drh Exp $
+** $Id: build.c,v 1.15 2000/06/05 18:54:46 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -108,6 +108,8 @@ void sqliteExprDelete(Expr *p){
   if( p==0 ) return;
   if( p->pLeft ) sqliteExprDelete(p->pLeft);
   if( p->pRight ) sqliteExprDelete(p->pRight);
+  if( p->pList ) sqliteExprListDelete(p->pList);
+  if( p->pSelect ) sqliteSelectDelete(p->pSelect);
   sqliteFree(p);
 }
 
index a993834969c2d4eaee9ff2f748de1af2b4ef6bce..5fd5bbb3468fbf5218f37f9492b08e627fa927f7 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** This file contains C code routines used for processing expressions
 **
-** $Id: expr.c,v 1.5 2000/06/04 12:58:37 drh Exp $
+** $Id: expr.c,v 1.6 2000/06/05 18:54:46 drh Exp $
 */
 #include "sqliteInt.h"
 
 ** table fields.  Nodes of the form ID.ID or ID resolve into an
 ** index to the table in the table list and a field offset.  The opcode
 ** for such nodes is changed to TK_FIELD.  The iTable value is changed
-** to the index of the referenced table in pTabList, and the iField value
-** is changed to the index of the field of the referenced table.
+** to the index of the referenced table in pTabList plus the pParse->nTab
+** 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.
+** If it finds any, it generates code to write the value of that select
+** into a memory cell.
 **
 ** Unknown fields or tables provoke an error.  The function returns
 ** the number of errors seen and leaves an error message on pParse->zErrMsg.
@@ -54,7 +59,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
             cnt++;
-            pExpr->iTable = i;
+            pExpr->iTable = i + pParse->nTab;
             pExpr->iField = j;
           }
         }
@@ -104,7 +109,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
             cnt++;
-            pExpr->iTable = i;
+            pExpr->iTable = i + pParse->nTab;
             pExpr->iField = j;
           }
         }
@@ -132,6 +137,14 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
       break;
     }
 
+    case TK_SELECT: {
+      pExpr->iField = pParse->nMem++;
+      if( sqliteSelect(pParse, pExpr->pSelect, -1, pExpr->iField) ){
+        return 1;
+      }
+      break;
+    }
+
     /* For all else, just recursively walk the tree */
     default: {
       if( pExpr->pLeft 
@@ -385,6 +398,10 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       }
       break;
     }
+    case TK_SELECT: {
+      sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iField, 0, 0, 0);
+      break;
+    }
   }
   return;
 }
index 24fcff6705b5cc632e09e7988b16e7a33669a7f6..112ba3386cc3383af5d7b29d3df95423541b9c91 100644 (file)
@@ -24,7 +24,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements.
 **
-** $Id: insert.c,v 1.5 2000/06/04 12:58:38 drh Exp $
+** $Id: insert.c,v 1.6 2000/06/05 18:54:46 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -101,7 +101,10 @@ void sqliteInsert(
       }
     }
   }
-  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  v = pParse->pVdbe;
+  if( v==0 ){
+    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  }
   if( v ){
     Index *pIdx;
     sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0);
index 001b03d0bc7dbe42564ce9af0076a16e480baee0..125a14051142981df37b89e2cf196cc2c4c1a1c4 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.9 2000/06/05 16:01:39 drh Exp $
+** @(#) $Id: parse.y,v 1.10 2000/06/05 18:54:46 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, 0, 0);
+  sqliteSelect(pParse, X, -1, -1);
   sqliteSelectDelete(X);
 }
 
@@ -311,6 +311,10 @@ expr(A) ::= expr(X) NOTNULL.       {A = sqliteExpr(TK_NOTNULL, X, 0, 0);}
 expr(A) ::= NOT expr(X).           {A = sqliteExpr(TK_NOT, X, 0, 0);}
 expr(A) ::= MINUS expr(X). [UMINUS]   {A = sqliteExpr(TK_UMINUS, X, 0, 0);}
 expr(A) ::= PLUS expr(X). [UMINUS]    {A = X;}
+expr(A) ::= LP select(X) RP. {
+  A = sqliteExpr(TK_SELECT, 0, 0, 0);
+  A->pSelect = X;
+}
 
 %type exprlist {ExprList*}
 %destructor exprlist {sqliteExprListDelete($$);}
index e1f94891ece39b5dc576f9461c915e5e6b18f87d..5317452e6dc4cc5766e6f150914e80d7e8c9ede4 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.7 2000/06/05 16:01:39 drh Exp $
+** $Id: select.c,v 1.8 2000/06/05 18:54:46 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -71,11 +71,12 @@ void sqliteSelectDelete(Select *p){
 /*
 ** Generate code for the given SELECT statement.
 **
-** If pDest==0 and iMem<0, then the results of the query are sent to
-** the callback function.  If pDest!=0 then the results are written to
-** the single table specified.  If pDest==0 and iMem>=0 then the result
-** should be a single value which is then stored in memory location iMem
-** of the virtual machine.
+** 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.
 **
 ** This routine returns the number of errors.  If any errors are
 ** encountered, then an appropriate error message is left in
@@ -87,7 +88,7 @@ void sqliteSelectDelete(Select *p){
 int sqliteSelect(
   Parse *pParse,         /* The parser context */
   Select *p,             /* The SELECT statement being coded. */
-  Table *pDest,          /* Write results here, if not NULL */
+  int iDest,             /* Write results to this cursor */
   int iMem               /* Save result in this memory location, if >=0 */
 ){
   int i, j;
@@ -98,14 +99,14 @@ int sqliteSelect(
   IdList *pTabList;      /* List of tables to select from */
   Expr *pWhere;          /* The WHERE clause.  May be NULL */
   ExprList *pOrderBy;    /* The ORDER BY clause.  May be NULL */
-  int distinct;          /* If true, only output distinct results */
-
+  int isDistinct;        /* True if the DISTINCT keyword is present */
+  int distinct;          /* Table to use for the distinct set */
 
   pEList = p->pEList;
   pTabList = p->pSrc;
   pWhere = p->pWhere;
   pOrderBy = p->pOrderBy;
-  distinct = p->isDistinct;
+  isDistinct = p->isDistinct;
 
   /* 
   ** Do not even attempt to generate any code if we have already seen
@@ -125,6 +126,13 @@ int sqliteSelect(
     }
   }
 
+  /* Allocate a temporary table to use for the DISTINCT set, if
+  ** necessary.
+  */
+  if( isDistinct ){
+    distinct = pParse->nTab++;
+  }
+
   /* If the list of fields to retrieve is "*" then replace it with
   ** a list of all fields from all tables.
   */
@@ -133,13 +141,22 @@ int sqliteSelect(
       Table *pTab = pTabList->a[i].pTab;
       for(j=0; j<pTab->nCol; j++){
         Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
-        pExpr->iTable = i;
+        pExpr->iTable = i + pParse->nTab;
         pExpr->iField = j;
         pEList = sqliteExprListAppend(pEList, pExpr, 0);
       }
     }
   }
 
+  /* If writing to memory, only a single column may be output.
+  */
+  if( iMem>=0 && pEList->nExpr>1 ){
+    sqliteSetString(&pParse->zErrMsg, "only a single result allowed for "
+       "a SELECT that is part of an expression", 0);
+    pParse->nErr++;
+    return 1;
+  }
+
   /* Resolve the field names and do a semantics check on all the expressions.
   */
   for(i=0; i<pEList->nExpr; i++){
@@ -180,17 +197,25 @@ int sqliteSelect(
     }
   }
 
-  /* ORDER BY is ignored if this is an aggregate query like count(*)
-  ** since only one row will be returned.
+  /* 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.
   */
-  if( isAgg && pOrderBy ){
+  if( isAgg || iDest>=0 || iMem>=0 ){
     pOrderBy = 0;
   }
 
-  /* Turn off distinct if this is an aggregate
+  /* Turn off distinct if this is an aggregate or writing to memory.
   */
-  if( isAgg ){
-    distinct = 0;
+  if( isAgg || iMem>=0 ){
+    isDistinct = 0;
   }
 
   /* Begin generating code.
@@ -208,38 +233,42 @@ int sqliteSelect(
     sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
   }
 
-  /* Identify column names
+  /* 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.
   */
-  sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
-  for(i=0; i<pEList->nExpr; i++){
-    Expr *p;
-    if( pEList->a[i].zName ){
-      char *zName = pEList->a[i].zName;
-      int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
-      if( zName[0]=='\'' || zName[0]=='"' ){
-        sqliteVdbeDequoteP3(v, addr);
+  if( iDest<0 && iMem<0 ){
+    sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
+    for(i=0; i<pEList->nExpr; i++){
+      Expr *p;
+      if( pEList->a[i].zName ){
+        char *zName = pEList->a[i].zName;
+        int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+        if( zName[0]=='\'' || zName[0]=='"' ){
+          sqliteVdbeDequoteP3(v, addr);
+        }
+        continue;
       }
-      continue;
-    }
-    p = pEList->a[i].pExpr;
-    if( p->op!=TK_FIELD ){
-      char zName[30];
-      sprintf(zName, "field%d", i+1);
-      sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
-    }else{
-      if( pTabList->nId>1 ){
-        char *zName = 0;
-        Table *pTab = pTabList->a[p->iTable].pTab;
-        char *zTab;
-
-        zTab = pTabList->a[p->iTable].zAlias;
-        if( zTab==0 ) zTab = pTab->zName;
-        sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
+      p = pEList->a[i].pExpr;
+      if( p->op!=TK_FIELD ){
+        char zName[30];
+        sprintf(zName, "field%d", i+1);
         sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
-        sqliteFree(zName);
       }else{
-        Table *pTab = pTabList->a[0].pTab;
-        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->aCol[p->iField].zName, 0);
+        if( pTabList->nId>1 ){
+          char *zName = 0;
+          Table *pTab = pTabList->a[p->iTable].pTab;
+          char *zTab;
+  
+          zTab = pTabList->a[p->iTable].zAlias;
+          if( zTab==0 ) zTab = pTab->zName;
+          sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
+          sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+          sqliteFree(zName);
+        }else{
+          Table *pTab = pTabList->a[0].pTab;
+          char *zName = pTab->aCol[p->iField].zName;
+          sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+        }
       }
     }
   }
@@ -263,10 +292,16 @@ int sqliteSelect(
     }
   }
 
+  /* Initialize the memory cell to NULL
+  */
+  if( iMem>=0 ){
+    sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0);
+  }
+
   /* Begin the database scan
   */
-  if( distinct ){
-    distinct = pTabList->nId*2+1;
+  if( isDistinct ){
     sqliteVdbeAddOp(v, OP_Open, distinct, 1, 0, 0);
   }
   pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
@@ -283,13 +318,13 @@ int sqliteSelect(
   /* If the current result is not distinct, script the remainder
   ** of this processing.
   */
-  if( distinct ){
-    int isDistinct = sqliteVdbeMakeLabel(v);
+  if( isDistinct ){
+    int lbl = sqliteVdbeMakeLabel(v);
     sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
-    sqliteVdbeAddOp(v, OP_Distinct, distinct, isDistinct, 0, 0);
+    sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl, 0, 0);
     sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0);
     sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
-    sqliteVdbeAddOp(v, OP_String, 0, 0, "", isDistinct);
+    sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl);
     sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
   }
   
@@ -330,6 +365,14 @@ int sqliteSelect(
       }
       sqliteVdbeAddOp(v, op, p1, 0, 0, 0);
     }
+  }else if( iDest>=0 ){
+    sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_New, iDest, 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_Goto, 0, pWInfo->iBreak, 0, 0);
   }else{
     sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
   }
@@ -348,14 +391,23 @@ int sqliteSelect(
     addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
     sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
     sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
-    sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end);
+    sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
   }
 
   /* If this is an aggregate, then we need to invoke the callback
   ** exactly once.
   */
   if( isAgg ){
-    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+    if( iDest>=0 ){
+      sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_New, iDest, 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);
+    }else{
+      sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+    }
   }
   return 0;
 }
index a0a6f319d08fa7c0b983646f260d86a360355a84..eca54c49f88f7c08feb15cbf305459a50ada40c9 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.14 2000/06/05 16:01:39 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.15 2000/06/05 18:54:46 drh Exp $
 */
 #include "sqlite.h"
 #include "dbbe.h"
@@ -162,6 +162,7 @@ struct Expr {
   Token token;           /* An operand token */
   int iTable, iField;    /* When op==TK_FIELD, then this node means the
                          ** iField-th field of the iTable-th table */
+  Select *pSelect;       /* When the expression is a sub-select */
 };
 
 /*
@@ -191,7 +192,7 @@ struct IdList {
     char *zName;      /* Text of the identifier. */
     char *zAlias;     /* The "B" part of a "A AS B" phrase.  zName is the "A" */
     Table *pTab;      /* Table corresponding to zName */
-    int idx;          /* Index of a field name in the table */
+    int idx;          /* Index of a field named zName in a table */
   } *a;            /* One entry for each identifier on the list */
 };
 
@@ -204,9 +205,11 @@ struct IdList {
 */
 struct WhereInfo {
   Parse *pParse;
-  IdList *pTabList;
-  int iContinue;
-  int iBreak;
+  IdList *pTabList;    /* List of tables in the join */
+  int iContinue;       /* Jump here to continue with next record */
+  int iBreak;          /* Jump here to break out of the loop */
+  int base;            /* Index of first Open opcode */
+  Index *aIdx[32];     /* Indices used for each table */
 };
 
 /*
@@ -239,6 +242,8 @@ struct Parse {
   int explain;         /* True if the EXPLAIN flag is found on the query */
   int initFlag;        /* True if reparsing CREATE TABLEs */
   int nErr;            /* Number of errors seen */
+  int nTab;            /* Number of previously allocated cursors */
+  int nMem;            /* Number of memory cells used so far */
 };
 
 /*
@@ -281,7 +286,7 @@ void sqliteIdListAddAlias(IdList*, Token*);
 void sqliteIdListDelete(IdList*);
 void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
 void sqliteDropIndex(Parse*, Token*);
-int sqliteSelect(Parse*, Select*, Table*, int);
+int sqliteSelect(Parse*, Select*, int, int);
 Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,int);
 void sqliteSelectDelete(Select*);
 void sqliteDeleteFrom(Parse*, Token*, Expr*);
index d302a031b62003f92e9d5e21eaf61cb18a371566..1b836bb8fb115bf317fa65f4bae681a1546cf03f 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.15 2000/06/05 16:01:39 drh Exp $
+** $Id: vdbe.c,v 1.16 2000/06/05 18:54:47 drh Exp $
 */
 #include "sqliteInt.h"
 #include <unistd.h>
@@ -92,10 +92,20 @@ struct Stack {
   int i;         /* Integer value */
   int n;         /* Number of characters in string value, including '\0' */
   int flags;     /* Some combination of STK_Null, STK_Str, STK_Dyn, etc. */
-  double r;      /* Real value */      
+  double r;      /* Real value */
 };
 typedef struct Stack Stack;
 
+/*
+** Memory cells use the same structure as the stack except that space
+** for an arbitrary string is added.
+*/
+struct Mem {
+  Stack s;       /* All values of the memory cell besides string */
+  char *z;       /* String value for this memory cell */
+};
+typedef struct Mem Mem;
+
 /*
 ** Allowed values for Stack.flags
 */
@@ -133,6 +143,8 @@ struct Vdbe {
   char **azField;     /* Data for each file field */
   char *zLine;        /* A single line from the input file */
   int nLineAlloc;     /* Number of spaces allocated for zLine */
+  int nMem;           /* Number of memory locations currently allocated */
+  Mem *aMem;          /* The memory locations */
 };
 
 /*
@@ -464,6 +476,14 @@ static void Cleanup(Vdbe *p){
   sqliteFree(p->aTab);
   p->aTab = 0;
   p->nTable = 0;
+  for(i=0; i<p->nMem; i++){
+    if( p->aMem[i].s.flags & STK_Dyn ){
+      sqliteFree(p->aMem[i].z);
+    }
+  }
+  sqliteFree(p->aMem);
+  p->aMem = 0;
+  p->nMem = 0;
   for(i=0; i<p->nList; i++){
     if( p->apList[i] ){
       sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
@@ -536,20 +556,21 @@ static char *zOpName[] = { 0,
   "Put",            "Distinct",       "Delete",         "Field",
   "Key",            "Rewind",         "Next",           "Destroy",
   "Reorganize",     "ResetIdx",       "NextIdx",        "PutIdx",
-  "DeleteIdx",      "ListOpen",       "ListWrite",      "ListRewind",
-  "ListRead",       "ListClose",      "SortOpen",       "SortPut",
-  "SortMakeRec",    "SortMakeKey",    "Sort",           "SortNext",
-  "SortKey",        "SortCallback",   "SortClose",      "FileOpen",
-  "FileRead",       "FileField",      "FileClose",      "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",
+  "DeleteIdx",      "MemLoad",        "MemStore",       "ListOpen",
+  "ListWrite",      "ListRewind",     "ListRead",       "ListClose",
+  "SortOpen",       "SortPut",        "SortMakeRec",    "SortMakeKey",
+  "Sort",           "SortNext",       "SortKey",        "SortCallback",
+  "SortClose",      "FileOpen",       "FileRead",       "FileField",
+  "FileClose",      "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",         
 };
 
 /*
@@ -2402,6 +2423,64 @@ int sqliteVdbeExec(
         break;
       }
 
+      /* Opcode: MemStore P1 * *
+      **
+      ** Pop a single value of the stack and store that value into memory
+      ** location P1.  P1 should be a small integer since space is allocated
+      ** for all memory locations between 0 and P1 inclusive.
+      */
+      case OP_MemStore: {
+        int i = pOp->p1;
+        int tos = p->tos;
+        Mem *pMem;
+        if( tos<0 ) goto not_enough_stack;
+        if( i>=p->nMem ){
+          int nOld = p->nMem;
+          p->nMem = i + 5;
+          p->aMem = sqliteRealloc(p->aMem, p->nMem*sizeof(p->aMem[0]));
+          if( p->aMem==0 ) goto no_mem;
+          if( nOld<p->nMem ){
+            memset(&p->aMem[nOld], 0, sizeof(p->aMem[0])*(p->nMem-nOld));
+          }
+        }
+        pMem = &p->aMem[i];
+        if( pMem->s.flags & STK_Dyn ){
+          sqliteFree(pMem->z);
+        }
+        pMem->s = p->aStack[tos];
+        if( pMem->s.flags & STK_Str ){
+          pMem->z = 0;
+          sqliteSetString(&pMem->z, p->zStack[tos], 0);
+          pMem->s.flags |= STK_Dyn;
+        }
+        PopStack(p, 1);
+        break;
+      }
+
+      /* Opcode: MemLoad P1 * *
+      **
+      ** Push a copy of the value in memory location P1 onto the stack.
+      */
+      case OP_MemLoad: {
+        int tos = ++p->tos;
+        int i = pOp->p1;
+        if( NeedStack(p, tos) ) goto no_mem;
+        if( i<0 || i>=p->nMem ){
+          p->aStack[tos].flags = STK_Null;
+          p->zStack[tos] = 0;
+        }else{
+          p->aStack[tos] = p->aMem[i].s;
+          if( p->aStack[tos].flags & STK_Str ){
+            char *z = sqliteMalloc(p->aStack[tos].n);
+            if( z==0 ) goto no_mem;
+            memcpy(z, p->aMem[i].z, p->aStack[tos].n);
+            p->zStack[tos] = z;
+            p->aStack[tos].flags |= STK_Dyn;
+          }
+        }
+        break;
+      }
+
       /* An other opcode is illegal...
       */
       default: {
index 3abba05695eabb6d20af4f3e778991b9df857b52..c85a1d3a812c8a5b18c1c25a0eb6ca0f845604d6 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.5 2000/06/04 12:58:39 drh Exp $
+** $Id: vdbe.h,v 1.6 2000/06/05 18:54:47 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -91,70 +91,73 @@ typedef struct VdbeOp VdbeOp;
 #define OP_PutIdx             16
 #define OP_DeleteIdx          17
 
-#define OP_ListOpen           18
-#define OP_ListWrite          19
-#define OP_ListRewind         20
-#define OP_ListRead           21
-#define OP_ListClose          22
-
-#define OP_SortOpen           23
-#define OP_SortPut            24
-#define OP_SortMakeRec        25
-#define OP_SortMakeKey        26
-#define OP_Sort               27
-#define OP_SortNext           28
-#define OP_SortKey            29
-#define OP_SortCallback       30
-#define OP_SortClose          31
-
-#define OP_FileOpen           32
-#define OP_FileRead           33
-#define OP_FileField          34
-#define OP_FileClose          35
-
-#define OP_MakeRecord         36
-#define OP_MakeKey            37
-
-#define OP_Goto               38
-#define OP_If                 39
-#define OP_Halt               40
-
-#define OP_ColumnCount        41
-#define OP_ColumnName         42
-#define OP_Callback           43
-
-#define OP_Integer            44
-#define OP_String             45
-#define OP_Null               46
-#define OP_Pop                47
-#define OP_Dup                48
-#define OP_Pull               49
-
-#define OP_Add                50
-#define OP_AddImm             51
-#define OP_Subtract           52
-#define OP_Multiply           53
-#define OP_Divide             54
-#define OP_Min                55
-#define OP_Max                56
-#define OP_Like               57
-#define OP_Glob               58
-#define OP_Eq                 59
-#define OP_Ne                 60
-#define OP_Lt                 61
-#define OP_Le                 62
-#define OP_Gt                 63
-#define OP_Ge                 64
-#define OP_IsNull             65
-#define OP_NotNull            66
-#define OP_Negative           67
-#define OP_And                68
-#define OP_Or                 69
-#define OP_Not                70
-#define OP_Concat             71
-#define OP_Noop               72
-
-#define OP_MAX                72
+#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_MakeRecord         38
+#define OP_MakeKey            39
+
+#define OP_Goto               40
+#define OP_If                 41
+#define OP_Halt               42
+
+#define OP_ColumnCount        43
+#define OP_ColumnName         44
+#define OP_Callback           45
+
+#define OP_Integer            46
+#define OP_String             47
+#define OP_Null               48
+#define OP_Pop                49
+#define OP_Dup                50
+#define OP_Pull               51
+
+#define OP_Add                52
+#define OP_AddImm             53
+#define OP_Subtract           54
+#define OP_Multiply           55
+#define OP_Divide             56
+#define OP_Min                57
+#define OP_Max                58
+#define OP_Like               59
+#define OP_Glob               60
+#define OP_Eq                 61
+#define OP_Ne                 62
+#define OP_Lt                 63
+#define OP_Le                 64
+#define OP_Gt                 65
+#define OP_Ge                 66
+#define OP_IsNull             67
+#define OP_NotNull            68
+#define OP_Negative           69
+#define OP_And                70
+#define OP_Or                 71
+#define OP_Not                72
+#define OP_Concat             73
+#define OP_Noop               74
+
+#define OP_MAX                74
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index 188d172ba1a681a836ba46d911961ae14b99690e..69c7e1612759f0e77933b24753fd49265490e0d9 100644 (file)
@@ -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.5 2000/05/31 15:34:54 drh Exp $
+** $Id: where.c,v 1.6 2000/06/05 18:54:47 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -86,18 +86,22 @@ static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
 ** In order for this routine to work, the calling function must have
 ** previously invoked sqliteExprResolveIds() on the expression.  See
 ** the header comment on that routine for additional information.
+**
+** "base" is the cursor number (the value of the iTable field) that
+** corresponds to the first entry in the table list.  This is the
+** same as pParse->nTab.
 */
-static int exprTableUsage(Expr *p){
+static int exprTableUsage(int base, Expr *p){
   unsigned int mask = 0;
   if( p==0 ) return 0;
   if( p->op==TK_FIELD ){
-    return 1<<p->iTable;
+    return 1<< (p->iTable - base);
   }
   if( p->pRight ){
-    mask = exprTableUsage(p->pRight);
+    mask = exprTableUsage(base, p->pRight);
   }
   if( p->pLeft ){
-    mask |= exprTableUsage(p->pLeft);
+    mask |= exprTableUsage(base, p->pLeft);
   }
   return mask;
 }
@@ -107,21 +111,25 @@ static int exprTableUsage(Expr *p){
 ** "p" field filled in.  The job of this routine is to analyze the
 ** subexpression and populate all the other fields of the ExprInfo
 ** structure.
+**
+** "base" is the cursor number (the value of the iTable field) that
+** corresponds to the first entyr in the table list.  This is the
+** same as pParse->nTab.
 */
-static void exprAnalyze(ExprInfo *pInfo){
+static void exprAnalyze(int base, ExprInfo *pInfo){
   Expr *pExpr = pInfo->p;
-  pInfo->prereqLeft = exprTableUsage(pExpr->pLeft);
-  pInfo->prereqRight = exprTableUsage(pExpr->pRight);
+  pInfo->prereqLeft = exprTableUsage(base, pExpr->pLeft);
+  pInfo->prereqRight = exprTableUsage(base, pExpr->pRight);
   pInfo->indexable = 0;
   pInfo->idxLeft = -1;
   pInfo->idxRight = -1;
   if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
     if( pExpr->pRight->op==TK_FIELD ){
-      pInfo->idxRight = pExpr->pRight->iTable;
+      pInfo->idxRight = pExpr->pRight->iTable - base;
       pInfo->indexable = 1;
     }
     if( pExpr->pLeft->op==TK_FIELD ){
-      pInfo->idxLeft = pExpr->pLeft->iTable;
+      pInfo->idxLeft = pExpr->pLeft->iTable - base;
       pInfo->indexable = 1;
     }
   }
@@ -150,6 +158,7 @@ WhereInfo *sqliteWhereBegin(
   int nExpr;           /* Number of subexpressions in the WHERE clause */
   int loopMask;        /* One bit set for each outer loop */
   int haveKey;         /* True if KEY is on the stack */
+  int base;            /* First available index for OP_Open opcodes */
   Index *aIdx[32];     /* Index to use on each nested loop.  */
   ExprInfo aExpr[50];  /* The WHERE clause is divided into these expressions */
 
@@ -166,6 +175,7 @@ WhereInfo *sqliteWhereBegin(
   }
   pWInfo->pParse = pParse;
   pWInfo->pTabList = pTabList;
+  base = pWInfo->base = pParse->nTab;
 
   /* Split the WHERE clause into as many as 32 separate subexpressions
   ** where each subexpression is separated by an AND operator.  Any additional
@@ -180,7 +190,7 @@ WhereInfo *sqliteWhereBegin(
   /* Analyze all of the subexpressions.
   */
   for(i=0; i<nExpr; i++){
-    exprAnalyze(&aExpr[i]);
+    exprAnalyze(pParse->nTab, &aExpr[i]);
   }
 
   /* Figure out a good nesting order for the tables.  aOrder[0] will
@@ -261,11 +271,12 @@ WhereInfo *sqliteWhereBegin(
   /* Open all tables in the pTabList and all indices in aIdx[].
   */
   for(i=0; i<pTabList->nId; i++){
-    sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0);
+    sqliteVdbeAddOp(v, OP_Open, base+i, 0, pTabList->a[i].pTab->zName, 0);
     if( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
-      sqliteVdbeAddOp(v, OP_Open, pTabList->nId+i, 0, aIdx[i]->zName, 0);
+      sqliteVdbeAddOp(v, OP_Open, base+pTabList->nId+i, 0, aIdx[i]->zName, 0);
     }
   }
+  memcpy(pWInfo->aIdx, aIdx, sizeof(aIdx));
 
   /* Generate the code to do the search
   */
@@ -281,7 +292,7 @@ WhereInfo *sqliteWhereBegin(
       /* Case 1:  There was no usable index.  We must do a complete
       ** scan of the table.
       */
-      sqliteVdbeAddOp(v, OP_Next, idx, brk, 0, cont);
+      sqliteVdbeAddOp(v, OP_Next, base+idx, brk, 0, cont);
       haveKey = 0;
     }else{
       /* Case 2:  We do have a usable index in pIdx.
@@ -308,8 +319,8 @@ WhereInfo *sqliteWhereBegin(
         }
       }
       sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_Fetch, pTabList->nId+i, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_NextIdx, pTabList->nId+i, brk, 0, cont);
+      sqliteVdbeAddOp(v, OP_Fetch, base+pTabList->nId+i, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_NextIdx, base+pTabList->nId+i, brk, 0, cont);
       if( i==pTabList->nId-1 && pushKey ){
         haveKey = 1;
       }else{
@@ -327,7 +338,7 @@ WhereInfo *sqliteWhereBegin(
       if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue;
       if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
       if( haveKey ){
-        sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
+        sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
         haveKey = 0;
       }
       sqliteExprIfFalse(pParse, aExpr[j].p, cont);
@@ -348,8 +359,21 @@ WhereInfo *sqliteWhereBegin(
 */
 void sqliteWhereEnd(WhereInfo *pWInfo){
   Vdbe *v = pWInfo->pParse->pVdbe;
+  int i;
+  int brk = pWInfo->iBreak;
+  int base = pWInfo->base;
+
   sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
-  sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak);
+  for(i=0; i<pWInfo->pTabList->nId; i++){
+    sqliteVdbeAddOp(v, OP_Close, base+i, 0, 0, brk);
+    brk = 0;
+    if( i<ARRAYSIZE(pWInfo->aIdx) && pWInfo->aIdx[i]!=0 ){
+      sqliteVdbeAddOp(v, OP_Close, base+pWInfo->pTabList->nId+i, 0, 0, 0);
+    }
+  }
+  if( brk!=0 ){
+    sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, brk);
+  }
   sqliteFree(pWInfo);
   return;
 }
index ebbbd557cb5e94370a6efa1736a2e3430b7aec54..97b42e5173d65a3ad2aab2e8c2cbc5858f4d9306 100644 (file)
@@ -23,7 +23,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the COPY statement.
 #
-# $Id: copy.test,v 1.3 2000/06/05 02:07:05 drh Exp $
+# $Id: copy.test,v 1.4 2000/06/05 18:54:47 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -81,7 +81,6 @@ do_test copy-1.4 {
   execsql {COPY test1 FROM 'data2.txt'}
   execsql {SELECT * FROM test1 ORDER BY one}
 } {11 22 33}
-return
 
 # Test out the USING DELIMITERS clause
 #
index 65439abc36301a7076adbe41a4275ecaa06f4856..6b5c29bed59668dd616b19b1230aca536cb6c2cd 100644 (file)
@@ -23,7 +23,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select2.test,v 1.4 2000/06/02 15:05:33 drh Exp $
+# $Id: select2.test,v 1.5 2000/06/05 18:54:47 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -71,14 +71,16 @@ do_test select2-1.2 {
 
 # Create a largish table
 #
-execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int)}
-set f [open ./testdata1.txt w]
-for {set i 1} {$i<=30000} {incr i} {
-  puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]"
-}
-close $f
-execsql {COPY tbl2 FROM './testdata1.txt'}
-file delete -force ./testdata1.txt
+do_test select2-2.0 {
+  execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int)}
+  set f [open ./testdata1.txt w]
+  for {set i 1} {$i<=30000} {incr i} {
+    puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]"
+  }
+  close $f
+  execsql {COPY tbl2 FROM './testdata1.txt'}
+  file delete -force ./testdata1.txt
+} {}
 
 do_test select2-2.1 {
   execsql {SELECT count(*) FROM tbl2}
@@ -91,9 +93,11 @@ do_test select2-3.1 {
   execsql {SELECT f1 FROM tbl2 WHERE f2==1000}
 } {500}
 
-execsql {CREATE INDEX idx1 ON tbl2(f2)}
+do_test select2-3.2a {
+  execsql {CREATE INDEX idx1 ON tbl2(f2)}
+} {}
 
-do_test select2-3.2 {
+do_test select2-3.2b {
   execsql {SELECT f1 FROM tbl2 WHERE f2==1000}
 } {500}
 
diff --git a/test/subselect.test b/test/subselect.test
new file mode 100644 (file)
index 0000000..c3b4e8b
--- /dev/null
@@ -0,0 +1,90 @@
+# 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 SELECT statements that are part of
+# expressions.
+#
+# $Id: subselect.test,v 1.1 2000/06/05 18:54:47 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Basic sanity checking.  Try a simple subselect.
+#
+do_test subselect-1.1 {
+  execsql {
+    CREATE TABLE t1(a int, b int);
+    INSERT INTO t1 VALUES(1,2);
+    INSERT INTO t1 VALUES(3,4);
+    INSERT INTO t1 VALUES(5,6);
+  }
+  execsql {SELECT * FROM t1 WHERE a = (SELECT count(*) FROM t1)}
+} {3 4}
+
+# Try a select with more than one result column.
+#
+do_test subselect-1.2 {
+  set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
+  lappend v $msg
+} {1 {only a single result allowed for a SELECT this is part of an expression}}
+
+# A subselect without an aggregate.
+#
+do_test subselect-1.3a {
+  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=2)}
+} {2}
+do_test subselect-1.3b {
+  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=4)}
+} {4}
+do_test subselect-1.3c {
+  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=6)}
+} {6}
+do_test subselect-1.3c {
+  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=8)}
+} {}
+
+# What if the subselect doesn't return any value.  We should get
+# NULL as the result.  Check it out.
+#
+do_test subselect-1.4 {
+  execsql {INSERT INTO t1 VALUES(NULL,8)}
+  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=5)}
+} {8}
+
+# Try multiple subselects within a single expression.
+#
+do_test subselect-1.5 {
+  execsql {
+    CREATE TABLE t2(x int, y int);
+    INSERT INTO t2 VALUES(1,2);
+    INSERT INTO t2 VALUES(2,4);
+    INSERT INTO t2 VALUES(3,8);
+    INSERT INTO t2 VALUES(4,16);
+  }
+  execsql {
+    SELECT y from t2 
+    WHERE x = (SELECT sum(b) FROM t1 where a notnull) - (SELECT sum(a) FROM t1)
+  }
+} {8}
+
+finish_test