]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Subquery flattening is implemented and passes all regression tests.
authordrh <drh@noemail.net>
Sat, 2 Mar 2002 17:04:07 +0000 (17:04 +0000)
committerdrh <drh@noemail.net>
Sat, 2 Mar 2002 17:04:07 +0000 (17:04 +0000)
We still need to add addition tests to the suite to further exercise
the flattener, however. (CVS 408)

FossilOrigin-Name: d5d3e79cc58da5bd315cc1fea1f7cbf46274da16

14 files changed:
manifest
manifest.uuid
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/parse.y
src/select.c
src/sqliteInt.h
src/update.c
src/where.c
test/func.test
test/select1.test
www/changes.tcl

index 15d3d861dce5fcfcd0cf27cc27f5fc836583b2a5..b0ea0909d958662890206c81d6cc0335862592ad 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bug\sfix\swhen\s-DMEMORY_DEBUG\sis\soff.\s(CVS\s407)
-D 2002-02-28T04:10:30
+C Subquery\sflattening\sis\simplemented\sand\spasses\sall\sregression\stests.\nWe\sstill\sneed\sto\sadd\saddition\stests\sto\sthe\ssuite\sto\sfurther\sexercise\nthe\sflattener,\showever.\s(CVS\s408)
+D 2002-03-02T17:04:08
 F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
 F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -21,27 +21,27 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d
 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
-F src/build.c 7ada2426caba70cb1072ba268bedb694b5018065
-F src/delete.c 950d8f9097361419f1963875f9943344b469cf02
-F src/expr.c b70bedaffd27ea24c5c2a197a88b07e82dfa4967
+F src/build.c 2f6d3136e6b824b2b446c54db2d2be5703033203
+F src/delete.c bf569eeb66dc851966b5681e5154d5fe2aee92c2
+F src/expr.c 17e3db6f115d60530a55530e3046312196c5eb36
 F src/func.c 0db438ba17f3394dc5a3ffcd2ee41ca0c8e80b21
 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
-F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11
+F src/insert.c 4fb3428f591afe106f6e60029a61f87fa0e79920
 F src/main.c 5651146585ae613e759fcf372ee064e4940c2463
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea
 F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
 F src/pager.c 9761c79ccb844bf29ffc5cbed4fa1a32e0740147
 F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
-F src/parse.y fc460cda6f475beae963c7f9c737cf13f44f3420
+F src/parse.y d62960cdee2d2e7821f277d2fe63d823c86602ba
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 1851abd794ad81639bb7d8ac4b24c8c70cd0ec17
+F src/select.c b99de04f6f2414bb21a065150e0748ccd8329519
 F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in a9b5772604265f98f3120573ef29e37b9d917216
-F src/sqliteInt.h 9cd512d5be2d2838950e5ace7f92e14ab4804e70
+F src/sqliteInt.h df68f09091ab37d904020ac48ca2ea29e4985442
 F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52
 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
 F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
@@ -49,11 +49,11 @@ F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
 F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
 F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
-F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997
+F src/update.c 943b821901efb796dc82d91653621aeeb48d223b
 F src/util.c 00a35b421c92ae0d7cfa51bd87f7d4995f464d19
 F src/vdbe.c 91311e99efe980459a78e8f5b9e5456d772c9e23
 F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc
-F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585
+F src/where.c 34d91fd5d822c2663caeb023f72d60df316ebf29
 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
@@ -63,7 +63,7 @@ F test/conflict.test c794c6c8f6e59918107dbab2d201ae454bb47db8
 F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a
 F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
 F test/expr.test c8a495050dcec3f9e68538c3ef466726933302c1
-F test/func.test 13572d84cb0f5d4dbd9a51bf767eac047e6f9779
+F test/func.test 4359344586067e79abf4c710c4737d67ed3cf963
 F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
 F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
 F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9
@@ -82,7 +82,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
 F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
 F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
 F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
-F test/select1.test ec4c20514598ae9b52abbd5e17488c8dbcdc74d1
+F test/select1.test 572d53f7b28c35a1efdce207f7579523358ba637
 F test/select2.test ed2c1882857106b85478f54f67000e14966be4c4
 F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
 F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
@@ -112,7 +112,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
 F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
 F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
 F www/c_interface.tcl 63efc40f09e2f0d8fea43d174103248b160fdf0e
-F www/changes.tcl 0c40569ef5c92af1fb211ea1f679113a44c60f19
+F www/changes.tcl 18df8bc8dbcefbfd9538ce476d4fcbc193ac3352
 F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
 F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
 F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0
@@ -127,7 +127,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P e9fd9e7b0fed445b48e7024ecde0354fff1478a6
-R 963d95bca45cb4c9e29d8cc361ba95c2
+P e14b0c82f3514f41934a7c0d173b6fdb186aafc8
+R f60c0ec90c7ca3b07c137c446692b495
 U drh
-Z bd4f48a03082cea631000ad6c4df51a3
+Z 28d7598277d139ff75ed8a6bf05f4a82
index 07f84c335f5e4a0aae1f029821fad99bfca37819..7018ef2a6c2c2ed61e7609a832ffea486a539b81 100644 (file)
@@ -1 +1 @@
-e14b0c82f3514f41934a7c0d173b6fdb186aafc8
\ No newline at end of file
+d5d3e79cc58da5bd315cc1fea1f7cbf46274da16
\ No newline at end of file
index 6f6acc3f7d1ea00009c50ad18d3a435e62e08e53..b5b6579f53d989cd142763c897b5fb3212480b66 100644 (file)
@@ -17,7 +17,7 @@
 **     DROP TABLE
 **     CREATE INDEX
 **     DROP INDEX
-**     creating expressions and ID lists
+**     creating ID lists
 **     COPY
 **     VACUUM
 **     BEGIN TRANSACTION
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.80 2002/02/27 01:47:12 drh Exp $
+** $Id: build.c,v 1.81 2002/03/02 17:04:08 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -791,7 +791,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
       int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
       sqliteVdbeAddOp(v, op, 1, 0);
       pParse->nTab = 2;
-      sqliteSelect(pParse, pSelect, SRT_Table, 1);
+      sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0);
     }
     sqliteEndWriteOperation(pParse);
   }
index d815a45009979d9c9e9d1e37d666d1f5c21114f9..2d5771a4e4aeabfa689027b0b505ecdb71361465 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.27 2002/02/23 02:32:10 drh Exp $
+** $Id: delete.c,v 1.28 2002/03/02 17:04:08 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -102,9 +102,9 @@ void sqliteDeleteFrom(
 
   /* Resolve the column names in all the expressions.
   */
+  base = pParse->nTab++;
   if( pWhere ){
-    sqliteExprResolveInSelect(pParse, pWhere);
-    if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
+    if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){
       goto delete_from_cleanup;
     }
     if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
@@ -135,6 +135,7 @@ void sqliteDeleteFrom(
       int endOfLoop = sqliteVdbeMakeLabel(v);
       int addr;
       openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
+      assert( base==0 );
       sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
       sqliteVdbeAddOp(v, OP_Rewind, 0, sqliteVdbeCurrentAddr(v)+2);
       addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
@@ -154,7 +155,7 @@ void sqliteDeleteFrom(
   else{
     /* Begin the database scan
     */
-    pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
+    pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1);
     if( pWInfo==0 ) goto delete_from_cleanup;
 
     /* Remember the key of every item to be deleted.
@@ -172,7 +173,6 @@ void sqliteDeleteFrom(
     ** database scan.  We have to delete items after the scan is complete
     ** because deleting an item can change the scan order.
     */
-    base = pParse->nTab;
     sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
     openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
     sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
index f265f0f4a9c9738f3e6f20fda1ca4fa4dbd00773..46c56b57fb7b6f900c8e0126ba9ac219cadfcda9 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.51 2002/02/28 03:04:48 drh Exp $
+** $Id: expr.c,v 1.52 2002/03/02 17:04:08 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -172,6 +172,9 @@ Expr *sqliteExprDup(Expr *p){
   pNew->pLeft = sqliteExprDup(p->pLeft);
   pNew->pRight = sqliteExprDup(p->pRight);
   pNew->pList = sqliteExprListDup(p->pList);
+  pNew->iTable = p->iTable;
+  pNew->iColumn = p->iColumn;
+  pNew->iAgg = p->iAgg;
   pNew->token = p->token;
   pNew->span = p->span;
   pNew->pSelect = sqliteSelectDup(p->pSelect);
@@ -310,40 +313,6 @@ int sqliteExprIsConstant(Expr *p){
   return 0;
 }
 
-/*
-** Walk the expression tree and process operators of the form:
-**
-**       expr IN (SELECT ...)
-**
-** These operators have to be processed before column names are
-** resolved because each such operator increments pParse->nTab
-** to reserve cursor numbers for its own use.  But pParse->nTab
-** needs to be constant once we begin resolving column names.  For
-** that reason, this procedure needs to be called on every expression
-** before sqliteExprResolveIds() is called on any expression.
-**
-** Actually, the processing of IN-SELECT is only started by this
-** routine.  This routine allocates a cursor number to the IN-SELECT
-** and then moves on.  The code generation is done by 
-** sqliteExprResolveIds() which must be called afterwards.
-*/
-void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){
-  if( pExpr==0 ) return;
-  if( pExpr->op==TK_IN && pExpr->pSelect!=0 ){
-    pExpr->iTable = pParse->nTab++;
-  }else{
-    if( pExpr->pLeft ) sqliteExprResolveInSelect(pParse, pExpr->pLeft);
-    if( pExpr->pRight ) sqliteExprResolveInSelect(pParse, pExpr->pRight);
-    if( pExpr->pList ){
-      int i;
-      ExprList *pList = pExpr->pList;
-      for(i=0; i<pList->nExpr; i++){
-        sqliteExprResolveInSelect(pParse, pList->a[i].pExpr);
-      }
-    }
-  }
-}
-
 /*
 ** Return TRUE if the given string is a row-id column name.
 */
@@ -360,7 +329,7 @@ static int sqliteIsRowid(const char *z){
 ** index to the table in the table list and a column offset.  The 
 ** Expr.opcode for such nodes is changed to TK_COLUMN.  The Expr.iTable
 ** value is changed to the index of the referenced table in pTabList
-** plus the pParse->nTab value.  This value will ultimately become the
+** plus the "base" value.  The base value will ultimately become the
 ** VDBE cursor number for a cursor that is pointing into the referenced
 ** table.  The Expr.iColumn value is changed to the index of the column 
 ** of the referenced table.  The Expr.iColumn value for the special
@@ -387,11 +356,13 @@ static int sqliteIsRowid(const char *z){
 */
 int sqliteExprResolveIds(
   Parse *pParse,     /* The parser context */
+  int base,          /* VDBE cursor number for first entry in pTabList */
   IdList *pTabList,  /* List of tables used to resolve column names */
   ExprList *pEList,  /* List of expressions used to resolve "AS" */
   Expr *pExpr        /* The expression to be analyzed. */
 ){
   if( pExpr==0 || pTabList==0 ) return 0;
+  assert( base+pTabList->nId<=pParse->nTab );
   switch( pExpr->op ){
     /* A lone identifier.  Try and match it as follows:
     **
@@ -418,7 +389,7 @@ int sqliteExprResolveIds(
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
             cnt++;
-            pExpr->iTable = i + pParse->nTab;
+            pExpr->iTable = i + base;
             if( j==pTab->iPKey ){
               /* Substitute the record number for the INTEGER PRIMARY KEY */
               pExpr->iColumn = -1;
@@ -444,7 +415,7 @@ int sqliteExprResolveIds(
       }
       if( cnt==0 && sqliteIsRowid(z) ){
         pExpr->iColumn = -1;
-        pExpr->iTable = pParse->nTab;
+        pExpr->iTable = base;
         cnt = 1 + (pTabList->nId>1);
         pExpr->op = TK_COLUMN;
       }
@@ -496,11 +467,11 @@ int sqliteExprResolveIds(
           zTab = pTab->zName;
         }
         if( sqliteStrICmp(zTab, zLeft)!=0 ) continue;
-        if( 0==(cntTab++) ) pExpr->iTable = i + pParse->nTab;
+        if( 0==(cntTab++) ) pExpr->iTable = i + base;
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
             cnt++;
-            pExpr->iTable = i + pParse->nTab;
+            pExpr->iTable = i + base;
             if( j==pTab->iPKey ){
               /* Substitute the record number for the INTEGER PRIMARY KEY */
               pExpr->iColumn = -1;
@@ -540,7 +511,7 @@ int sqliteExprResolveIds(
     case TK_IN: {
       Vdbe *v = sqliteGetVdbe(pParse);
       if( v==0 ) return 1;
-      if( sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){
+      if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pLeft) ){
         return 1;
       }
       if( pExpr->pSelect ){
@@ -550,8 +521,9 @@ int sqliteExprResolveIds(
         ** table.  The cursor number of the temporary table has already
         ** been put in iTable by sqliteExprResolveInSelect().
         */
+        pExpr->iTable = pParse->nTab++;
         sqliteVdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 1);
-        if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) );
+        sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable, 0,0,0);
       }else if( pExpr->pList ){
         /* Case 2:     expr IN (exprlist)
         **
@@ -601,7 +573,7 @@ int sqliteExprResolveIds(
       ** of the memory cell in iColumn.
       */
       pExpr->iColumn = pParse->nMem++;
-      if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn) ){
+      if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn,0,0,0) ){
         return 1;
       }
       break;
@@ -610,18 +582,19 @@ int sqliteExprResolveIds(
     /* For all else, just recursively walk the tree */
     default: {
       if( pExpr->pLeft
-      && sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){
+      && sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pLeft) ){
         return 1;
       }
       if( pExpr->pRight 
-      && sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pRight) ){
+      && sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pRight) ){
         return 1;
       }
       if( pExpr->pList ){
         int i;
         ExprList *pList = pExpr->pList;
         for(i=0; i<pList->nExpr; i++){
-          if( sqliteExprResolveIds(pParse,pTabList,pEList,pList->a[i].pExpr) ){
+          Expr *pArg = pList->a[i].pExpr;
+          if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pArg) ){
             return 1;
           }
         }
index c12c32a70c8274891a1a52aa204b20cfbd904d1e..b7617289e5ca480734d8ce6e12651c83c03f8063 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.45 2002/02/23 02:32:10 drh Exp $
+** $Id: insert.c,v 1.46 2002/03/02 17:04:08 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -84,7 +84,7 @@ void sqliteInsert(
     int rc;
     srcTab = pParse->nTab++;
     sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0);
-    rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
+    rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab, 0,0,0);
     if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
     assert( pSelect->pEList );
     nColumn = pSelect->pEList->nExpr;
@@ -94,12 +94,9 @@ void sqliteInsert(
     srcTab = -1;
     assert( pList );
     nColumn = pList->nExpr;
-    for(i=0; i<nColumn; i++){
-      sqliteExprResolveInSelect(pParse, pList->a[i].pExpr);
-    }
     dummy.nId = 0;
     for(i=0; i<nColumn; i++){
-      if( sqliteExprResolveIds(pParse, &dummy, 0, pList->a[i].pExpr) ){
+      if( sqliteExprResolveIds(pParse, 0, &dummy, 0, pList->a[i].pExpr) ){
         goto insert_cleanup;
       }
     }
@@ -183,6 +180,7 @@ void sqliteInsert(
     sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum);
     sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
   }
+  pParse->nTab += idx;
 
   /* If the data source is a SELECT statement, then we have to create
   ** a loop because there might be multiple rows of data.  If the data
index fee981f632318734c2267a76d588fbbb610345f7..6e3f45c44287c0618267720db3bae67d3df3ba91 100644 (file)
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.53 2002/02/23 02:32:10 drh Exp $
+** @(#) $Id: parse.y,v 1.54 2002/03/02 17:04:08 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -202,7 +202,7 @@ cmd ::= DROP VIEW ids(X). {
 //////////////////////// The SELECT statement /////////////////////////////////
 //
 cmd ::= select(X).  {
-  sqliteSelect(pParse, X, SRT_Callback, 0);
+  sqliteSelect(pParse, X, SRT_Callback, 0, 0, 0, 0);
   sqliteSelectDelete(X);
 }
 
index f71016524e64dd804b3d4ac945bbd9a6fd65838b..94eb2ae0be9478d1cdf5dd026ffe933436b3e641 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.70 2002/02/28 01:46:13 drh Exp $
+** $Id: select.c,v 1.71 2002/03/02 17:04:08 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -231,8 +231,12 @@ static void generateSortTail(Vdbe *v, int nColumn){
 ** are in the result and the name for each column.  This information
 ** is used to provide "argc" and "azCol[]" values in the callback.
 */
-static 
-void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
+static void generateColumnNames(
+  Parse *pParse,      /* Parser context */
+  int base,           /* VDBE cursor corresponding to first entry in pTabList */
+  IdList *pTabList,   /* List of tables */
+  ExprList *pEList    /* Expressions defining the result set */
+){
   Vdbe *v = pParse->pVdbe;
   int i;
   if( pParse->colNamesSet || v==0 || sqlite_malloc_failed ) return;
@@ -255,7 +259,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
       sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
       sqliteVdbeCompressSpace(v, addr);
     }else if( p->op==TK_COLUMN && pTabList ){
-      Table *pTab = pTabList->a[p->iTable - pParse->nTab].pTab;
+      Table *pTab = pTabList->a[p->iTable - base].pTab;
       char *zCol;
       int iCol = p->iColumn;
       if( iCol<0 ) iCol = pTab->iPKey;
@@ -265,7 +269,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
         char *zName = 0;
         char *zTab;
  
-        zTab = pTabList->a[p->iTable - pParse->nTab].zAlias;
+        zTab = pTabList->a[p->iTable - base].zAlias;
         if( showFullNames || zTab==0 ) zTab = pTab->zName;
         sqliteSetString(&zName, zTab, ".", zCol, 0);
         sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
@@ -640,7 +644,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
 
       /* Code the SELECT statements to our left
       */
-      rc = sqliteSelect(pParse, pPrior, priorOp, unionTab);
+      rc = sqliteSelect(pParse, pPrior, priorOp, unionTab, 0, 0, 0);
       if( rc ) return rc;
 
       /* Code the current SELECT statement
@@ -651,7 +655,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
          case TK_ALL:     op = SRT_Table;    break;
       }
       p->pPrior = 0;
-      rc = sqliteSelect(pParse, p, op, unionTab);
+      rc = sqliteSelect(pParse, p, op, unionTab, 0, 0, 0);
       p->pPrior = pPrior;
       if( rc ) return rc;
 
@@ -661,7 +665,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
       if( eDest!=priorOp ){
         int iCont, iBreak, iStart;
         assert( p->pEList );
-        generateColumnNames(pParse, 0, p->pEList);
+        generateColumnNames(pParse, p->base, 0, p->pEList);
         iBreak = sqliteVdbeMakeLabel(v);
         iCont = sqliteVdbeMakeLabel(v);
         sqliteVdbeAddOp(v, OP_Rewind, unionTab, iBreak);
@@ -698,7 +702,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
 
       /* Code the SELECTs to our left into temporary table "tab1".
       */
-      rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
+      rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1, 0, 0, 0);
       if( rc ) return rc;
 
       /* Code the current SELECT into temporary table "tab2"
@@ -706,7 +710,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
       sqliteVdbeAddOp(v, OP_OpenTemp, tab2, 1);
       sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1);
       p->pPrior = 0;
-      rc = sqliteSelect(pParse, p, SRT_Union, tab2);
+      rc = sqliteSelect(pParse, p, SRT_Union, tab2, 0, 0, 0);
       p->pPrior = pPrior;
       if( rc ) return rc;
 
@@ -714,7 +718,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
       ** tables.
       */
       assert( p->pEList );
-      generateColumnNames(pParse, 0, p->pEList);
+      generateColumnNames(pParse, p->base, 0, p->pEList);
       iBreak = sqliteVdbeMakeLabel(v);
       iCont = sqliteVdbeMakeLabel(v);
       sqliteVdbeAddOp(v, OP_Rewind, tab1, iBreak);
@@ -746,6 +750,74 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
   return 0;
 }
 
+/*
+** Recursively scan through an expression tree.  For every reference
+** to a column in table number iFrom, change that reference to the
+** same column in table number iTo.
+*/
+static void changeTables(Expr *pExpr, int iFrom, int iTo){
+  if( pExpr==0 ) return;
+  if( pExpr->op==TK_COLUMN && pExpr->iTable==iFrom ){
+    pExpr->iTable = iTo;
+  }else{
+    changeTables(pExpr->pLeft, iFrom, iTo);
+    changeTables(pExpr->pRight, iFrom, iTo);
+    if( pExpr->pList ){
+      int i;
+      for(i=0; i<pExpr->pList->nExpr; i++){
+        changeTables(pExpr->pList->a[i].pExpr, iFrom, iTo);
+      }
+    }
+  }
+}
+
+/*
+** Scan through the expression pExpr.  Replace every reference to
+** a column in table number iTable with a copy of the corresponding
+** entry in pEList.  When make a copy of pEList, change references
+** to columns in table iSub into references to table iTable.
+**
+** This routine is part of the flattening procedure.  A subquery
+** whose result set is defined by pEList appears as entry in the
+** FROM clause of a SELECT such that the VDBE cursor assigned to that
+** FORM clause entry is iTable.  This routine make the necessary 
+** changes to pExpr so that it refers directly to the source table
+** of the subquery rather the result set of the subquery.
+*/
+static void substExpr(Expr *pExpr, int iTable, ExprList *pEList, int iSub){
+  if( pExpr==0 ) return;
+  if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){
+    Expr *pNew;
+    assert( pEList!=0 && pExpr->iColumn>=0 && pExpr->iColumn<pEList->nExpr );
+    assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 );
+    pNew = pEList->a[pExpr->iColumn].pExpr;
+    assert( pNew!=0 );
+    pExpr->op = pNew->op;
+    pExpr->pLeft = sqliteExprDup(pNew->pLeft);
+    pExpr->pRight = sqliteExprDup(pNew->pRight);
+    pExpr->pList = sqliteExprListDup(pNew->pList);
+    pExpr->iTable = pNew->iTable;
+    pExpr->iColumn = pNew->iColumn;
+    pExpr->iAgg = pNew->iAgg;
+    if( iSub!=iTable ){
+      changeTables(pExpr, iSub, iTable);
+    }
+  }else{
+    static void substExprList(ExprList*,int,ExprList*,int);
+    substExpr(pExpr->pLeft, iTable, pEList, iSub);
+    substExpr(pExpr->pRight, iTable, pEList, iSub);
+    substExprList(pExpr->pList, iTable, pEList, iSub);
+  }
+}
+static void 
+substExprList(ExprList *pList, int iTable, ExprList *pEList, int iSub){
+  int i;
+  if( pList==0 ) return;
+  for(i=0; i<pList->nExpr; i++){
+    substExpr(pList->a[i].pExpr, iTable, pEList, iSub);
+  }
+}
+
 /*
 ** This routine attempts to flatten subqueries in order to speed
 ** execution.  It returns 1 if it makes changes and 0 if no flattening
@@ -761,48 +833,115 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
 ** run the outer query on that temporary table.  This requires two
 ** passes over the data.  Furthermore, because the temporary table
 ** has no indices, the WHERE clause on the outer query cannot be
-** optimized using indices.
+** optimized.
 **
-** This routine attempts to write queries such as the above into
+** This routine attempts to rewrite queries such as the above into
 ** a single flat select, like this:
 **
 **     SELECT x+y AS a FROM t1 WHERE z<100 AND a>5
 **
 ** The code generated for this simpification gives the same result
-** but only has to scan the data once.
+** but only has to scan the data once.  And because indices might 
+** exist on the table t1, a complete scan of the data might be
+** avoided.
+**
+** Flattening is only attempted if all of the following are true:
 **
-** Generally speaking, flattening is only possible if the subquery
-** query is a simple query without a GROUP BY clause or the DISTINCT
-** keyword and the outer query is not a join. 
+**   (1)  The subquery and the outer query do not both use aggregates.
 **
-** If flattening is not possible, this routine is a no-op and return 0.
-** If flattening is possible, this routine  rewrites the query into
-** the simplified form and return 1.
+**   (2)  The subquery is not an aggregate or the outer query is not a join.
 **
-** All of the expression analysis must occur before this routine runs.
-** This routine depends on the results of the expression analysis.
+**   (3)  The subquery is not a join.
+**
+**   (4)  The subquery is not DISTINCT or the outer query is not a join.
+**
+**   (5)  The subquery is not DISTINCT or the outer query does not use
+**        aggregates.
+**
+**   (6)  The subquery does not use aggregates or the outer query is not
+**        DISTINCT.
+**
+** In this routine, the "p" parameter is a pointer to the outer query.
+** The subquery is p->pSrc->a[iFrom].  isAgg is true if the outer query
+** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
+**
+** If flattening is not attempted, this routine is a no-op and return 0.
+** If flattening is attempted this routine returns 1.
+**
+** All of the expression analysis must occur on both the outer query and
+** the subquery before this routine runs.
 */
-int flattenSubqueries(Select *p){
+int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){
   Select *pSub;
-  if( p->pSrc->nId>1 ){
-    return 0;   /* Cannot optimize: The outer query is a join. */
+  IdList *pSrc, *pSubSrc;
+  ExprList *pList;
+  int i;
+  int iParent, iSub;
+  Expr *pWhere;
+
+  /* Check to see if flattening is permitted.  Return 0 if not.
+  */
+  if( p==0 ) return 0;
+  pSrc = p->pSrc;
+  assert( pSrc && iFrom>=0 && iFrom<pSrc->nId );
+  pSub = pSrc->a[iFrom].pSelect;
+  assert( pSub!=0 );
+  if( isAgg && subqueryIsAgg ) return 0;
+  if( subqueryIsAgg && pSrc->nId>1 ) return 0;
+  pSubSrc = pSub->pSrc;
+  assert( pSubSrc );
+  if( pSubSrc->nId>1 ) return 0;
+  if( pSub->isDistinct && pSrc->nId>1 ) return 0;
+  if( pSub->isDistinct && isAgg ) return 0;
+  if( p->isDistinct && subqueryIsAgg ) return 0;
+
+  /* If we reach this point, it means flatting is permitted for the
+  ** i-th entry of the FROM clause in the outer query.
+  */
+  iParent = p->base + iFrom;
+  iSub = pSub->base;
+  substExprList(p->pEList, iParent, pSub->pEList, iSub);
+  pList = p->pEList;
+  for(i=0; i<pList->nExpr; i++){
+    if( pList->a[i].zName==0 ){
+      Expr *pExpr = pList->a[i].pExpr;
+      pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n);
+    }
   }
-  pSub = p->pSrc->a[0].pSelect;
-  if( pSub==0 ){
-    return 0;   /* Nothing to optimize: There is no subquery. */
+  substExprList(p->pGroupBy, iParent, pSub->pEList, iSub);
+  substExpr(p->pHaving, iParent, pSub->pEList, iSub);
+  substExprList(p->pOrderBy, iParent, pSub->pEList, iSub);
+  if( pSub->pWhere ){
+    pWhere = sqliteExprDup(pSub->pWhere);
+    if( iParent!=iSub ){
+      changeTables(pWhere, iSub, iParent);
+    }
+  }else{
+    pWhere = 0;
   }
-  if( pSub->isDistinct ){
-    return 0;   /* Subquery contains DISTINCT keyword */
+  if( subqueryIsAgg ){
+    assert( p->pHaving==0 );
+    p->pHaving = pWhere;
+    substExpr(p->pHaving, iParent, pSub->pEList, iSub);
+  }else if( p->pWhere==0 ){
+    p->pWhere = pWhere;
+  }else{
+    substExpr(p->pWhere, iParent, pSub->pEList, iSub);
+    if( pWhere ){
+      p->pWhere = sqliteExpr(TK_AND, p->pWhere, pWhere, 0);
+    }
   }
-  if( pSub->pGroupBy ){
-    return 0;   /* Subquery contains a GROUP BY clause */
+  p->isDistinct = p->isDistinct || pSub->isDistinct;
+  if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
+    sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
   }
-  if( pSub->pPrior ){
-    return 0;   /* Subquery is the union of two or more queries */
-  } 
-
-  return 0;
-}      
+  pSrc->a[iFrom].pTab = pSubSrc->a[0].pTab;
+  pSubSrc->a[0].pTab = 0;
+  pSrc->a[iFrom].pSelect = pSubSrc->a[0].pSelect;
+  pSubSrc->a[0].pSelect = 0;
+  sqliteSelectDelete(pSub);
+  return 1;
+}
 
 /*
 ** Analyze the SELECT statement passed in as an argument to see if it
@@ -881,7 +1020,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
   v = sqliteGetVdbe(pParse);
   if( v==0 ) return 0;
   if( eDest==SRT_Callback ){
-    generateColumnNames(pParse, p->pSrc, p->pEList);
+    generateColumnNames(pParse, p->base, p->pSrc, p->pEList);
   }
 
   /* Generating code to find the min or the max.  Basically all we have
@@ -894,7 +1033,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
     pParse->schemaVerified = 1;
   }
   openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
-  base = pParse->nTab;
+  base = p->base;
   sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
   sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
   if( pIdx==0 ){
@@ -949,7 +1088,10 @@ int sqliteSelect(
   Parse *pParse,         /* The parser context */
   Select *p,             /* The SELECT statement being coded. */
   int eDest,             /* One of: SRT_Callback Mem Set Union Except */
-  int iParm              /* Save result in this memory location, if >=0 */
+  int iParm,             /* Save result in this memory location, if >=0 */
+  Select *pParent,       /* Another SELECT for which this is a sub-query */
+  int parentTab,         /* Index in pParent->pSrc of this query */
+  int parentAgg          /* True if pParent uses aggregate functions */
 ){
   int i;
   WhereInfo *pWInfo;
@@ -983,10 +1125,12 @@ int sqliteSelect(
   pHaving = p->pHaving;
   isDistinct = p->isDistinct;
 
-  /* Save the current value of pParse->nTab.  Restore this value before
-  ** we exit.
+  /* Allocate a block of VDBE cursors, one for each table in the FROM clause.
+  ** The WHERE processing requires that the cursors for the tables in the
+  ** FROM clause be consecutive.
   */
-  base = pParse->nTab;
+  base = p->base = pParse->nTab;
+  pParse->nTab += pTabList->nId;
 
   /* 
   ** Do not even attempt to generate any code if we have already seen
@@ -1004,16 +1148,6 @@ int sqliteSelect(
   pEList = p->pEList;
   if( pEList==0 ) goto select_end;
 
-  /* Allocate a temporary table to use for the DISTINCT set, if
-  ** necessary.  This must be done early to allocate the cursor before
-  ** any calls to sqliteExprResolveIds().
-  */
-  if( isDistinct ){
-    distinct = pParse->nTab++;
-  }else{
-    distinct = -1;
-  }
-
   /* If writing to memory or generating a set
   ** only a single column may be output.
   */
@@ -1027,35 +1161,17 @@ int sqliteSelect(
   /* ORDER BY is ignored if we are not sending the result to a callback.
   */
   if( eDest!=SRT_Callback ){
-    pOrderBy = 0;
+    sqliteExprListDelete(p->pOrderBy);
+    pOrderBy = p->pOrderBy = 0;
   }
 
-  /* Allocate cursors for "expr IN (SELECT ...)" constructs.
-  */
-  for(i=0; i<pEList->nExpr; i++){
-    sqliteExprResolveInSelect(pParse, pEList->a[i].pExpr);
-  }
-  if( pWhere ) sqliteExprResolveInSelect(pParse, pWhere);
-  if( pOrderBy ){
-    for(i=0; i<pOrderBy->nExpr; i++){
-      sqliteExprResolveInSelect(pParse, pOrderBy->a[i].pExpr);
-    }
-  }
-  if( pGroupBy ){
-    for(i=0; i<pGroupBy->nExpr; i++){
-      sqliteExprResolveInSelect(pParse, pGroupBy->a[i].pExpr);
-    }
-  }
-  if( pHaving ) sqliteExprResolveInSelect(pParse, pHaving);
-
   /* At this point, we should have allocated all the cursors that we
-  ** need to handle subquerys and temporary tables.  From here on we
-  ** are committed to keeping the same value for pParse->nTab.
+  ** need to handle subquerys and temporary tables.  
   **
   ** Resolve the column names and do a semantics check on all the expressions.
   */
   for(i=0; i<pEList->nExpr; i++){
-    if( sqliteExprResolveIds(pParse, pTabList, 0, pEList->a[i].pExpr) ){
+    if( sqliteExprResolveIds(pParse, base, pTabList, 0, pEList->a[i].pExpr) ){
       goto select_end;
     }
     if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &isAgg) ){
@@ -1063,7 +1179,7 @@ int sqliteSelect(
     }
   }
   if( pWhere ){
-    if( sqliteExprResolveIds(pParse, pTabList, pEList, pWhere) ){
+    if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pWhere) ){
       goto select_end;
     }
     if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
@@ -1079,7 +1195,7 @@ int sqliteSelect(
         pParse->nErr++;
         goto select_end;
       }
-      if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){
+      if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){
         goto select_end;
       }
       if( sqliteExprCheck(pParse, pE, isAgg, 0) ){
@@ -1096,7 +1212,7 @@ int sqliteSelect(
         pParse->nErr++;
         goto select_end;
       }
-      if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){
+      if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){
         goto select_end;
       }
       if( sqliteExprCheck(pParse, pE, isAgg, 0) ){
@@ -1111,7 +1227,7 @@ int sqliteSelect(
       pParse->nErr++;
       goto select_end;
     }
-    if( sqliteExprResolveIds(pParse, pTabList, pEList, pHaving) ){
+    if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pHaving) ){
       goto select_end;
     }
     if( sqliteExprCheck(pParse, pHaving, isAgg, 0) ){
@@ -1119,14 +1235,6 @@ int sqliteSelect(
     }
   }
 
-  /* Try to merge subqueries in the FROM clause into the main
-  ** query.
-  */
-  if( flattenSubqueries(p) ){
-    pEList = p->pEList;
-    pWhere = p->pWhere;
-  }
-
   /* Check for the special case of a min() or max() function by itself
   ** in the result set.
   */
@@ -1143,14 +1251,24 @@ int sqliteSelect(
   /* Generate code for all sub-queries in the FROM clause
   */
   for(i=0; i<pTabList->nId; i++){
-    int oldNTab;
     if( pTabList->a[i].pSelect==0 ) continue;
-    oldNTab = pParse->nTab;
-    pParse->nTab += i+1;
-    sqliteVdbeAddOp(v, OP_OpenTemp, oldNTab+i, 0);
-    sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_Table, oldNTab+i);
-    pParse->nTab = oldNTab;
+    sqliteVdbeAddOp(v, OP_OpenTemp, base+i, 0);
+    sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_Table, base+i,
+                 p, i, isAgg);
+  }
+
+  /* Check to see if this is a subquery that can be "flattened" into its parent.
+  ** If flattening is a possiblity, do so and return immediately.  
+  */
+  if( flattenSubquery(pParent, parentTab, parentAgg, isAgg) ){
+    return rc;
   }
+  pTabList = p->pSrc;
+  pWhere = p->pWhere;
+  pOrderBy = p->pOrderBy;
+  pGroupBy = p->pGroupBy;
+  pHaving = p->pHaving;
+  isDistinct = p->isDistinct;
 
   /* Do an analysis of aggregate expressions.
   */
@@ -1195,7 +1313,7 @@ int sqliteSelect(
   ** step is skipped if the output is going to a table or a memory cell.
   */
   if( eDest==SRT_Callback ){
-    generateColumnNames(pParse, pTabList, pEList);
+    generateColumnNames(pParse, p->base, pTabList, pEList);
   }
 
   /* Reset the aggregator
@@ -1222,12 +1340,18 @@ int sqliteSelect(
     sqliteVdbeAddOp(v, OP_MemStore, iParm, 1);
   }
 
-  /* Begin the database scan
+  /* Open a temporary table to use for the distinct set.
   */
   if( isDistinct ){
+    distinct = pParse->nTab++;
     sqliteVdbeAddOp(v, OP_OpenTemp, distinct, 1);
+  }else{
+    distinct = -1;
   }
-  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
+
+  /* Begin the database scan
+  */
+  pWInfo = sqliteWhereBegin(pParse, p->base, pTabList, pWhere, 0);
   if( pWInfo==0 ) goto select_end;
 
   /* Use the standard inner loop if we are not dealing with
@@ -1309,7 +1433,6 @@ int sqliteSelect(
   if( pOrderBy ){
     generateSortTail(v, pEList->nExpr);
   }
-  pParse->nTab = base;
 
 
   /* Issue a null callback if that is what the user wants.
@@ -1327,6 +1450,7 @@ int sqliteSelect(
   ** successful coding of the SELECT.
   */
 select_end:
+  pParse->nTab = base;
   sqliteAggregateInfoReset(pParse);
   return rc;
 }
index a39272077a37d082dc91efb600a99fefad2733e0..3dcda0a66a8f3191d4cfa4d163d3dd69ed1d0b3e 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.96 2002/02/28 00:41:11 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.97 2002/03/02 17:04:08 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -434,6 +434,8 @@ struct WhereInfo {
   int iBreak;          /* Jump here to break out of the loop */
   int base;            /* Index of first Open opcode */
   int nLevel;          /* Number of nested loop */
+  int savedNTab;       /* Value of pParse->nTab before WhereBegin() */
+  int peakNTab;        /* Value of pParse->nTab after WhereBegin() */
   WhereLevel a[1];     /* Information about each nest loop in the WHERE */
 };
 
@@ -461,6 +463,7 @@ struct Select {
   Select *pPrior;        /* Prior select in a compound select statement */
   int nLimit, nOffset;   /* LIMIT and OFFSET values.  -1 means not used */
   char *zSelect;         /* Complete text of the SELECT command */
+  int base;              /* Index of VDBE cursor for left-most FROM table */
 };
 
 /*
@@ -521,7 +524,7 @@ struct Parse {
   int nameClash;       /* A permanent table name clashes with temp table name */
   int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
   int nErr;            /* Number of errors seen */
-  int nTab;            /* Number of previously allocated cursors */
+  int nTab;            /* Number of previously allocated VDBE cursors */
   int nMem;            /* Number of memory cells used so far */
   int nSet;            /* Number of sets used so far */
   int nAgg;            /* Number of aggregate expressions */
@@ -586,7 +589,7 @@ void sqliteIdListAddAlias(IdList*, Token*);
 void sqliteIdListDelete(IdList*);
 void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, int, Token*, Token*);
 void sqliteDropIndex(Parse*, Token*);
-int sqliteSelect(Parse*, Select*, int, int);
+int sqliteSelect(Parse*, Select*, int, int, Select*, int, int);
 Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,
                         int,int,int);
 void sqliteSelectDelete(Select*);
@@ -595,7 +598,7 @@ Table *sqliteTableNameToTable(Parse*, const char*);
 IdList *sqliteTableTokenToIdList(Parse*, Token*);
 void sqliteDeleteFrom(Parse*, Token*, Expr*);
 void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int);
-WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
+WhereInfo *sqliteWhereBegin(Parse*, int, IdList*, Expr*, int);
 void sqliteWhereEnd(WhereInfo*);
 void sqliteExprCode(Parse*, Expr*);
 void sqliteExprIfTrue(Parse*, Expr*, int);
@@ -611,8 +614,7 @@ char *sqliteTableNameFromToken(Token*);
 int sqliteExprCheck(Parse*, Expr*, int, int*);
 int sqliteExprCompare(Expr*, Expr*);
 int sqliteFuncId(Token*);
-int sqliteExprResolveIds(Parse*, IdList*, ExprList*, Expr*);
-void sqliteExprResolveInSelect(Parse*, Expr*);
+int sqliteExprResolveIds(Parse*, int, IdList*, ExprList*, Expr*);
 int sqliteExprAnalyzeAggregates(Parse*, Expr*);
 Vdbe *sqliteGetVdbe(Parse*);
 int sqliteRandomByte(void);
index a4aa308e40ad77cf06215144437f11b3ec8cc3bb..7e470fed7f4a12f48009c23d01f2a556672a6539 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.34 2002/02/23 02:32:10 drh Exp $
+** $Id: update.c,v 1.35 2002/03/02 17:04:09 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -66,14 +66,9 @@ void sqliteUpdate(
   ** WHERE clause and in the new values.  Also find the column index
   ** for each column to be updated in the pChanges array.
   */
+  base = pParse->nTab++;
   if( pWhere ){
-    sqliteExprResolveInSelect(pParse, pWhere);
-  }
-  for(i=0; i<pChanges->nExpr; i++){
-    sqliteExprResolveInSelect(pParse, pChanges->a[i].pExpr);
-  }
-  if( pWhere ){
-    if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
+    if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){
       goto update_cleanup;
     }
     if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
@@ -82,7 +77,7 @@ void sqliteUpdate(
   }
   chngRecno = 0;
   for(i=0; i<pChanges->nExpr; i++){
-    if( sqliteExprResolveIds(pParse, pTabList, 0, pChanges->a[i].pExpr) ){
+    if( sqliteExprResolveIds(pParse, base, pTabList, 0, pChanges->a[i].pExpr) ){
       goto update_cleanup;
     }
     if( sqliteExprCheck(pParse, pChanges->a[i].pExpr, 0, 0) ){
@@ -150,7 +145,7 @@ void sqliteUpdate(
 
   /* Begin the database scan
   */
-  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
+  pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1);
   if( pWInfo==0 ) goto update_cleanup;
 
   /* Remember the index of every item to be updated.
@@ -174,7 +169,6 @@ void sqliteUpdate(
   ** to be deleting some records.
   */
   sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
-  base = pParse->nTab;
   openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
   sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
   if( onError==OE_Replace ){
@@ -191,7 +185,9 @@ void sqliteUpdate(
   for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
     if( openAll || aIdxUsed[i] ){
       sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum);
+      assert( pParse->nTab==base+i+1 );
     }
+    pParse->nTab++;
   }
 
   /* Loop over every record that needs updating.  We have to load
index e325af807ac194bfda646d3100df897b6abe4ccb..32077dc1570445762803351ba656f55ebb1dd83e 100644 (file)
@@ -13,7 +13,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.37 2002/02/23 02:32:10 drh Exp $
+** $Id: where.c,v 1.38 2002/03/02 17:04:09 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -76,8 +76,7 @@ static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
 ** 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.
+** corresponds to the first entry in the table list. 
 */
 static int exprTableUsage(int base, Expr *p){
   unsigned int mask = 0;
@@ -119,8 +118,7 @@ static int allowedOp(int op){
 ** structure.
 **
 ** "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.
+** corresponds to the first entry in the table list.
 */
 static void exprAnalyze(int base, ExprInfo *pInfo){
   Expr *pExpr = pInfo->p;
@@ -152,6 +150,7 @@ static void exprAnalyze(int base, ExprInfo *pInfo){
 */
 WhereInfo *sqliteWhereBegin(
   Parse *pParse,       /* The parser context */
+  int base,            /* VDBE cursor index for left-most table in pTabList */
   IdList *pTabList,    /* A list of all tables */
   Expr *pWhere,        /* The WHERE clause */
   int pushKey          /* If TRUE, leave the table key on the stack */
@@ -164,8 +163,6 @@ 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 */
-  int nCur;            /* Next unused cursor number */
   int aDirect[32];     /* If TRUE, then index this table using ROWID */
   int iDirectEq[32];   /* Term of the form ROWID==X for the N-th table */
   int iDirectLt[32];   /* Term of the form ROWID<X or ROWID<=X */
@@ -186,9 +183,8 @@ WhereInfo *sqliteWhereBegin(
   }
   pWInfo->pParse = pParse;
   pWInfo->pTabList = pTabList;
-  base = pWInfo->base = pParse->nTab;
-  nCur = base + pTabList->nId;
-  pParse->nTab += nCur*2;
+  pWInfo->base = base;
+  pWInfo->peakNTab = pWInfo->savedNTab = pParse->nTab;
 
   /* Split the WHERE clause into as many as 32 separate subexpressions
   ** where each subexpression is separated by an AND operator.  Any additional
@@ -388,7 +384,8 @@ WhereInfo *sqliteWhereBegin(
     pWInfo->a[i].score = bestScore;
     loopMask |= 1<<idx;
     if( pBestIdx ){
-      pWInfo->a[i].iCur = nCur++;
+      pWInfo->a[i].iCur = pParse->nTab++;
+      pWInfo->peakNTab = pParse->nTab;
     }
   }
 
@@ -794,7 +791,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
       sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0);
     }
   }
-  pWInfo->pParse->nTab = base;
+  if( pWInfo->pParse->nTab==pWInfo->peakNTab ){
+    pWInfo->pParse->nTab = pWInfo->savedNTab;
+  }
   sqliteFree(pWInfo);
   return;
 }
index af3311c640604b256a7d7fbb9e0345d042a5003d..f37f185a5bdd9047919df7a5f6e7ae492d817d0a 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing built-in functions.
 #
-# $Id: func.test,v 1.8 2002/02/28 03:04:48 drh Exp $
+# $Id: func.test,v 1.9 2002/03/02 17:04:09 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -25,6 +25,17 @@ do_test func-0.0 {
   }
   execsql {SELECT t1 FROM tbl1 ORDER BY t1}
 } {free is program software this}
+do_test func-0.1 {
+  execsql {
+     CREATE TABLE t2(a);
+     INSERT INTO t2 VALUES(1);
+     INSERT INTO t2 VALUES(NULL);
+     INSERT INTO t2 VALUES(345);
+     INSERT INTO t2 VALUES(NULL);
+     INSERT INTO t2 VALUES(67890);
+     SELECT * FROM t2;
+  }
+} {1 {} 345 {} 67890}
 
 # Check out the length() function
 #
@@ -43,6 +54,9 @@ do_test func-1.3 {
   execsql {SELECT length(t1), count(*) FROM tbl1 GROUP BY length(t1)
            ORDER BY length(t1)}
 } {2 1 4 2 7 1 8 1}
+do_test func-1.4 {
+  execsql {SELECT length(a) FROM t2}
+} {1 0 3 0 5}
 
 # Check out the substr() function
 #
@@ -73,6 +87,12 @@ do_test func-2.7 {
 do_test func-2.8 {
   execsql {SELECT t1 FROM tbl1 ORDER BY substr(t1,2,20)}
 } {this software free program is}
+do_test func-2.9 {
+  execsql {SELECT substr(a,1,1) FROM t2}
+} {1 {} 3 {} 6}
+do_test func-2.10 {
+  execsql {SELECT substr(a,2,2) FROM t2}
+} {{} {} 45 {} 78}
 
 # Only do the following tests if TCL has UTF-8 capabilities and
 # the UTF-8 encoding is turned on in the SQLite library.
@@ -118,6 +138,13 @@ do_test func-3.9 {
 do_test func-3.10 {
   execsql {SELECT substr(t1,-4,3) FROM tbl1 ORDER BY t1}
 } "ter ain i\u1234h TF-"
+do_test func-3.99 {
+  execsql {DELETE FROM tbl1}
+  foreach word {this program is free software} {
+    execsql "INSERT INTO tbl1 VALUES('$word')"
+  }
+  execsql {SELECT t1 FROM tbl1}
+} {this program is free software}
 
 } ;# End [sqlite -encoding]==UTF-8 and \u1234!=u1234
 
@@ -141,6 +168,12 @@ do_test func-4.3 {
 do_test func-4.4 {
   catchsql {SELECT abs(c) FROM t1 ORDER BY a}
 } {0 {3 12345.67890 5}}
+do_test func-4.4.1 {
+  execsql {SELECT abs(a) FROM t2}
+} {1 {} 345 {} 67890}
+do_test func-4.4.2 {
+  execsql {SELECT abs(t1) FROM tbl1}
+} {this program is free software}
 
 do_test func-4.5 {
   catchsql {SELECT round(a,b,c) FROM t1}
@@ -163,5 +196,39 @@ do_test func-4.10 {
 do_test func-4.11 {
   catchsql {SELECT round() FROM t1 ORDER BY a}
 } {1 {wrong number of arguments to function round()}}
+do_test func-4.12 {
+  execsql {SELECT round(a,2) FROM t2}
+} {1.00 0.00 345.00 0.00 67890.00}
+do_test func-4.13 {
+  execsql {SELECT round(t1,2) FROM tbl1}
+} {0.00 0.00 0.00 0.00 0.00}
+
+# Test the upper() and lower() functions
+#
+do_test func-5.1 {
+  execsql {SELECT upper(t1) FROM tbl1}
+} {THIS PROGRAM IS FREE SOFTWARE}
+do_test func-5.2 {
+  execsql {SELECT lower(upper(t1)) FROM tbl1}
+} {this program is free software}
+do_test func-5.3 {
+  execsql {SELECT upper(a), lower(a) FROM t2}
+} {1 1 {} {} 345 345 {} {} 67890 67890}
+do_test func-5.4 {
+  catchsql {SELECT upper(a,5) FROM t2}
+} {1 {wrong number of arguments to function upper()}}
+do_test func-5.5 {
+  catchsql {SELECT upper(*) FROM t2}
+} {1 {wrong number of arguments to function upper()}}
+
+# Test the coalesce() function
+#
+do_test func-6.1 {
+  execsql {SELECT coalesce(a,'xyz') FROM t2}
+} {1 xyz 345 xyz 67890}
+do_test func-6.2 {
+  execsql {SELECT coalesce(upper(a),'nil') FROM t2}
+} {1 nil 345 nil 67890}
+
 
 finish_test
index 7fd6a360f8c13530c506b9fcd9f32d8d703b84a5..4aa36e8a6b2cbea018483ac45e071d8e4fbfb776 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select1.test,v 1.21 2002/02/28 03:14:18 drh Exp $
+# $Id: select1.test,v 1.22 2002/03/02 17:04:09 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -97,10 +97,22 @@ do_test select1-1.13 {
            FROM test1, test2}
 } {1.1 22}
 
-execsql {DROP TABLE test2}
-execsql {DELETE FROM test1}
-execsql {INSERT INTO test1 VALUES(11,22)}
-execsql {INSERT INTO test1 VALUES(33,44)}
+set long {This is a string that is too big to fit inside a NBFS buffer}
+do_test select1-2.0 {
+  execsql "
+    DROP TABLE test2;
+    DELETE FROM test1;
+    INSERT INTO test1 VALUES(11,22);
+    INSERT INTO test1 VALUES(33,44);
+    CREATE TABLE t3(a,b);
+    INSERT INTO t3 VALUES('abc',NULL);
+    INSERT INTO t3 VALUES(NULL,'xyz');
+    INSERT INTO t3 SELECT * FROM test1;
+    CREATE TABLE t4(a,b);
+    INSERT INTO t4 VALUES(NULL,'$long');
+    SELECT * FROM t3;
+  "
+} {abc {} {} xyz 11 22 33 44}
 
 # Error messges from sqliteExprCheck
 #
@@ -124,6 +136,15 @@ do_test select1-2.5 {
   set v [catch {execsql {SELECT COUNT(*)+1 FROM test1}} msg]
   lappend v $msg
 } {0 3}
+do_test select1-2.5.1 {
+  execsql {SELECT count(*),count(a),count(b) FROM t3}
+} {4 3 3}
+do_test select1-2.5.2 {
+  execsql {SELECT count(*),count(a),count(b) FROM t4}
+} {1 0 1}
+do_test select1-2.5.3 {
+  execsql {SELECT count(*),count(a),count(b) FROM t4 WHERE b=5}
+} {0 0 0}
 do_test select1-2.6 {
   set v [catch {execsql {SELECT min(*) FROM test1}} msg]
   lappend v $msg
@@ -136,6 +157,15 @@ do_test select1-2.8 {
   set v [catch {execsql {SELECT MIN(f1,f2) FROM test1}} msg]
   lappend v [lsort $msg]
 } {0 {11 33}}
+do_test select1-2.8.1 {
+  execsql {SELECT coalesce(min(a),'xyzzy') FROM t3}
+} {xyzzy}
+do_test select1-2.8.2 {
+  execsql {SELECT min(coalesce(a,'xyzzy')) FROM t3}
+} {11}
+do_test select1-2.8.3 {
+  execsql {SELECT min(b), min(b) FROM t4}
+} [list $long $long]
 do_test select1-2.9 {
   set v [catch {execsql {SELECT MAX(*) FROM test1}} msg]
   lappend v $msg
@@ -156,6 +186,12 @@ do_test select1-2.13 {
   set v [catch {execsql {SELECT MAX(f1)+1 FROM test1}} msg]
   lappend v $msg
 } {0 34}
+do_test select1-2.13.1 {
+  execsql {SELECT coalesce(max(a),'xyzzy') FROM t3}
+} {abc}
+do_test select1-2.13.1 {
+  execsql {SELECT max(coalesce(a,'xyzzy')) FROM t3}
+} {xyzzy}
 do_test select1-2.14 {
   set v [catch {execsql {SELECT SUM(*) FROM test1}} msg]
   lappend v $msg
@@ -172,6 +208,9 @@ do_test select1-2.17 {
   set v [catch {execsql {SELECT SUM(f1)+1 FROM test1}} msg]
   lappend v $msg
 } {0 45}
+do_test select1-2.17.1 {
+  execsql {SELECT sum(a) FROM t3}
+} {44}
 do_test select1-2.18 {
   set v [catch {execsql {SELECT XYZZY(f1) FROM test1}} msg]
   lappend v $msg
index 1e22c76dd8e852c622a1a140ba0a571fba9cbd6c..2cbdfcdcd7c933ad1ed2563cbdc4a1b2e79c6ab8 100644 (file)
@@ -28,6 +28,8 @@ chng {2002 Feb * (2.3.4)} {
 <li>Modifications to the "lemon" parser generator so that the parser tables
     are 4 times smaller.</li>
 <li>Added support for user-defined functions implemented in C.</li>
+<li>Added support for VIEWs.</li>
+<li>Added the subquery flattening optimizer.</li>
 }
 
 chng {2002 Feb 18 (2.3.3)} {