]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Rewrite the aggregate handling logic so that it runs in O(1) space.
authordrh <drh@noemail.net>
Wed, 7 Sep 2005 21:22:45 +0000 (21:22 +0000)
committerdrh <drh@noemail.net>
Wed, 7 Sep 2005 21:22:45 +0000 (21:22 +0000)
This is the first cut at the code.  Many regression tests fail. (CVS 2662)

FossilOrigin-Name: 17039ec3ff4396862beedf4a8af89654b2140f58

14 files changed:
manifest
manifest.uuid
src/build.c
src/expr.c
src/main.c
src/parse.y
src/select.c
src/sqliteInt.h
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeaux.c
src/vdbemem.c
test/misc4.test

index a5ff70bff920931fe87ba638a5e3545021d5f4e8..cf457f8a608a6cdad11d6b8588194265090dbe18 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Changes\sto\sreduce\sthe\samount\sof\sstack\sspace\srequired.\s(CVS\s2661)
-D 2005-09-06T21:40:45
+C Rewrite\sthe\saggregate\shandling\slogic\sso\sthat\sit\sruns\sin\sO(1)\sspace.\nThis\sis\sthe\sfirst\scut\sat\sthe\scode.\s\sMany\sregression\stests\sfail.\s(CVS\s2662)
+D 2005-09-07T21:22:46
 F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1
 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -34,19 +34,19 @@ F src/attach.c 4b21689700a72ae281fa85dbaff06b2a62bd49ee
 F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454
 F src/btree.c 5b3bc015c49a41c025cfdf8ad36051f3007e2cb0
 F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e
-F src/build.c d61682e8d0368fbc6ff230cd4b9bd41659d5634b
+F src/build.c d9f3c0e65ada1087da21b524f6ef685e4d1a1725
 F src/callback.c 9a1162c8f9dae9fad6d548339669aacb5f6cf76b
 F src/complete.c 4de937dfdd4c79a501772ab2035b26082f337a79
 F src/date.c 7444b0900a28da77e57e3337a636873cff0ae940
 F src/delete.c be1fc25c9e109cd8cbab42a43ee696263da7c04b
 F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
-F src/expr.c 8a72157fa6842e84819a8c80521be02ec471180c
+F src/expr.c e0a3f275586bb076cc5995885a328b531b5aecf2
 F src/func.c 713cf33a0ab8685d44ed31a9c753983a7ff9fd6e
 F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
 F src/insert.c 484c73bc1309f283a31baa0e114f3ee980536397
 F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b
-F src/main.c 8bcd1d2ed92dcb24bafb770eae6e4afce8ddcbde
+F src/main.c bf88855445d365b497070d85e3faa0579a9edb91
 F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070
 F src/os.h c4b34bd4d6fea51a420f337468b907f4edecb161
 F src/os_common.h 0e7f428ba0a6c40a61bc56c4e96f493231301b73
@@ -58,15 +58,15 @@ F src/os_win.c 4aad6cd49a2a546f945491a9e6a0b7d061cf47c5
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
 F src/pager.c cd9896287a8fd33cc267bd0c2b69c421a4808169
 F src/pager.h 17b13225abd93c1e9f470060f40a21b9edb5a164
-F src/parse.y d57cdd2adc0923762b40314f08683c836a2e0c90
+F src/parse.y 4c0cf6b0646166b232693249b89e32a75c6f87d7
 F src/pragma.c 69413fbdc0c6aaa493a776ea52c1b3e6cf35dfb2
 F src/prepare.c 86f0d8e744b8d956eff6bc40e29049efee017610
 F src/printf.c c01e9ad473d79463fb1f483b1eca5c3cbed2a4e5
 F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4
-F src/select.c 79bd7f97345ee4291e7f4d469307b185eb2cba8f
+F src/select.c 2f965220652f5417a70720ea3bf7703bf432fb6b
 F src/shell.c b21daba017b8feef2fdc65ecde57f70209494217
 F src/sqlite.h.in d6561d51025d08de4f455607f3f9f9aa76e855d5
-F src/sqliteInt.h 845ff6f8019f80baafb1bdbb8ef80fcd04d9d0f9
+F src/sqliteInt.h 97d7d13bfcccd67974b0db9c19fce4428ae9d236
 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
 F src/tclsqlite.c ac94682f9e601dd373912c46414a5a842db2089a
 F src/test1.c b569b60e35f0e3ea20e5ebfaf6e522a01c08d481
@@ -80,13 +80,13 @@ F src/update.c a9d2c5f504212d62da1b094476f1389c0e02f83f
 F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
 F src/util.c 5650f6fe5ee30e0678985ad7b94da91e3f85752b
 F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c
-F src/vdbe.c 5f0ed87252912fa1d4c989b0c6d7e3d4d2bb337a
-F src/vdbe.h 3b29a9af6c7a64ed692bef1fc5f61338f40d2f67
-F src/vdbeInt.h 7a6b3c1adfa7b23c1f4f15ce0549b5b52a85a635
+F src/vdbe.c e774761b4566540e0007ec3d6ff24ed7a89f51c9
+F src/vdbe.h c8e105979fc7aaf5b8004e9621904e3bd096dfa2
+F src/vdbeInt.h 15a32128a8173c724a90829b90af495b370c09cc
 F src/vdbeapi.c 46e2fd47e2ce3c1aea9bb48bbbac31a1dc75a6ff
-F src/vdbeaux.c 2cfc66b30be5e293bd72db8084f8cb5c865e8b01
+F src/vdbeaux.c 11db0de973c850bb5d92c67af1c8f3c189f08741
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
-F src/vdbemem.c b6ae3ac842a6759bd8ec4eb1cd428520b5eafc57
+F src/vdbemem.c 3cb63f021ac5ed102d80ec888d6f76fc0dcac153
 F src/where.c 92ab208abe6bec15e81616b8c1a619be23ece506
 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
 F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3
@@ -178,7 +178,7 @@ F test/minmax.test 9429a06f1f93acf76fcacafd17160a4392e88526
 F test/misc1.test 4ca69ca2e2ef33c7a0b0fc8b324111e37a522d29
 F test/misc2.test 5c699af2fede2694736a9f45aea7e2f052686e15
 F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03
-F test/misc4.test edd3e3adf5b6e3b995b29843565ca58dd602f9a7
+F test/misc4.test 8a28f046bac8121dc2fe623ccb75578cd51b9db0
 F test/misc5.test 24bd03404039ec727028ac9cf7fd9066fd209ec9
 F test/misuse.test 1c7fee3c4c0cb4008717ecccf5c72281fac0008e
 F test/notnull.test 7a08117a71e74b0321aaa937dbeb41a09d6eb1d0
@@ -306,7 +306,7 @@ F www/tclsqlite.tcl 3df553505b6efcad08f91e9b975deb2e6c9bb955
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 7ecf3654aa9a275a4cf0c3ec5f63a8c1e0a11fc9
-R a30f5538e6a4b7f1c84ee615fff6a374
+P b86bd70f301205d6ca66475a425e157b976107e2
+R 47d4cda0a17a577f38d2fea59c44e4f5
 U drh
-Z 6a9878dbd0fbab628ba267b019a75820
+Z e6ce677b69437ad913acfe1b1ebf26c2
index ad534eb1d176335453a5acf90c6d69513416ec20..f90b49b3985b40c8480463925abf24a3103cd424 100644 (file)
@@ -1 +1 @@
-b86bd70f301205d6ca66475a425e157b976107e2
\ No newline at end of file
+17039ec3ff4396862beedf4a8af89654b2140f58
\ No newline at end of file
index fc4a0311f529a2e2527928d480b0536762ac2bfe..b2be37aab505982eb2fe0aaee26c2019ffa98f24 100644 (file)
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.344 2005/08/31 13:13:31 drh Exp $
+** $Id: build.c,v 1.345 2005/09/07 21:22:46 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -103,7 +103,7 @@ void sqlite3FinishCoding(Parse *pParse){
     FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
     sqlite3VdbeTrace(v, trace);
     sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3,
-                         pParse->nTab+3, pParse->nMaxDepth+1, pParse->explain);
+                         pParse->nTab+3, pParse->explain);
     pParse->rc = SQLITE_DONE;
     pParse->colNamesSet = 0;
   }else if( pParse->rc==SQLITE_OK ){
@@ -2469,6 +2469,45 @@ exit_drop_index:
   sqlite3SrcListDelete(pName);
 }
 
+/*
+** ppArray points into a structure where there is an array pointer
+** followed by two integers. The first integer is the
+** number of elements in the structure array.  The second integer
+** is the number of allocated slots in the array.
+**
+** In other words, the structure looks something like this:
+**
+**        struct Example1 {
+**          struct subElem *aEntry;
+**          int nEntry;
+**          int nAlloc;
+**        }
+**
+** The pnEntry parameter points to the equivalent of Example1.nEntry.
+**
+** This routine allocates a new slot in the array, zeros it out,
+** and returns its index.  If malloc fails a negative number is returned.
+**
+** szEntry is the sizeof of a single array entry.  initSize is the 
+** number of array entries allocated on the initial allocation.
+*/
+int sqlite3ArrayAllocate(void **ppArray, int szEntry, int initSize){
+  char *p;
+  int *an = (int*)&ppArray[1];
+  if( an[0]>=an[1] ){
+    void *pNew;
+    an[1] = an[1]*2 + initSize;
+    pNew = sqliteRealloc(*ppArray, an[1]*szEntry);
+    if( pNew==0 ){
+      return -1;
+    }
+    *ppArray = pNew;
+  }
+  p = *ppArray;
+  memset(&p[an[0]*szEntry], 0, szEntry);
+  return an[0]++;
+}
+
 /*
 ** Append a new element to the given IdList.  Create a new IdList if
 ** need be.
@@ -2476,24 +2515,18 @@ exit_drop_index:
 ** A new IdList is returned, or NULL if malloc() fails.
 */
 IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){
+  int i;
   if( pList==0 ){
     pList = sqliteMalloc( sizeof(IdList) );
     if( pList==0 ) return 0;
     pList->nAlloc = 0;
   }
-  if( pList->nId>=pList->nAlloc ){
-    struct IdList_item *a;
-    pList->nAlloc = pList->nAlloc*2 + 5;
-    a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]) );
-    if( a==0 ){
-      sqlite3IdListDelete(pList);
-      return 0;
-    }
-    pList->a = a;
+  i = sqlite3ArrayAllocate((void**)&pList->a, sizeof(pList->a[0]), 5);
+  if( i<0 ){
+    sqlite3IdListDelete(pList);
+    return 0;
   }
-  memset(&pList->a[pList->nId], 0, sizeof(pList->a[0]));
-  pList->a[pList->nId].zName = sqlite3NameFromToken(pToken);
-  pList->nId++;
+  pList->a[i].zName = sqlite3NameFromToken(pToken);
   return pList;
 }
 
index 7ac7a7ae46c1a7aa3057a38774e708407a739514..ac37e507fc8b67ab479269f525c9c2d41d680870 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.224 2005/09/05 20:06:49 drh Exp $
+** $Id: expr.c,v 1.225 2005/09/07 21:22:46 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -695,6 +695,7 @@ static int exprNodeIsConstant(void *pArg, Expr *pExpr){
     case TK_COLUMN:
     case TK_DOT:
     case TK_AGG_FUNCTION:
+    case TK_AGG_COLUMN:
 #ifndef SQLITE_OMIT_SUBQUERY
     case TK_SELECT:
     case TK_EXISTS:
@@ -1230,11 +1231,19 @@ int sqlite3ExprResolveNames(
   NameContext *pNC,       /* Namespace to resolve expressions in. */
   Expr *pExpr             /* The expression to be analyzed. */
 ){
+  int savedHasAgg;
   if( pExpr==0 ) return 0;
+  savedHasAgg = pNC->hasAgg;
+  pNC->hasAgg = 0;
   walkExprTree(pExpr, nameResolverStep, pNC);
   if( pNC->nErr>0 ){
     ExprSetProperty(pExpr, EP_Error);
   }
+  if( pNC->hasAgg ){
+    ExprSetProperty(pExpr, EP_Agg);
+  }else if( savedHasAgg ){
+    pNC->hasAgg = 1;
+  }
   return ExprHasProperty(pExpr, EP_Error);
 }
 
@@ -1287,10 +1296,6 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
     sqlite3VdbeAddOp(v, OP_MemStore, mem, 1);
   }
 
-  if( pExpr->pSelect ){
-    sqlite3VdbeAddOp(v, OP_AggContextPush, 0, 0);
-  }
-
   switch( pExpr->op ){
     case TK_IN: {
       char affinity;
@@ -1403,9 +1408,6 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
     }
   }
 
-  if( pExpr->pSelect ){
-    sqlite3VdbeAddOp(v, OP_AggContextPop, 0, 0);
-  }
   if( testAddr ){
     sqlite3VdbeChangeP2(v, testAddr, sqlite3VdbeCurrentAddr(v));
   }
@@ -1448,10 +1450,21 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
   }
   op = pExpr->op;
   switch( op ){
+    case TK_AGG_COLUMN: {
+      AggInfo *pAggInfo = pExpr->pAggInfo;
+      struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
+      if( !pAggInfo->directMode ){
+        sqlite3VdbeAddOp(v, OP_MemLoad, pCol->iMem, 0);
+        break;
+      }else if( pAggInfo->useSortingIdx ){
+        sqlite3VdbeAddOp(v, OP_Column, pAggInfo->sortingIdx,
+                              pCol->iSorterColumn);
+        break;
+      }
+      /* Otherwise, fall thru into the TK_COLUMN case */
+    }
     case TK_COLUMN: {
-      if( !pParse->fillAgg && pExpr->iAgg>=0 ){
-        sqlite3VdbeAddOp(v, OP_AggGet, pExpr->iAggCtx, pExpr->iAgg);
-      }else if( pExpr->iColumn>=0 ){
+      if( pExpr->iColumn>=0 ){
         sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn);
         sqlite3ColumnDefault(v, pExpr->pTab, pExpr->iColumn);
       }else{
@@ -1600,7 +1613,8 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
       break;
     }
     case TK_AGG_FUNCTION: {
-      sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg);
+      AggInfo *pInfo = pExpr->pAggInfo;
+      sqlite3VdbeAddOp(v, OP_MemLoad, pInfo->aFunc[pExpr->iAgg].iMem, 0);
       break;
     }
     case TK_CONST_FUNC:
@@ -1610,7 +1624,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
       FuncDef *pDef;
       int nId;
       const char *zId;
-      int p2 = 0;
+      int constMask = 0;
       int i;
       u8 enc = pParse->db->enc;
       CollSeq *pColl = 0;
@@ -1621,7 +1635,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
       nExpr = sqlite3ExprCodeExprList(pParse, pList);
       for(i=0; i<nExpr && i<32; i++){
         if( sqlite3ExprIsConstant(pList->a[i].pExpr) ){
-          p2 |= (1<<i);
+          constMask |= (1<<i);
         }
         if( pDef->needCollSeq && !pColl ){
           pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
@@ -1631,7 +1645,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
         if( !pColl ) pColl = pParse->db->pDfltColl; 
         sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
       }
-      sqlite3VdbeOp3(v, OP_Function, nExpr, p2, (char*)pDef, P3_FUNCDEF);
+      sqlite3VdbeOp3(v, OP_Function, constMask, nExpr, (char*)pDef, P3_FUNCDEF);
       break;
     }
 #ifndef SQLITE_OMIT_SUBQUERY
@@ -2047,23 +2061,32 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
   return 1;
 }
 
+
 /*
-** Add a new element to the pParse->aAgg[] array and return its index.
-** The new element is initialized to zero.  The calling function is
-** expected to fill it in.
+** Add a new element to the pAggInfo->aCol[] array.  Return the index of
+** the new element.  Return a negative number if malloc fails.
 */
-static int appendAggInfo(Parse *pParse){
-  if( (pParse->nAgg & 0x7)==0 ){
-    int amt = pParse->nAgg + 8;
-    AggExpr *aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0]));
-    if( aAgg==0 ){
-      return -1;
-    }
-    pParse->aAgg = aAgg;
+static int addAggInfoColumn(AggInfo *pInfo){
+  int i;
+  i = sqlite3ArrayAllocate((void**)&pInfo->aCol, sizeof(pInfo->aCol[0]), 3);
+  if( i<0 ){
+    return -1;
   }
-  memset(&pParse->aAgg[pParse->nAgg], 0, sizeof(pParse->aAgg[0]));
-  return pParse->nAgg++;
-}
+  return i;
+}    
+
+/*
+** Add a new element to the pAggInfo->aFunc[] array.  Return the index of
+** the new element.  Return a negative number if malloc fails.
+*/
+static int addAggInfoFunc(AggInfo *pInfo){
+  int i;
+  i = sqlite3ArrayAllocate((void**)&pInfo->aFunc, sizeof(pInfo->aFunc[0]), 2);
+  if( i<0 ){
+    return -1;
+  }
+  return i;
+}    
 
 /*
 ** This is an xFunc for walkExprTree() used to implement 
@@ -2074,63 +2097,112 @@ static int appendAggInfo(Parse *pParse){
 */
 static int analyzeAggregate(void *pArg, Expr *pExpr){
   int i;
-  AggExpr *pAgg;
   NameContext *pNC = (NameContext *)pArg;
   Parse *pParse = pNC->pParse;
   SrcList *pSrcList = pNC->pSrcList;
+  AggInfo *pAggInfo = pNC->pAggInfo;
+  
 
   switch( pExpr->op ){
     case TK_COLUMN: {
-      for(i=0; pSrcList && i<pSrcList->nSrc; i++){
-        if( pExpr->iTable==pSrcList->a[i].iCursor ){
-          pAgg = pParse->aAgg;
-          for(i=0; i<pParse->nAgg; i++, pAgg++){
-            Expr *pE;
-            if( pAgg->isAgg ) continue;
-            pE = pAgg->pExpr;
-            if( pE->iTable==pExpr->iTable && pE->iColumn==pExpr->iColumn ){
-              break;
+      /* Check to see if the column is in one of the tables in the FROM
+      ** clause of the aggregate query */
+      if( pSrcList ){
+        struct SrcList_item *pItem = pSrcList->a;
+        for(i=0; i<pSrcList->nSrc; i++, pItem++){
+          struct AggInfo_col *pCol;
+          if( pExpr->iTable==pItem->iCursor ){
+            /* If we reach this point, it means that pExpr refers to a table
+            ** that is in the FROM clause of the aggregate query.  
+            **
+            ** Make an entry for the column in pAggInfo->aCol[] if there
+            ** is not an entry there already.
+            */
+            pCol = pAggInfo->aCol;
+            for(i=0; i<pAggInfo->nColumn; i++, pCol++){
+              if( pCol->iTable==pExpr->iTable &&
+                  pCol->iColumn==pExpr->iColumn ){
+                break;
+              }
             }
-          }
-          if( i>=pParse->nAgg ){
-            i = appendAggInfo(pParse);
-            if( i<0 ) return 1;
-            pAgg = &pParse->aAgg[i];
-            pAgg->isAgg = 0;
-            pAgg->pExpr = pExpr;
-          }
-          pExpr->iAgg = i;
-          pExpr->iAggCtx = pNC->nDepth;
-          return 1;
-        }
+            if( i>=pAggInfo->nColumn && (i = addAggInfoColumn(pAggInfo))>=0 ){
+              pCol = &pAggInfo->aCol[i];
+              pCol->iTable = pExpr->iTable;
+              pCol->iColumn = pExpr->iColumn;
+              pCol->iMem = pParse->nMem++;
+              pCol->iSorterColumn = -1;
+              if( pAggInfo->pGroupBy ){
+                int j, n;
+                ExprList *pGB = pAggInfo->pGroupBy;
+                struct ExprList_item *pTerm = pGB->a;
+                n = pGB->nExpr;
+                for(j=0; j<n; j++, pTerm++){
+                  Expr *pE = pTerm->pExpr;
+                  if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable &&
+                      pE->iColumn==pExpr->iColumn ){
+                    pCol->iSorterColumn = j;
+                    break;
+                  }
+                }
+              }
+              if( pCol->iSorterColumn<0 ){
+                pCol->iSorterColumn = pAggInfo->nSortingColumn++;
+              }
+            }
+            /* There is now an entry for pExpr in pAggInfo->aCol[] (either
+            ** because it was there before or because we just created it).
+            ** Convert the pExpr to be a TK_AGG_COLUMN referring to that
+            ** pAggInfo->aCol[] entry.
+            */
+            pExpr->pAggInfo = pAggInfo;
+            pExpr->op = TK_AGG_COLUMN;
+            pExpr->iAgg = i;
+            break;
+          } /* endif pExpr->iTable==pItem->iCursor */
+        } /* end loop over pSrcList */
       }
       return 1;
     }
     case TK_AGG_FUNCTION: {
+      /* The pNC->nDepth==0 test causes aggregate functions in subqueries
+      ** to be ignored */
       if( pNC->nDepth==0 ){
-        pAgg = pParse->aAgg;
-        for(i=0; i<pParse->nAgg; i++, pAgg++){
-          if( !pAgg->isAgg ) continue;
-          if( sqlite3ExprCompare(pAgg->pExpr, pExpr) ){
+        /* Check to see if pExpr is a duplicate of another aggregate 
+        ** function that is already in the pAggInfo structure
+        */
+        struct AggInfo_func *pItem = pAggInfo->aFunc;
+        for(i=0; i<pAggInfo->nFunc; i++, pItem++){
+          if( sqlite3ExprCompare(pItem->pExpr, pExpr) ){
             break;
           }
         }
-        if( i>=pParse->nAgg ){
+        if( i>=pAggInfo->nFunc ){
+          /* pExpr is original.  Make a new entry in pAggInfo->aFunc[]
+          */
           u8 enc = pParse->db->enc;
-          i = appendAggInfo(pParse);
-          if( i<0 ) return 1;
-          pAgg = &pParse->aAgg[i];
-          pAgg->isAgg = 1;
-          pAgg->pExpr = pExpr;
-          pAgg->pFunc = sqlite3FindFunction(pParse->db,
-               pExpr->token.z, pExpr->token.n,
-               pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
+          i = addAggInfoFunc(pAggInfo);
+          if( i>=0 ){
+            pItem = &pAggInfo->aFunc[i];
+            pItem->pExpr = pExpr;
+            pItem->iMem = pParse->nMem++;
+            pItem->pFunc = sqlite3FindFunction(pParse->db,
+                   pExpr->token.z, pExpr->token.n,
+                   pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
+          }
         }
+        /* Make pExpr point to the appropriate pAggInfo->aFunc[] entry
+        */
         pExpr->iAgg = i;
+        pExpr->pAggInfo = pAggInfo;
         return 1;
       }
     }
   }
+
+  /* Recursively walk subqueries looking for TK_COLUMN nodes that need
+  ** to be changed to TK_AGG_COLUMN.  But increment nDepth so that
+  ** TK_AGG_FUNCTION nodes in subqueries will be unchanged.
+  */
   if( pExpr->pSelect ){
     pNC->nDepth++;
     walkSelectExpr(pExpr->pSelect, analyzeAggregate, pNC);
index bfc2bd597a0cda5b954cb6ae27451b6d59552f8b..0169111678ef33d87b5f201e3277695a37857c46 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.300 2005/08/29 23:00:04 drh Exp $
+** $Id: main.c,v 1.301 2005/09/07 21:22:46 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -875,7 +875,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
     rc = SQLITE_OK;
   }else{
     rc = sqlite3VdbeReset((Vdbe*)pStmt);
-    sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0, 0);
+    sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0);
   }
   return rc;
 }
index 750f8a44184ce44252530c42a2475673413115cd..b008a01dd808d50ccd9318892723152b0697c63a 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.175 2005/07/08 12:13:05 drh Exp $
+** @(#) $Id: parse.y,v 1.176 2005/09/07 21:22:46 drh Exp $
 */
 
 // All token codes are small integers with #defines that begin with "TK_"
@@ -93,7 +93,7 @@ struct AttachKey { int type;  Token key; };
 // add them to the parse.h output file.
 //
 %nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
-          COLUMN AGG_FUNCTION CONST_FUNC.
+          COLUMN AGG_FUNCTION AGG_COLUMN CONST_FUNC.
 
 // Input is a single SQL command
 input ::= cmdlist.
index 94943a5414b0500a924b2c3aed65984c999524b9..dc1a57890b24d1b28a9a19a7dcd0d6bc004d1438 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.260 2005/09/05 20:06:49 drh Exp $
+** $Id: select.c,v 1.261 2005/09/07 21:22:47 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -346,7 +346,7 @@ static void codeLimiter(
   int iBreak,       /* Jump here to end the loop */
   int nPop          /* Number of times to pop stack when jumping */
 ){
-  if( p->iOffset>=0 ){
+  if( p->iOffset>=0 && iContinue!=0 ){
     int addr = sqlite3VdbeCurrentAddr(v) + 3;
     if( nPop>0 ) addr++;
     sqlite3VdbeAddOp(v, OP_MemIncr, p->iOffset, 0);
@@ -357,7 +357,7 @@ static void codeLimiter(
     sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue);
     VdbeComment((v, "# skip OFFSET records"));
   }
-  if( p->iLimit>=0 ){
+  if( p->iLimit>=0 && iBreak!=0 ){
     sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, iBreak);
     VdbeComment((v, "# exit when LIMIT reached"));
   }
@@ -435,13 +435,15 @@ static int selectInnerLoop(
   }
 
   switch( eDest ){
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
     /* In this mode, write each query result to the key of the temporary
     ** table iParm.
     */
+#ifndef SQLITE_OMIT_COMPOUND_SELECT
     case SRT_Union: {
       sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT);
-      sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
+      if( aff ){
+        sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
+      }
       sqlite3VdbeAddOp(v, OP_IdxInsert, iParm, 0);
       break;
     }
@@ -1350,6 +1352,20 @@ static void createSortingIndex(Parse *pParse, Select *p, ExprList *pOrderBy){
   }
 }
 
+/*
+** The opcode at addr is an OP_OpenVirtual that created a sorting
+** index tha we ended up not needing.  This routine changes that
+** opcode to OP_Noop.
+*/
+static void uncreateSortingIndex(Parse *pParse, int addr){
+  Vdbe *v = pParse->pVdbe;
+  VdbeOp *pOp = sqlite3VdbeGetOp(v, addr);
+  sqlite3VdbeChangeP3(v, addr, 0, 0);
+  pOp->opcode = OP_Noop;
+  pOp->p1 = 0;
+  pOp->p2 = 0;
+}
+
 #ifndef SQLITE_OMIT_COMPOUND_SELECT
 /*
 ** Return the appropriate collating sequence for the iCol-th column of
@@ -2276,6 +2292,7 @@ int sqlite3SelectResolve(
   ExprList *pEList;          /* Result set. */
   int i;                     /* For-loop variable used in multiple places */
   NameContext sNC;           /* Local name-context */
+  ExprList *pGroupBy;        /* The group by clause */
 
   /* If this routine has run before, return immediately. */
   if( p->isResolved ){
@@ -2319,18 +2336,6 @@ int sqlite3SelectResolve(
   sNC.pSrcList = p->pSrc;
   sNC.pNext = pOuterNC;
 
-  /* NameContext.nDepth stores the depth of recursion for this query. For
-  ** an outer query (e.g. SELECT * FROM sqlite_master) this is 1. For
-  ** a subquery it is 2. For a subquery of a subquery, 3. And so on. 
-  ** Parse.nMaxDepth is the maximum depth for any subquery resolved so
-  ** far. This is used to determine the number of aggregate contexts
-  ** required at runtime.
-  */
-  sNC.nDepth = (pOuterNC?pOuterNC->nDepth+1:1);
-  if( sNC.nDepth>pParse->nMaxDepth ){
-    pParse->nMaxDepth = sNC.nDepth;
-  }
-
   /* Resolve names in the result set. */
   pEList = p->pEList;
   if( !pEList ) return SQLITE_ERROR;
@@ -2345,7 +2350,8 @@ int sqlite3SelectResolve(
   ** expression, do not allow aggregates in any of the other expressions.
   */
   assert( !p->isAgg );
-  if( p->pGroupBy || sNC.hasAgg ){
+  pGroupBy = p->pGroupBy;
+  if( pGroupBy || sNC.hasAgg ){
     p->isAgg = 1;
   }else{
     sNC.allowAgg = 0;
@@ -2353,7 +2359,7 @@ int sqlite3SelectResolve(
 
   /* If a HAVING clause is present, then there must be a GROUP BY clause.
   */
-  if( p->pHaving && !p->pGroupBy ){
+  if( p->pHaving && !pGroupBy ){
     sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
     return SQLITE_ERROR;
   }
@@ -2370,49 +2376,112 @@ int sqlite3SelectResolve(
   if( sqlite3ExprResolveNames(&sNC, p->pWhere) ||
       sqlite3ExprResolveNames(&sNC, p->pHaving) ||
       processOrderGroupBy(&sNC, p->pOrderBy, "ORDER") ||
-      processOrderGroupBy(&sNC, p->pGroupBy, "GROUP")
+      processOrderGroupBy(&sNC, pGroupBy, "GROUP")
   ){
     return SQLITE_ERROR;
   }
 
+  /* Make sure the GROUP BY clause does not contain aggregate functions.
+  */
+  if( pGroupBy ){
+    struct ExprList_item *pItem;
+  
+    for(i=0, pItem=pGroupBy->a; i<pGroupBy->nExpr; i++, pItem++){
+      if( ExprHasProperty(pItem->pExpr, EP_Agg) ){
+        sqlite3ErrorMsg(pParse, "aggregate functions are not allowed in "
+            "the GROUP BY clause");
+        return SQLITE_ERROR;
+      }
+    }
+  }
+
   return SQLITE_OK;
 }
 
 /*
-** An instance of the following struct is used by sqlite3Select()
-** to save aggregate related information from the Parse object
-** at the start of each call and to restore it at the end. See
-** saveAggregateInfo() and restoreAggregateInfo().
-*/ 
-struct AggregateInfo {
-  int nAgg;
-  AggExpr *aAgg;
-};
-typedef struct AggregateInfo AggregateInfo;
-
-/* 
-** Copy aggregate related information from the Parse structure
-** into the AggregateInfo structure. Zero the aggregate related
-** values in the Parse struct.
+** Reset the aggregate accumulator.
+**
+** The aggregate accumulator is a set of memory cells that hold
+** intermediate results while calculating an aggregate.  This
+** routine simply stores NULLs in all of those memory cells.
 */
-static void saveAggregateInfo(Parse *pParse, AggregateInfo *pInfo){
-  pInfo->aAgg = pParse->aAgg;
-  pInfo->nAgg = pParse->nAgg;
-  pParse->aAgg = 0;
-  pParse->nAgg = 0;
+static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
+  Vdbe *v = pParse->pVdbe;
+  int i;
+  int addr;
+  if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){
+    return;
+  }
+  sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+  for(i=0; i<pAggInfo->nColumn; i++){
+    addr = sqlite3VdbeAddOp(v, OP_MemStore, pAggInfo->aCol[i].iMem, 0);
+  }
+  for(i=0; i<pAggInfo->nFunc; i++){
+    addr = sqlite3VdbeAddOp(v, OP_MemStore, pAggInfo->aFunc[i].iMem, 0);
+  }
+  sqlite3VdbeChangeP2(v, addr, 1);
 }
 
 /*
-** Copy aggregate related information from the AggregateInfo struct
-** back into the Parse structure. The aggregate related information
-** currently stored in the Parse structure is deleted.
+** Invoke the OP_AggFinalize opcode for every aggregate function
+** in the AggInfo structure.
 */
-static void restoreAggregateInfo(Parse *pParse, AggregateInfo *pInfo){
-  sqliteFree(pParse->aAgg);
-  pParse->aAgg = pInfo->aAgg;
-  pParse->nAgg = pInfo->nAgg;
+static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
+  Vdbe *v = pParse->pVdbe;
+  int i;
+  struct AggInfo_func *pF;
+  for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
+    sqlite3VdbeAddOp(v, OP_AggFinal, pF->iMem, 0);
+  }
 }
-  
+
+/*
+** Update the accumulator memory cells for an aggregate based on
+** the current cursor position.
+*/
+static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
+  Vdbe *v = pParse->pVdbe;
+  int i;
+  struct AggInfo_func *pF;
+  struct AggInfo_col *pC;
+  Expr fauxExpr;
+
+  pAggInfo->directMode = 1;
+  for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
+    int nArg;
+    ExprList *pList = pF->pExpr->pList;
+    if( pList ){
+      nArg = pList->nExpr;
+      sqlite3ExprCodeExprList(pParse, pList);
+    }else{
+      nArg = 0;
+    }
+    if( pF->pFunc->needCollSeq ){
+      CollSeq *pColl = 0;
+      struct ExprList_item *pItem;
+      int j;
+      for(j=0, pItem=pList->a; !pColl && j<pList->nExpr; j++, pItem++){
+        pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+      }
+      if( !pColl ){
+        pColl = pParse->db->pDfltColl;
+      }
+      sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+    }
+    sqlite3VdbeOp3(v, OP_AggStep, pF->iMem, nArg, (void*)pF->pFunc, P3_FUNCDEF);
+  }
+  memset(&fauxExpr, 0, sizeof(fauxExpr));
+  fauxExpr.op = TK_AGG_COLUMN;
+  fauxExpr.pAggInfo = pAggInfo;
+  for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
+    fauxExpr.iAgg = i;
+    sqlite3ExprCode(pParse, &fauxExpr);
+    sqlite3VdbeAddOp(v, OP_MemStore, pC->iMem, 1);
+  }
+  pAggInfo->directMode = 0;
+}
+
+
 /*
 ** Generate code for the given SELECT statement.
 **
@@ -2475,9 +2544,9 @@ int sqlite3Select(
   int *pParentAgg,       /* True if pParent uses aggregate functions */
   char *aff              /* If eDest is SRT_Union, the affinity string */
 ){
-  int i;
-  WhereInfo *pWInfo;
-  Vdbe *v;
+  int i, j;              /* Loop counters */
+  WhereInfo *pWInfo;     /* Return from sqlite3WhereBegin() */
+  Vdbe *v;               /* The virtual machine under construction */
   int isAgg;             /* True for select lists like "count(*)" */
   ExprList *pEList;      /* List of columns to extract. */
   SrcList *pTabList;     /* List of tables to select from */
@@ -2488,10 +2557,11 @@ int sqlite3Select(
   int isDistinct;        /* True if the DISTINCT keyword is present */
   int distinct;          /* Table to use for the distinct set */
   int rc = 1;            /* Value to return from this function */
-  AggregateInfo sAggInfo;
+  AggInfo sAggInfo;      /* Information used by aggregate queries */
 
   if( sqlite3_malloc_failed || pParse->nErr || p==0 ) return 1;
   if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
+  memset(&sAggInfo, 0, sizeof(sAggInfo));
 
 #ifndef SQLITE_OMIT_COMPOUND_SELECT
   /* If there is are a sequence of queries, do the earlier ones first.
@@ -2507,9 +2577,8 @@ int sqlite3Select(
   }
 #endif
 
-  saveAggregateInfo(pParse, &sAggInfo);
   pOrderBy = p->pOrderBy;
-  if( eDest==SRT_Union || eDest==SRT_Except || eDest==SRT_Discard ){
+  if( IgnorableOrderby(eDest) ){
     p->pOrderBy = 0;
   }
   if( sqlite3SelectResolve(pParse, p, 0) ){
@@ -2548,14 +2617,8 @@ int sqlite3Select(
 
   /* ORDER BY is ignored for some destinations.
   */
-  switch( eDest ){
-    case SRT_Union:
-    case SRT_Except:
-    case SRT_Discard:
-      pOrderBy = 0;
-      break;
-    default:
-      break;
+  if( IgnorableOrderby(eDest) ){
+    pOrderBy = 0;
   }
 
   /* Begin generating code.
@@ -2576,23 +2639,24 @@ int sqlite3Select(
   for(i=0; i<pTabList->nSrc; i++){
     const char *zSavedAuthContext = 0;
     int needRestoreContext;
+    struct SrcList_item *pItem = &pTabList->a[i];
 
-    if( pTabList->a[i].pSelect==0 ) continue;
-    if( pTabList->a[i].zName!=0 ){
+    if( pItem->pSelect==0 ) continue;
+    if( pItem->zName!=0 ){
       zSavedAuthContext = pParse->zAuthContext;
-      pParse->zAuthContext = pTabList->a[i].zName;
+      pParse->zAuthContext = pItem->zName;
       needRestoreContext = 1;
     }else{
       needRestoreContext = 0;
     }
-    sqlite3Select(pParse, pTabList->a[i].pSelect, SRT_TempTable, 
-                 pTabList->a[i].iCursor, p, i, &isAgg, 0);
+    sqlite3Select(pParse, pItem->pSelect, SRT_TempTable, 
+                 pItem->iCursor, p, i, &isAgg, 0);
     if( needRestoreContext ){
       pParse->zAuthContext = zSavedAuthContext;
     }
     pTabList = p->pSrc;
     pWhere = p->pWhere;
-    if( eDest!=SRT_Union && eDest!=SRT_Except && eDest!=SRT_Discard ){
+    if( !IgnorableOrderby(eDest) ){
       pOrderBy = p->pOrderBy;
     }
     pGroupBy = p->pGroupBy;
@@ -2652,55 +2716,6 @@ int sqlite3Select(
     sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, pEList->nExpr);
   }
 
-  /* Do an analysis of aggregate expressions.
-  */
-  if( isAgg || pGroupBy ){
-    NameContext sNC;
-    memset(&sNC, 0, sizeof(sNC));
-    sNC.pParse = pParse;
-    sNC.pSrcList = pTabList;
-
-    assert( pParse->nAgg==0 );
-    isAgg = 1;
-    if( sqlite3ExprAnalyzeAggList(&sNC, pEList) ){
-      goto select_end;
-    }
-    if( sqlite3ExprAnalyzeAggList(&sNC, pGroupBy) ){
-      goto select_end;
-    }
-    if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
-      goto select_end;
-    }
-    if( sqlite3ExprAnalyzeAggList(&sNC, pOrderBy) ){
-      goto select_end;
-    }
-  }
-
-  /* Reset the aggregator
-  */
-  if( isAgg ){
-    int addr = sqlite3VdbeAddOp(v, OP_AggReset, (pGroupBy?0:1), pParse->nAgg);
-    for(i=0; i<pParse->nAgg; i++){
-      FuncDef *pFunc;
-      if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){
-        int nExpr = 0;
-#ifdef SQLITE_SSE
-        Expr *pAggExpr = pParse->aAgg[i].pExpr;
-        if( pAggExpr && pAggExpr->pList ){
-          nExpr = pAggExpr->pList->nExpr;
-        }
-#endif
-        sqlite3VdbeOp3(v, OP_AggInit, nExpr, i, (char*)pFunc, P3_FUNCDEF);
-      }
-    }
-    if( pGroupBy ){
-      KeyInfo *pKey = keyInfoFromExprList(pParse, pGroupBy);
-      if( 0==pKey ){
-        goto select_end;
-      }
-      sqlite3VdbeChangeP3(v, addr, (char *)pKey, P3_KEYINFO_HANDOFF);
-    }
-  }
 
   /* Initialize the memory cell to NULL for SRT_Mem or 0 for SRT_Exists
   */
@@ -2721,94 +2736,264 @@ int sqlite3Select(
     distinct = -1;
   }
 
-  /* Begin the database scan
-  */
-  pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,
-                             pGroupBy ? 0 : &pOrderBy);
-  if( pWInfo==0 ) goto select_end;
+  /* Aggregate and non-aggregate queries are handled differently */
+  if( !isAgg && pGroupBy==0 ){
+    /* This case is for non-aggregate queries
+    ** Begin the database scan
+    */
+    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy);
+    if( pWInfo==0 ) goto select_end;
 
-  /* Use the standard inner loop if we are not dealing with
-  ** aggregates
-  */
-  if( !isAgg ){
+    /* Use the standard inner loop
+    */
     if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
                     iParm, pWInfo->iContinue, pWInfo->iBreak, aff) ){
        goto select_end;
     }
-  }
 
-  /* If we are dealing with aggregates, then do the special aggregate
-  ** processing.  
-  */
-  else{
-    AggExpr *pAgg;
-    int lbl1 = 0;
-    pParse->fillAgg = 1;
+    /* End the database scan loop.
+    */
+    sqlite3WhereEnd(pWInfo);
+  }else{
+    /* This is the processing for aggregate queries */
+    NameContext sNC;    /* Name context for processing aggregate information */
+    int iAMem;          /* First Mem address for storing current GROUP BY */
+    int iBMem;          /* First Mem address for previous GROUP BY */
+    int iUseFlag;       /* Mem address holding flag indicating that at least
+                        ** one row of the input to the aggregator has been
+                        ** processed */
+    int iAbortFlag;     /* Mem address which causes query abort if positive */
+    int groupBySort;    /* Rows come from source in GROUP BY order */
+
+
+    /* The following variables hold addresses or labels for parts of the
+    ** virtual machine program we are putting together */
+    int addrOutputRow;      /* Start of subroutine that outputs a result row */
+    int addrSetAbort;       /* Set the abort flag and return */
+    int addrInitializeLoop; /* Start of code that initializes the input loop */
+    int addrTopOfLoop;      /* Top of the input loop */
+    int addrGroupByChange;  /* Code that runs when any GROUP BY term changes */
+    int addrProcessRow;     /* Code to process a single input row */
+    int addrEnd;            /* End of all processing */
+    int addrSortingIdx;     /* The OP_OpenVirtual for the sorting index */
+
+    addrEnd = sqlite3VdbeMakeLabel(v);
+
+    /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
+    ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
+    ** SELECT statement.
+    */
+    memset(&sNC, 0, sizeof(sNC));
+    sNC.pParse = pParse;
+    sNC.pSrcList = pTabList;
+    sNC.pAggInfo = &sAggInfo;
+    sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
+    if( sqlite3ExprAnalyzeAggList(&sNC, pEList) ){
+      goto select_end;
+    }
+    if( sqlite3ExprAnalyzeAggList(&sNC, pOrderBy) ){
+      goto select_end;
+    }
+    if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
+      goto select_end;
+    }
+    sAggInfo.nAccumulator = sAggInfo.nColumn;
+    for(i=0; i<sAggInfo.nFunc; i++){
+      if( sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList) ){
+        goto select_end;
+      }
+    }
+
+    /* Processing for aggregates with GROUP BY is very different and
+    ** much more complex tha aggregates without a GROUP BY.
+    */
     if( pGroupBy ){
-      sqlite3ExprCodeExprList(pParse, pGroupBy);
-      /* No affinity string is attached to the following OP_MakeRecord 
-      ** because we do not need to do any coercion of datatypes. */
-      sqlite3VdbeAddOp(v, OP_MakeRecord, pGroupBy->nExpr, 0);
-      lbl1 = sqlite3VdbeMakeLabel(v);
-      sqlite3VdbeAddOp(v, OP_AggFocus, 0, lbl1);
-    }
-    for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
-      if( pAgg->isAgg ) continue;
-      sqlite3ExprCode(pParse, pAgg->pExpr);
-      sqlite3VdbeAddOp(v, OP_AggSet, 0, i);
-    }
-    pParse->fillAgg = 0;
-    if( lbl1<0 ){
-      sqlite3VdbeResolveLabel(v, lbl1);
-    }
-    for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
-      Expr *pE;
-      int nExpr;
-      FuncDef *pDef;
-      if( !pAgg->isAgg ) continue;
-      assert( pAgg->pFunc!=0 );
-      assert( pAgg->pFunc->xStep!=0 );
-      pDef = pAgg->pFunc;
-      pE = pAgg->pExpr;
-      assert( pE!=0 );
-      assert( pE->op==TK_AGG_FUNCTION );
-      nExpr = sqlite3ExprCodeExprList(pParse, pE->pList);
-      sqlite3VdbeAddOp(v, OP_Integer, i, 0);
-      if( pDef->needCollSeq ){
-        CollSeq *pColl = 0;
-        int j;
-        for(j=0; !pColl && j<nExpr; j++){
-          pColl = sqlite3ExprCollSeq(pParse, pE->pList->a[j].pExpr);
+      KeyInfo *pKeyInfo;  /* Keying information for the group by clause */
+
+      /* Create labels that we will be needing
+      */
+     
+      addrInitializeLoop = sqlite3VdbeMakeLabel(v);
+      addrGroupByChange = sqlite3VdbeMakeLabel(v);
+      addrProcessRow = sqlite3VdbeMakeLabel(v);
+
+      /* If there is a GROUP BY clause we might need a sorting index to
+      ** implement it.  Allocate that sorting index now.  If it turns out
+      ** that we do not need it after all, the OpenVirtual instruction
+      ** will be converted into a Noop.  
+      */
+      sAggInfo.sortingIdx = pParse->nTab++;
+      pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
+      addrSortingIdx =
+          sqlite3VdbeOp3(v, OP_OpenVirtual, sAggInfo.sortingIdx,
+                         sAggInfo.nSortingColumn,
+                         (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+
+      /* Initialize memory locations used by GROUP BY aggregate processing
+      */
+      iUseFlag = pParse->nMem++;
+      iAbortFlag = pParse->nMem++;
+      iAMem = pParse->nMem;
+      pParse->nMem += pGroupBy->nExpr;
+      iBMem = pParse->nMem;
+      pParse->nMem += pGroupBy->nExpr;
+      sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+      sqlite3VdbeAddOp(v, OP_MemStore, iAbortFlag, 0);
+      sqlite3VdbeAddOp(v, OP_MemStore, iUseFlag, 1);
+      sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+      sqlite3VdbeAddOp(v, OP_MemStore, iAMem, 1);
+      sqlite3VdbeAddOp(v, OP_Goto, 0, addrInitializeLoop);
+
+      /* Generate a subroutine that outputs a single row of the result
+      ** set.  This subroutine first looks at the iUseFlag.  If iUseFlag
+      ** is less than or equal to zero, the subroutine is a no-op.  If
+      ** the processing calls for the query to abort, this subroutine
+      ** increments the iAbortFlag memory location before returning in
+      ** order to signal the caller to abort.
+      */
+      addrSetAbort = sqlite3VdbeCurrentAddr(v);
+      sqlite3VdbeAddOp(v, OP_MemIncr, iAbortFlag, 0);
+      sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+      addrOutputRow = sqlite3VdbeCurrentAddr(v);
+      sqlite3VdbeAddOp(v, OP_IfMemPos, iUseFlag, addrOutputRow+2);
+      sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+      finalizeAggFunctions(pParse, &sAggInfo);
+      if( pHaving ){
+        sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, 1);
+      }
+      rc = selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
+                           distinct, eDest, iParm, 
+                           addrOutputRow+1, addrSetAbort, aff);
+      if( rc ){
+        goto select_end;
+      }
+      sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+
+      /* Begin a loop that will extract all source rows in GROUP BY order.
+      ** This might involve two separate loops with an OP_Sort in between, or
+      ** it might be a single loop that uses an index to extract information
+      ** in the right order to begin with.
+      */
+      sqlite3VdbeResolveLabel(v, addrInitializeLoop);
+      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy);
+      if( pGroupBy==0 ){
+        /* The optimizer is able to deliver rows in group by order so
+        ** we do not have to sort.  The OP_OpenVirtual table will be
+        ** cancelled later because we still need to use the pKeyInfo
+        */
+        pGroupBy = p->pGroupBy;
+        groupBySort = 0;
+      }else{
+        /* Rows are coming out in undetermined order.  We have to push
+        ** each row into a sorting index, terminate the first loop,
+        ** then loop over the sorting index in order to get the output
+        ** in sorted order
+        */
+        groupBySort = 1;
+        sqlite3ExprCodeExprList(pParse, pGroupBy);
+        sqlite3VdbeAddOp(v, OP_Sequence, sAggInfo.sortingIdx, 0);
+        j = pGroupBy->nExpr+1;
+        for(i=0; i<sAggInfo.nColumn; i++){
+          struct AggInfo_col *pCol = &sAggInfo.aCol[i];
+          if( pCol->iSorterColumn<j ) continue;
+          if( pCol->iColumn<0 ){
+            sqlite3VdbeAddOp(v, OP_Rowid, pCol->iTable, 0);
+          }else{
+            sqlite3VdbeAddOp(v, OP_Column, pCol->iTable, pCol->iColumn);
+          }
+          j++;
         }
-        if( !pColl ) pColl = pParse->db->pDfltColl;
-        sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+        sqlite3VdbeAddOp(v, OP_MakeRecord, j, 0);
+        sqlite3VdbeAddOp(v, OP_IdxInsert, sAggInfo.sortingIdx, 0);
+        sqlite3WhereEnd(pWInfo);
+        sqlite3VdbeAddOp(v, OP_Sort, sAggInfo.sortingIdx, 0);
+        sAggInfo.useSortingIdx = 1;
       }
-      sqlite3VdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_FUNCDEF);
-    }
-  }
 
-  /* End the database scan loop.
-  */
-  sqlite3WhereEnd(pWInfo);
+      /* Evaluate the current GROUP BY terms and store in b0, b1, b2...
+      ** (b0 is memory location iBMem+0, b1 is iBMem+1, and so forth)
+      ** Then compare the current GROUP BY terms against the GROUP BY terms
+      ** from the previous row currently stored in a0, a1, a2...
+      */
+      addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
+      for(j=0; j<pGroupBy->nExpr; j++){
+        if( groupBySort ){
+          sqlite3VdbeAddOp(v, OP_Column, sAggInfo.sortingIdx, j);
+        }else{
+          sAggInfo.directMode = 1;
+          sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr);
+        }
+        sqlite3VdbeAddOp(v, OP_MemStore, iBMem+j, j<pGroupBy->nExpr-1);
+      }
+      for(j=pGroupBy->nExpr-1; j>=0; j--){
+        if( j<pGroupBy->nExpr-1 ){
+          sqlite3VdbeAddOp(v, OP_MemLoad, iBMem+j, 0);
+        }
+        sqlite3VdbeAddOp(v, OP_MemLoad, iAMem+j, 0);
+        if( j==0 ){
+          sqlite3VdbeAddOp(v, OP_Eq, 0, addrProcessRow);
+        }else{
+          sqlite3VdbeAddOp(v, OP_Ne, 0x100, addrGroupByChange);
+        }
+        sqlite3VdbeChangeP3(v, -1, (void*)pKeyInfo->aColl[j], P3_COLLSEQ);
+      }
 
-  /* If we are processing aggregates, we need to set up a second loop
-  ** over all of the aggregate values and process them.
-  */
-  if( isAgg ){
-    int endagg = sqlite3VdbeMakeLabel(v);
-    int startagg;
-    startagg = sqlite3VdbeAddOp(v, OP_AggNext, 0, endagg);
-    if( pHaving ){
-      sqlite3ExprIfFalse(pParse, pHaving, startagg, 1);
-    }
-    if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
-                    iParm, startagg, endagg, aff) ){
-      goto select_end;
+      /* Generate code that runs whenever the GROUP BY changes.
+      ** Change in the GROUP BY are detected by the previous code
+      ** block.  If there were no changes, this block is skipped.
+      **
+      ** This code copies current group by terms in b0,b1,b2,...
+      ** over to a0,a1,a2.  It then calls the output subroutine
+      ** and resets the aggregate accumulator registers in preparation
+      ** for the next GROUP BY batch.
+      */
+      sqlite3VdbeResolveLabel(v, addrGroupByChange);
+      for(j=0; j<pGroupBy->nExpr; j++){
+        sqlite3VdbeAddOp(v, OP_MemLoad, iBMem+j, 0);
+        sqlite3VdbeAddOp(v, OP_MemStore, iAMem+j, 1);
+      }
+      sqlite3VdbeAddOp(v, OP_Gosub, 0, addrOutputRow);
+      sqlite3VdbeAddOp(v, OP_IfMemPos, iAbortFlag, addrEnd);
+      resetAccumulator(pParse, &sAggInfo);
+
+      /* Update the aggregate accumulators based on the content of
+      ** the current row
+      */
+      sqlite3VdbeResolveLabel(v, addrProcessRow);
+      updateAccumulator(pParse, &sAggInfo);
+      sqlite3VdbeAddOp(v, OP_MemIncr, iUseFlag, 0);
+
+      /* End of the loop
+      */
+      if( groupBySort ){
+        sqlite3VdbeAddOp(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
+      }else{
+        sqlite3WhereEnd(pWInfo);
+        uncreateSortingIndex(pParse, addrSortingIdx);
+      }
+
+      /* Output the final row of result
+      */
+      sqlite3VdbeAddOp(v, OP_Gosub, 0, addrOutputRow);
+      
+    } /* endif pGroupBy */
+    else {
+      /* This case runs if the aggregate has no GROUP BY clause.  The
+      ** processing is much simpler since there is only a single row
+      ** of output.
+      */
+      resetAccumulator(pParse, &sAggInfo);
+      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
+      updateAccumulator(pParse, &sAggInfo);
+      sqlite3WhereEnd(pWInfo);
+      finalizeAggFunctions(pParse, &sAggInfo);
+      pOrderBy = 0;
+      selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1, 
+                      eDest, iParm, addrEnd, addrEnd, aff);
     }
-    sqlite3VdbeAddOp(v, OP_Goto, 0, startagg);
-    sqlite3VdbeResolveLabel(v, endagg);
-    sqlite3VdbeAddOp(v, OP_Noop, 0, 0);
-  }
+    sqlite3VdbeResolveLabel(v, addrEnd);
+    
+  } /* endif aggregate query */
 
   /* If there is an ORDER BY clause, then we need to sort the results
   ** and send them to the callback one by one.
@@ -2840,6 +3025,7 @@ int sqlite3Select(
   ** successful coding of the SELECT.
   */
 select_end:
-  restoreAggregateInfo(pParse, &sAggInfo);
+  sqliteFree(sAggInfo.aCol);
+  sqliteFree(sAggInfo.aFunc);
   return rc;
 }
index 4dc7ed053041eb9eb488c2f685dec88a87950a7c..16ab6ae2fa25089634d07121ad15411b5f94bb93 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.407 2005/09/01 03:07:44 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.408 2005/09/07 21:22:47 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -298,7 +298,7 @@ extern int sqlite3_iMallocReset; /* Set iMallocFail to this when it reaches 0 */
 /*
 ** Forward references to structures
 */
-typedef struct AggExpr AggExpr;
+typedef struct AggInfo AggInfo;
 typedef struct AuthContext AuthContext;
 typedef struct CollSeq CollSeq;
 typedef struct Column Column;
@@ -788,6 +788,47 @@ struct Token {
   unsigned n    : 31;     /* Number of characters in this token */
 };
 
+/*
+** An instance of this structure contains information needed to generate
+** code for a SELECT that contains aggregate functions.
+**
+** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a
+** pointer to this structure.  The Expr.iColumn field is the index in
+** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate
+** code for that node.
+**
+** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the
+** original Select structure that describes the SELECT statement.  These
+** fields do not need to be freed when deallocating the AggInfo structure.
+*/
+struct AggInfo {
+  u8 directMode;          /* Direct rendering mode means take data directly
+                          ** from source tables rather than from accumulators */
+  u8 useSortingIdx;       /* In direct mode, reference the sorting index rather
+                          ** than the source table */
+  int sortingIdx;         /* Cursor number of the sorting index */
+  ExprList *pGroupBy;     /* The group by clause */
+  int nSortingColumn;     /* Number of columns in the sorting index */
+  struct AggInfo_col {    /* For each column used in source tables */
+    int iTable;              /* Cursor number of the source table */
+    int iColumn;             /* Column number within the source table */
+    int iSorterColumn;       /* Column number in the sorting index */
+    int iMem;                /* Memory location that acts as accumulator */
+  } *aCol;
+  int nColumn;            /* Number of used entries in aCol[] */
+  int nColumnAlloc;       /* Number of slots allocated for aCol[] */
+  int nAccumulator;       /* Number of columns that show through to the output.
+                          ** Additional columns are used only as parameters to
+                          ** aggregate functions */
+  struct AggInfo_func {   /* For each aggregate function */
+    Expr *pExpr;             /* Expression encoding the function */
+    FuncDef *pFunc;          /* The aggregate function implementation */
+    int iMem;                /* Memory location that acts as accumulator */
+  } *aFunc;
+  int nFunc;              /* Number of entries in aFunc[] */
+  int nFuncAlloc;         /* Number of slots allocated for aFunc[] */
+};
+
 /*
 ** Each node of an expression in the parse tree is an instance
 ** of this structure.
@@ -847,9 +888,8 @@ struct Expr {
   Token span;            /* Complete text of the expression */
   int iTable, iColumn;   /* When op==TK_COLUMN, then this expr node means the
                          ** iColumn-th field of the iTable-th table. */
-  int iAgg;              /* When op==TK_COLUMN and pParse->fillAgg==FALSE, pull
-                         ** result from the iAgg-th element of the aggregator */
-  int iAggCtx;           /* The value to pass as P1 of OP_AggGet. */
+  AggInfo *pAggInfo;     /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
+  int iAgg;              /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
   Select *pSelect;       /* When the expression is a sub-select.  Also the
                          ** right side of "<expr> IN (<select>)" */
   Table *pTab;           /* Table for OP_Column expressions. */
@@ -912,12 +952,12 @@ struct ExprList {
 ** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
 */
 struct IdList {
-  int nId;         /* Number of identifiers on the list */
-  int nAlloc;      /* Number of entries allocated for a[] below */
   struct IdList_item {
     char *zName;      /* Name of the identifier */
     int idx;          /* Index in some Table.aCol[] of a column named zName */
   } *a;
+  int nId;         /* Number of identifiers on the list */
+  int nAlloc;      /* Number of entries allocated for a[] below */
 };
 
 /*
@@ -1031,8 +1071,9 @@ struct NameContext {
   int nRef;            /* Number of names resolved by this context */
   int nErr;            /* Number of errors encountered while resolving names */
   u8 allowAgg;         /* Aggregate functions allowed here */
-  u8 hasAgg;
+  u8 hasAgg;           /* True if aggregates are seen */
   int nDepth;          /* Depth of subquery recursion. 1 for no recursion */
+  AggInfo *pAggInfo;   /* Information about aggregates at this level */
   NameContext *pNext;  /* Next outer name context.  NULL for outermost */
 };
 
@@ -1079,42 +1120,21 @@ struct Select {
 /*
 ** The results of a select can be distributed in several ways.
 */
-#define SRT_Callback     1  /* Invoke a callback with each row of result */
-#define SRT_Mem          2  /* Store result in a memory cell */
-#define SRT_Set          3  /* Store result as unique keys in a table */
-#define SRT_Union        5  /* Store result as keys in a table */
-#define SRT_Except       6  /* Remove result from a UNION table */
-#define SRT_Table        7  /* Store result as data with a unique key */
+#define SRT_Union        1  /* Store result as keys in an index */
+#define SRT_Except       2  /* Remove result from a UNION index */
+#define SRT_Discard      3  /* Do not save the results anywhere */
+
+/* The ORDER BY clause is ignored for all of the above */
+#define IgnorableOrderby(X) (X<=SRT_Discard)
+
+#define SRT_Callback     4  /* Invoke a callback with each row of result */
+#define SRT_Mem          5  /* Store result in a memory cell */
+#define SRT_Set          6  /* Store non-null results as keys in an index */
+#define SRT_Table        7  /* Store result as data and add automatic rowid */
 #define SRT_TempTable    8  /* Store result in a trasient table */
-#define SRT_Discard      9  /* Do not save the results anywhere */
-#define SRT_Sorter      10  /* Store results in the sorter */
-#define SRT_Subroutine  11  /* Call a subroutine to handle results */
-#define SRT_Exists      12  /* Put 0 or 1 in a memory cell */
-
-/*
-** When a SELECT uses aggregate functions (like "count(*)" or "avg(f1)")
-** we have to do some additional analysis of expressions.  An instance
-** of the following structure holds information about a single subexpression
-** somewhere in the SELECT statement.  An array of these structures holds
-** all the information we need to generate code for aggregate
-** expressions.
-**
-** Note that when analyzing a SELECT containing aggregates, both
-** non-aggregate field variables and aggregate functions are stored
-** in the AggExpr array of the Parser structure.
-**
-** The pExpr field points to an expression that is part of either the
-** field list, the GROUP BY clause, the HAVING clause or the ORDER BY
-** clause.  The expression will be freed when those clauses are cleaned
-** up.  Do not try to delete the expression attached to AggExpr.pExpr.
-**
-** If AggExpr.pExpr==0, that means the expression is "count(*)".
-*/
-struct AggExpr {
-  int isAgg;        /* if TRUE contains an aggregate function */
-  Expr *pExpr;      /* The expression */
-  FuncDef *pFunc;   /* Information about the aggregate function */
-};
+#define SRT_Sorter       9  /* Store results in the sorter NOT USED */
+#define SRT_Subroutine  10  /* Call a subroutine to handle results */
+#define SRT_Exists      11  /* Put 0 or 1 in a memory cell */
 
 /*
 ** An SQL parser context.  A copy of this structure is passed through
@@ -1135,7 +1155,6 @@ struct Parse {
   u8 nameClash;        /* A permanent table name clashes with temp table name */
   u8 checkSchema;      /* Causes schema cookie check after an error */
   u8 nested;           /* Number of nested calls to the parser/code generator */
-  u8 fillAgg;          /* If true, ignore the Expr.iAgg field. Normally false */
   int nErr;            /* Number of errors seen */
   int nTab;            /* Number of previously allocated VDBE cursors */
   int nMem;            /* Number of memory cells used so far */
@@ -1162,9 +1181,6 @@ struct Parse {
   Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
   TriggerStack *trigStack;  /* Trigger actions being coded */
   const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
-  int nAgg;            /* Number of aggregate expressions */
-  AggExpr *aAgg;       /* An array of aggregate expressions */
-  int nMaxDepth;       /* Maximum depth of subquery recursion */
 };
 
 /*
@@ -1421,6 +1437,7 @@ void sqlite3EndTable(Parse*,Token*,Token*,Select*);
 void sqlite3DropTable(Parse*, SrcList*, int);
 void sqlite3DeleteTable(sqlite3*, Table*);
 void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
+int sqlite3ArrayAllocate(void**,int,int);
 IdList *sqlite3IdListAppend(IdList*, Token*);
 int sqlite3IdListIndex(IdList*,const char*);
 SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
index 2de08559470ee24d6356d1475885c097f0666d77..c4ad4d511d8f0f3c638f9c6f73a918b04de8a984 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.481 2005/09/06 20:36:49 drh Exp $
+** $Id: vdbe.c,v 1.482 2005/09/07 21:22:47 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -160,38 +160,6 @@ static void _storeTypeInfo(Mem *pMem){
   }
 }
 
-/*
-** Insert a new aggregate element and make it the element that
-** has focus.
-**
-** Return 0 on success and 1 if memory is exhausted.
-*/
-static int AggInsert(Agg *p, char *zKey, int nKey){
-  AggElem *pElem;
-  int i;
-  int rc;
-  pElem = sqliteMalloc( sizeof(AggElem) + nKey +
-                        (p->nMem-1)*sizeof(pElem->aMem[0]) );
-  if( pElem==0 ) return SQLITE_NOMEM;
-  pElem->zKey = (char*)&pElem->aMem[p->nMem];
-  memcpy(pElem->zKey, zKey, nKey);
-  pElem->nKey = nKey;
-
-  if( p->pCsr ){
-    rc = sqlite3BtreeInsert(p->pCsr, zKey, nKey, &pElem, sizeof(AggElem*));
-    if( rc!=SQLITE_OK ){
-      sqliteFree(pElem);
-      return rc;
-    }
-  }
-
-  for(i=0; i<p->nMem; i++){
-    pElem->aMem[i].flags = MEM_Null;
-  }
-  p->pCurrent = pElem;
-  return 0;
-}
-
 /*
 ** Pop the stack N times.
 */
@@ -1131,26 +1099,25 @@ case OP_CollSeq: {             /* no-push */
 /* Opcode: Function P1 P2 P3
 **
 ** Invoke a user function (P3 is a pointer to a Function structure that
-** defines the function) with P1 arguments taken from the stack.  Pop all
+** defines the function) with P2 arguments taken from the stack.  Pop all
 ** arguments from the stack and push back the result.
 **
-** P2 is a 32-bit bitmask indicating whether or not each argument to the 
+** P1 is a 32-bit bitmask indicating whether or not each argument to the 
 ** function was determined to be constant at compile time. If the first
-** argument was constant then bit 0 of P2 is set. This is used to determine
+** argument was constant then bit 0 of P1 is set. This is used to determine
 ** whether meta data associated with a user function argument using the
 ** sqlite3_set_auxdata() API may be safely retained until the next
 ** invocation of this opcode.
 **
-** See also: AggFunc
+** See also: AggStep and AggFinal
 */
 case OP_Function: {
   int i;
   Mem *pArg;
   sqlite3_context ctx;
   sqlite3_value **apVal;
-  int n = pOp->p1;
+  int n = pOp->p2;
 
-  n = pOp->p1;
   apVal = p->apArg;
   assert( apVal || n==0 );
 
@@ -1189,7 +1156,7 @@ case OP_Function: {
   ** immediately call the destructor for any non-static values.
   */
   if( ctx.pVdbeFunc ){
-    sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p2);
+    sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p1);
     pOp->p3 = (char *)ctx.pVdbeFunc;
     pOp->p3type = P3_VDBEFUNC;
   }
@@ -4028,31 +3995,6 @@ case OP_FifoRead: {
   break;
 }
 
-#ifndef SQLITE_OMIT_SUBQUERY
-/* Opcode: AggContextPush * * * 
-**
-** Save the state of the current aggregator. It is restored an 
-** AggContextPop opcode.
-** 
-*/
-case OP_AggContextPush: {        /* no-push */
-  p->pAgg++;
-  assert( p->pAgg<&p->apAgg[p->nAgg] );
-  break;
-}
-
-/* Opcode: AggContextPop * * *
-**
-** Restore the aggregator to the state it was in when AggContextPush
-** was last called. Any data in the current aggregator is deleted.
-*/
-case OP_AggContextPop: {        /* no-push */
-  p->pAgg--;
-  assert( p->pAgg>=p->apAgg );
-  break;
-}
-#endif
-
 #ifndef SQLITE_OMIT_TRIGGER
 /* Opcode: ContextPush * * * 
 **
@@ -4199,64 +4141,16 @@ case OP_IfMemPos: {        /* no-push */
   break;
 }
 
-/* Opcode: AggReset P1 P2 P3
-**
-** Reset the current aggregator context so that it no longer contains any 
-** data. Future aggregator elements will contain P2 values each and be sorted
-** using the KeyInfo structure pointed to by P3.
-**
-** If P1 is non-zero, then only a single aggregator row is available (i.e.
-** there is no GROUP BY expression). In this case it is illegal to invoke
-** OP_AggFocus.
-*/
-case OP_AggReset: {        /* no-push */
-  assert( !pOp->p3 || pOp->p3type==P3_KEYINFO );
-  if( pOp->p1 ){
-    rc = sqlite3VdbeAggReset(0, p->pAgg, (KeyInfo *)pOp->p3);
-    p->pAgg->nMem = pOp->p2;    /* Agg.nMem is used by AggInsert() */
-    rc = AggInsert(p->pAgg, 0, 0);
-  }else{
-    rc = sqlite3VdbeAggReset(db, p->pAgg, (KeyInfo *)pOp->p3);
-    p->pAgg->nMem = pOp->p2;
-  }
-  if( rc!=SQLITE_OK ){
-    goto abort_due_to_error;
-  }
-  p->pAgg->apFunc = sqliteMalloc( p->pAgg->nMem*sizeof(p->pAgg->apFunc[0]) );
-  if( p->pAgg->apFunc==0 ) goto no_mem;
-  break;
-}
-
-/* Opcode: AggInit P1 P2 P3
-**
-** Initialize the function parameters for an aggregate function.
-** The aggregate will operate out of aggregate column P2.
-** P3 is a pointer to the FuncDef structure for the function.
-**
-** The P1 argument is not used by this opcode. However if the SSE
-** extension is compiled in, P1 is set to the number of arguments that
-** will be passed to the aggregate function, if any. This is used
-** by SSE to select the correct function when (de)serializing statements.
-*/
-case OP_AggInit: {        /* no-push */
-  int i = pOp->p2;
-  assert( i>=0 && i<p->pAgg->nMem );
-  p->pAgg->apFunc[i] = (FuncDef*)pOp->p3;
-  break;
-}
-
-/* Opcode: AggFunc * P2 P3
+/* Opcode: AggStep P1 P2 P3
 **
 ** Execute the step function for an aggregate.  The
 ** function has P2 arguments.  P3 is a pointer to the FuncDef
-** structure that specifies the function.
+** structure that specifies the function.  Use memory location
+** P1 as the accumulator.
 **
-** The top of the stack must be an integer which is the index of
-** the aggregate column that corresponds to this aggregate function.
-** Ideally, this index would be another parameter, but there are
-** no free parameters left.  The integer is popped from the stack.
+** The P2 arguments are popped from the stack.
 */
-case OP_AggFunc: {        /* no-push */
+case OP_AggStep: {        /* no-push */
   int n = pOp->p2;
   int i;
   Mem *pMem, *pRec;
@@ -4264,21 +4158,17 @@ case OP_AggFunc: {        /* no-push */
   sqlite3_value **apVal;
 
   assert( n>=0 );
-  assert( pTos->flags==MEM_Int );
-  pRec = &pTos[-n];
+  pRec = &pTos[1-n];
   assert( pRec>=p->aStack );
-
   apVal = p->apArg;
   assert( apVal || n==0 );
-
   for(i=0; i<n; i++, pRec++){
     apVal[i] = pRec;
     storeTypeInfo(pRec, db->enc);
   }
-  i = pTos->i;
-  assert( i>=0 && i<p->pAgg->nMem );
   ctx.pFunc = (FuncDef*)pOp->p3;
-  ctx.pMem = pMem = &p->pAgg->pCurrent->aMem[i];
+  assert( pOp->p1>=0 && pOp->p1<p->nMem );
+  ctx.pMem = pMem = &p->aMem[pOp->p1];
   pMem->n++;
   ctx.isError = 0;
   ctx.pColl = 0;
@@ -4289,165 +4179,26 @@ case OP_AggFunc: {        /* no-push */
     ctx.pColl = (CollSeq *)pOp[-1].p3;
   }
   (ctx.pFunc->xStep)(&ctx, n, apVal);
-  popStack(&pTos, n+1);
+  popStack(&pTos, n);
   if( ctx.isError ){
     rc = SQLITE_ERROR;
   }
   break;
 }
 
-/* Opcode: AggFocus * P2 *
-**
-** Pop the top of the stack and use that as an aggregator key.  If
-** an aggregator with that same key already exists, then make the
-** aggregator the current aggregator and jump to P2.  If no aggregator
-** with the given key exists, create one and make it current but
-** do not jump.
-**
-** The order of aggregator opcodes is important.  The order is:
-** AggReset AggFocus AggNext.  In other words, you must execute
-** AggReset first, then zero or more AggFocus operations, then
-** zero or more AggNext operations.  You must not execute an AggFocus
-** in between an AggNext and an AggReset.
-*/
-case OP_AggFocus: {        /* no-push */
-  char *zKey;
-  int nKey;
-  int res;
-  assert( pTos>=p->aStack );
-  Stringify(pTos, db->enc);
-  zKey = pTos->z;
-  nKey = pTos->n;
-  assert( p->pAgg->pBtree );
-  assert( p->pAgg->pCsr );
-  rc = sqlite3BtreeMoveto(p->pAgg->pCsr, zKey, nKey, &res);
-  if( rc!=SQLITE_OK ){
-    goto abort_due_to_error;
-  }
-  if( res==0 ){
-    rc = sqlite3BtreeData(p->pAgg->pCsr, 0, sizeof(AggElem*),
-        (char *)&p->pAgg->pCurrent);
-    pc = pOp->p2 - 1;
-  }else{
-    rc = AggInsert(p->pAgg, zKey, nKey);
-  }
-  if( rc!=SQLITE_OK ){
-    goto abort_due_to_error;
-  }
-  Release(pTos);
-  pTos--;
-  break; 
-}
-
-/* Opcode: AggSet * P2 *
+/* Opcode: AggFinal P1 * *
 **
-** Move the top of the stack into the P2-th field of the current
-** aggregate.  String values are duplicated into new memory.
+** Execute the finalizer function for an aggregate.  P1 is
+** the memory location that is the accumulator for the aggregate.
 */
-case OP_AggSet: {        /* no-push */
-  AggElem *pFocus;
-  int i = pOp->p2;
-  pFocus = p->pAgg->pCurrent;
-  assert( pTos>=p->aStack );
-  if( pFocus==0 ) goto no_mem;
-  assert( i>=0 && i<p->pAgg->nMem );
-  rc = sqlite3VdbeMemMove(&pFocus->aMem[i], pTos);
-  pTos--;
-  break;
-}
-
-/* Opcode: AggGet P1 P2 *
-**
-** Push a new entry onto the stack which is a copy of the P2-th field
-** of the current aggregate.  Strings are not duplicated so
-** string values will be ephemeral.
-**
-** If P1 is zero, then the value is pulled out of the current aggregate
-** in the current aggregate context. If P1 is greater than zero, then
-** the value is taken from the P1th outer aggregate context. (i.e. if
-** P1==1 then read from the aggregate context that will be restored
-** by the next OP_AggContextPop opcode).
-*/
-case OP_AggGet: {
-  AggElem *pFocus;
-  int i = pOp->p2;
-  Agg *pAgg = &p->pAgg[-pOp->p1];
-  assert( pAgg>=p->apAgg );
-  pFocus = pAgg->pCurrent;
-  if( pFocus==0 ){
-    int res;
-    if( sqlite3_malloc_failed ) goto no_mem;
-    rc = sqlite3BtreeFirst(pAgg->pCsr, &res);
-    if( rc!=SQLITE_OK ){
-      return rc;
-    }
-    if( res!=0 ){
-      rc = AggInsert(pAgg, "", 1);
-      pFocus = pAgg->pCurrent;
-    }else{
-      rc = sqlite3BtreeData(pAgg->pCsr, 0, 4, (char *)&pFocus);
-    }
-  }
-  assert( i>=0 && i<pAgg->nMem );
-  pTos++;
-  sqlite3VdbeMemShallowCopy(pTos, &pFocus->aMem[i], MEM_Ephem);
-  if( pTos->flags&MEM_Str ){
-    sqlite3VdbeChangeEncoding(pTos, db->enc);
-  }
+case OP_AggFinal: {        /* no-push */
+  Mem *pMem;
+  assert( pOp->p1>=0 && pOp->p1<p->nMem );
+  pMem = &p->aMem[pOp->p1];
+  sqlite3VdbeMemFinalize(pMem);
   break;
 }
 
-/* Opcode: AggNext * P2 *
-**
-** Make the next aggregate value the current aggregate.  The prior
-** aggregate is deleted.  If all aggregate values have been consumed,
-** jump to P2.
-**
-** The order of aggregator opcodes is important.  The order is:
-** AggReset AggFocus AggNext.  In other words, you must execute
-** AggReset first, then zero or more AggFocus operations, then
-** zero or more AggNext operations.  You must not execute an AggFocus
-** in between an AggNext and an AggReset.
-*/
-case OP_AggNext: {        /* no-push */
-  int res;
-  assert( rc==SQLITE_OK );
-  CHECK_FOR_INTERRUPT;
-  if( p->pAgg->searching==0 ){
-    p->pAgg->searching = 1;
-    if( p->pAgg->pCsr ){
-      rc = sqlite3BtreeFirst(p->pAgg->pCsr, &res);
-    }else{
-      res = 0;
-    }
-  }else{
-    if( p->pAgg->pCsr ){
-      rc = sqlite3BtreeNext(p->pAgg->pCsr, &res);
-    }else{
-      res = 1;
-    }
-  }
-  if( rc!=SQLITE_OK ) goto abort_due_to_error;
-  if( res!=0 ){
-    pc = pOp->p2 - 1;
-  }else{
-    int i;
-    Mem *aMem;
-
-    if( p->pAgg->pCsr ){
-      rc = sqlite3BtreeData(p->pAgg->pCsr, 0, sizeof(AggElem*),
-          (char *)&p->pAgg->pCurrent);
-      if( rc!=SQLITE_OK ) goto abort_due_to_error;
-    }
-    aMem = p->pAgg->pCurrent->aMem;
-    for(i=0; i<p->pAgg->nMem; i++){
-      FuncDef *pFunc = p->pAgg->apFunc[i];
-      Mem *pMem = &aMem[i];
-      sqlite3VdbeMemFinalize(pMem, pFunc);
-    }
-  }
-  break;
-}
 
 /* Opcode: Vacuum * * *
 **
index 0da0299550fcc2b3dafb1620e633a6fd79dd352e..6e3917432a64fbedc38de5ac5dd4111bacc175e3 100644 (file)
@@ -15,7 +15,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.97 2005/08/19 01:07:16 drh Exp $
+** $Id: vdbe.h,v 1.98 2005/09/07 21:22:47 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -108,7 +108,7 @@ void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
 VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
 int sqlite3VdbeMakeLabel(Vdbe*);
 void sqlite3VdbeDelete(Vdbe*);
-void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int);
+void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int);
 int sqlite3VdbeFinalize(Vdbe*);
 void sqlite3VdbeResolveLabel(Vdbe*, int);
 int sqlite3VdbeCurrentAddr(Vdbe*);
index 0ab61ec18e45a48173e28084941d81c324a3ec97..77951b2cd560c745ae9f970853f2ba50f2a82719 100644 (file)
@@ -201,30 +201,6 @@ struct sqlite3_context {
   CollSeq *pColl;       /* Collating sequence */
 };
 
-/*
-** An Agg structure describes an Aggregator.  Each Agg consists of
-** zero or more Aggregator elements (AggElem).  Each AggElem contains
-** a key and one or more values.  The values are used in processing
-** aggregate functions in a SELECT.  The key is used to implement
-** the GROUP BY clause of a select.
-*/
-typedef struct Agg Agg;
-typedef struct AggElem AggElem;
-struct Agg {
-  int nMem;            /* Number of values stored in each AggElem */
-  AggElem *pCurrent;   /* The AggElem currently in focus */
-  FuncDef **apFunc;    /* Information about aggregate functions */
-  Btree *pBtree;       /* The tmp. btree used to group elements, if required. */
-  BtCursor *pCsr;      /* Read/write cursor to the table in pBtree */
-  int nTab;            /* Root page of the table in pBtree */
-  u8 searching;        /* True between the first AggNext and AggReset */
-};
-struct AggElem {
-  char *zKey;          /* The key to this AggElem */
-  int nKey;            /* Number of bytes in the key, including '\0' at end */
-  Mem aMem[1];         /* The values for this AggElem */
-};
-
 /*
 ** A Set structure is used for quick testing to see if a value
 ** is part of a small set.  Sets are used to implement code like
@@ -309,9 +285,6 @@ struct Vdbe {
   int magic;              /* Magic number for sanity checking */
   int nMem;               /* Number of memory locations currently allocated */
   Mem *aMem;              /* The memory locations */
-  int nAgg;               /* Number of elements in apAgg */
-  Agg *apAgg;             /* Array of aggregate contexts */
-  Agg *pAgg;              /* Current aggregate context */
   int nCallback;          /* Number of callbacks invoked so far */
   Fifo sFifo;             /* A list of ROWIDs */
   int contextStackTop;    /* Index of top element in the context stack */
@@ -349,7 +322,6 @@ struct Vdbe {
 ** Function prototypes
 */
 void sqlite3VdbeFreeCursor(Cursor*);
-int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *);
 void sqliteVdbePopStack(Vdbe*,int);
 int sqlite3VdbeCursorMoveto(Cursor*);
 #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
@@ -391,7 +363,7 @@ double sqlite3VdbeRealValue(Mem*);
 int sqlite3VdbeMemRealify(Mem*);
 int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
 void sqlite3VdbeMemRelease(Mem *p);
-void sqlite3VdbeMemFinalize(Mem*, FuncDef*);
+void sqlite3VdbeMemFinalize(Mem*);
 #ifndef NDEBUG
 void sqlite3VdbeMemSanity(Mem*, u8);
 int sqlite3VdbeOpcodeNoPush(u8);
index 0109f0bc2203e14e20f0d840b009442a869c9547..a4d5b0524ffc4aa383b981f4fff24a1038047b1d 100644 (file)
@@ -215,8 +215,8 @@ int sqlite3VdbeOpcodeNoPush(u8 op){
 **
 ** This routine is called once after all opcodes have been inserted.
 **
-** Variable *pMaxFuncArgs is set to the maximum value of any P1 argument 
-** to an OP_Function or P2 to an OP_AggFunc opcode. This is used by 
+** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument 
+** to an OP_Function or OP_AggStep opcode. This is used by 
 ** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
 **
 ** The integer *pMaxStack is set to the maximum number of vdbe stack
@@ -239,12 +239,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){
   for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
     u8 opcode = pOp->opcode;
 
-    /* Todo: Maybe OP_AggFunc should change to use P1 in the same
-     * way as OP_Function. 
-     */
-    if( opcode==OP_Function ){
-      if( pOp->p1>nMaxArgs ) nMaxArgs = pOp->p1;
-    }else if( opcode==OP_AggFunc ){
+    if( opcode==OP_Function || opcode==OP_AggStep ){
       if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
     }else if( opcode==OP_Halt ){
       if( pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort ){
@@ -663,7 +658,6 @@ void sqlite3VdbeMakeReady(
   int nVar,                      /* Number of '?' see in the SQL statement */
   int nMem,                      /* Number of memory cells to allocate */
   int nCursor,                   /* Number of cursors to allocate */
-  int nAgg,                      /* Number of aggregate contexts required */
   int isExplain                  /* True if the EXPLAIN keywords is present */
 ){
   int n;
@@ -706,7 +700,6 @@ void sqlite3VdbeMakeReady(
       + nVar*sizeof(char*)             /* azVar */
       + nMem*sizeof(Mem)               /* aMem */
       + nCursor*sizeof(Cursor*)        /* apCsr */
-      + nAgg*sizeof(Agg)               /* Aggregate contexts */
     );
     if( !sqlite3_malloc_failed ){
       p->aMem = &p->aStack[nStack];
@@ -717,17 +710,12 @@ void sqlite3VdbeMakeReady(
       p->apArg = (Mem**)&p->aVar[nVar];
       p->azVar = (char**)&p->apArg[nArg];
       p->apCsr = (Cursor**)&p->azVar[nVar];
-      if( nAgg>0 ){
-        p->nAgg = nAgg;
-        p->apAgg = (Agg*)&p->apCsr[nCursor];
-      }
       p->nCursor = nCursor;
       for(n=0; n<nVar; n++){
         p->aVar[n].flags = MEM_Null;
       }
     }
   }
-  p->pAgg = p->apAgg;
   for(n=0; n<p->nMem; n++){
     p->aMem[n].flags = MEM_Null;
   }
@@ -768,129 +756,6 @@ void sqlite3VdbeMakeReady(
 #endif
 }
 
-/*
-** Free all resources allociated with AggElem pElem, an element of
-** aggregate pAgg.
-*/
-static void freeAggElem(AggElem *pElem, Agg *pAgg){
-  int i;
-  for(i=0; i<pAgg->nMem; i++){
-    Mem *pMem = &pElem->aMem[i];
-    if( pAgg->apFunc && pAgg->apFunc[i] && (pMem->flags & MEM_Agg)!=0 ){
-      sqlite3VdbeMemFinalize(pMem, pAgg->apFunc[i]);
-    }
-    sqlite3VdbeMemRelease(pMem);
-
-  }
-  sqliteFree(pElem);
-}
-
-/*
-** Reset an Agg structure.  Delete all its contents.
-**
-** For installable aggregate functions, if the step function has been
-** called, make sure the finalizer function has also been called.  The
-** finalizer might need to free memory that was allocated as part of its
-** private context.  If the finalizer has not been called yet, call it
-** now.
-**
-** If db is NULL, then this is being called from sqliteVdbeReset(). In
-** this case clean up all references to the temp-table used for
-** aggregates (if it was ever opened).
-**
-** If db is not NULL, then this is being called from with an OP_AggReset
-** opcode. Open the temp-table, if it has not already been opened and
-** delete the contents of the table used for aggregate information, ready
-** for the next round of aggregate processing.
-*/
-int sqlite3VdbeAggReset(sqlite3 *db, Agg *pAgg, KeyInfo *pKeyInfo){
-  int rc = 0;
-  BtCursor *pCsr;
-
-  if( !pAgg ) return SQLITE_OK;
-  pCsr = pAgg->pCsr;
-  assert( (pCsr && pAgg->nTab>0) || (!pCsr && pAgg->nTab==0)
-         || sqlite3_malloc_failed );
-
-  /* If pCsr is not NULL, then the table used for aggregate information
-  ** is open. Loop through it and free the AggElem* structure pointed at
-  ** by each entry. If the finalizer has not been called for an AggElem,
-  ** do that too. Finally, clear the btree table itself.
-  */
-  if( pCsr ){
-    int res;
-    assert( pAgg->pBtree );
-    assert( pAgg->nTab>0 );
-
-    rc=sqlite3BtreeFirst(pCsr, &res);
-    while( res==0 && rc==SQLITE_OK ){
-      AggElem *pElem;
-      rc = sqlite3BtreeData(pCsr, 0, sizeof(AggElem*), (char *)&pElem);
-      if( rc!=SQLITE_OK ){
-        return rc;
-      }
-      assert( pAgg->apFunc!=0 );
-      freeAggElem(pElem, pAgg);
-      rc=sqlite3BtreeNext(pCsr, &res);
-    }
-    if( rc!=SQLITE_OK ){
-      return rc;
-    }
-
-    sqlite3BtreeCloseCursor(pCsr);
-    sqlite3BtreeClearTable(pAgg->pBtree, pAgg->nTab);
-  }else{ 
-    /* The cursor may not be open because the aggregator was never used,
-    ** or it could be that it was used but there was no GROUP BY clause.
-    */
-    if( pAgg->pCurrent ){
-      freeAggElem(pAgg->pCurrent, pAgg);
-    }
-  }
-
-  /* If db is not NULL and we have not yet and we have not yet opened
-  ** the temporary btree then do so and create the table to store aggregate
-  ** information.
-  **
-  ** If db is NULL, then close the temporary btree if it is open.
-  */
-  if( db ){
-    if( !pAgg->pBtree ){
-      assert( pAgg->nTab==0 );
-#ifndef SQLITE_OMIT_MEMORYDB
-      rc = sqlite3BtreeFactory(db, ":memory:", 0, TEMP_PAGES, &pAgg->pBtree);
-#else
-      rc = sqlite3BtreeFactory(db, 0, 0, TEMP_PAGES, &pAgg->pBtree);
-#endif
-      if( rc!=SQLITE_OK ) return rc;
-      sqlite3BtreeBeginTrans(pAgg->pBtree, 1);
-      rc = sqlite3BtreeCreateTable(pAgg->pBtree, &pAgg->nTab, 0);
-      if( rc!=SQLITE_OK ) return rc;
-    }
-    assert( pAgg->nTab!=0 );
-
-    rc = sqlite3BtreeCursor(pAgg->pBtree, pAgg->nTab, 1,
-        sqlite3VdbeRecordCompare, pKeyInfo, &pAgg->pCsr);
-    if( rc!=SQLITE_OK ) return rc;
-  }else{
-    if( pAgg->pBtree ){
-      sqlite3BtreeClose(pAgg->pBtree);
-      pAgg->pBtree = 0;
-      pAgg->nTab = 0;
-    }
-    pAgg->pCsr = 0;
-  }
-
-  if( pAgg->apFunc ){ 
-    sqliteFree(pAgg->apFunc);
-    pAgg->apFunc = 0;
-  }
-  pAgg->pCurrent = 0;
-  pAgg->nMem = 0;
-  pAgg->searching = 0;
-  return SQLITE_OK;
-}
-
 /*
 ** Close a cursor and release all the resources that cursor happens
 ** to hold.
@@ -944,9 +809,6 @@ static void Cleanup(Vdbe *p){
     }
     sqliteFree(p->contextStack);
   }
-  for(i=0; i<p->nAgg; i++){
-    sqlite3VdbeAggReset(0, &p->apAgg[i], 0);
-  }
   p->contextStack = 0;
   p->contextStackDepth = 0;
   p->contextStackTop = 0;
index a6c850029f41ce78118be149d68e0e1f23002265..70a46637e9f3ee1d1b29c085ff3aaa688f3dd19d 100644 (file)
@@ -192,20 +192,23 @@ int sqlite3VdbeMemStringify(Mem *pMem, int enc){
 ** This routine calls the finalize method for that function.  The
 ** result of the aggregate is stored back into pMem.
 */
-void sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
-  if( pFunc && pFunc->xFinalize ){
-    sqlite3_context ctx;
-    ctx.s.flags = MEM_Null;
-    ctx.s.z = pMem->zShort;
-    ctx.pMem = pMem;
-    ctx.pFunc = pFunc;
-    pFunc->xFinalize(&ctx);
-    if( pMem->z && pMem->z!=pMem->zShort ){
-      sqliteFree( pMem->z );
-    }
-    *pMem = ctx.s;
-    if( pMem->flags & MEM_Short ){
-      pMem->z = pMem->zShort;
+void sqlite3VdbeMemFinalize(Mem *pMem){
+  if( pMem->flags & MEM_Agg ){
+    FuncDef *pFunc = *(FuncDef**)&pMem->i;
+    if( pFunc && pFunc->xFinalize ){
+      sqlite3_context ctx;
+      ctx.s.flags = MEM_Null;
+      ctx.s.z = pMem->zShort;
+      ctx.pMem = pMem;
+      ctx.pFunc = pFunc;
+      pFunc->xFinalize(&ctx);
+      if( pMem->z && pMem->z!=pMem->zShort ){
+        sqliteFree( pMem->z );
+      }
+      *pMem = ctx.s;
+      if( pMem->flags & MEM_Short ){
+        pMem->z = pMem->zShort;
+      }
     }
   }
 }
@@ -219,7 +222,7 @@ void sqlite3VdbeMemRelease(Mem *p){
   if( p->flags & (MEM_Dyn|MEM_Agg) ){
     if( p->xDel ){
       if( p->flags & MEM_Agg ){
-        sqlite3VdbeMemFinalize(p, (FuncDef*)&p->i);
+        sqlite3VdbeMemFinalize(p);
         assert( (p->flags & MEM_Agg)==0 );
         sqlite3VdbeMemRelease(p);
       }else{
index 91fafb1acee1998e6b33676a37c6a0c8827016cb..31bd68480165e4450ff3c375bc8ea8c1ea875b80 100644 (file)
@@ -13,7 +13,7 @@
 # This file implements tests for miscellanous features that were
 # left out of other test files.
 #
-# $Id: misc4.test,v 1.16 2005/03/29 03:11:00 danielk1977 Exp $
+# $Id: misc4.test,v 1.17 2005/09/07 21:22:47 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -96,7 +96,7 @@ do_test misc4-3.1 {
     INSERT INTO Table2 VALUES(1, 'z');
     INSERT INTO Table2 VALUES (1, 'a');
     SELECT ID, Value FROM Table1
-       UNION SELECT ID, max(Value) FROM Table2 GROUP BY 1,2
+       UNION SELECT ID, max(Value) FROM Table2 GROUP BY 1
     ORDER BY 1, 2;
   }
 } {{} {} 1 x 1 z}
@@ -177,4 +177,3 @@ do_test misc4-6.2 {
 } {1}
 
 finish_test
-