]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
GROUP BY and HAVING installed (CVS 58)
authordrh <drh@noemail.net>
Tue, 6 Jun 2000 17:27:05 +0000 (17:27 +0000)
committerdrh <drh@noemail.net>
Tue, 6 Jun 2000 17:27:05 +0000 (17:27 +0000)
FossilOrigin-Name: db88a0c2d4b5c5cd05e0172f061fc33763fe3829

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

index 5d19b5ec922a0cb5a0bd4caa414f18eab2a6c3f1..f47dcae0fbb439e797c0cf93c61ce58195c0be3f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C added\sIN\sand\sBETWEEN\soperators\s(CVS\s57)
-D 2000-06-06T13:54:15
+C GROUP\sBY\sand\sHAVING\sinstalled\s(CVS\s58)
+D 2000-06-06T17:27:05
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@@ -10,19 +10,19 @@ F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20
 F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63
 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
 F src/delete.c 8c733bb82a1b84126116d03dcdccf433c0856f5d
-F src/expr.c 1bedf5f426ee1e1609ef1758985b7ce0581987b8
+F src/expr.c d350fe393e1753aaa733a5d21f0830a23e547400
 F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf
 F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6
-F src/parse.y 51ef63a49e73ced4ef3e81d7b1f9fd825d776837
-F src/select.c a1891cfce003a98a57374c01fa5875366c8d9be2
+F src/parse.y 210c888c052f3fde3def6ff1f402e4b4e8ba270d
+F src/select.c 77906ffaae962e49acc9a0c0c39cb710f06e56d8
 F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb
 F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
-F src/sqliteInt.h 821b435a18e1c9d0fddcd7bfeefcf5f3fe925c6e
+F src/sqliteInt.h 267f66d4a851e19adb0f9a3050a3acb62fc70cae
 F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
-F src/tokenize.c f190e16ebb82dd60497796022f1e56e2e0527975
+F src/tokenize.c 32f0579d4a7276b8e3bfd353cac034f67976aa82
 F src/update.c 18746f920f989b3d19d96c08263c92584823cd35
 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
-F src/vdbe.c 1ab61ada503b99d6b3224c9d40ed9bac855fe317
+F src/vdbe.c 7a4909b44a58a81e03dfc9376b926d5e8f773fcc
 F src/vdbe.h 49e0f9c6742860e224cd81d1278059f5d029dfb6
 F src/where.c c9b90e7672f4662a83ef9a27a193020d69fe034c
 F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
@@ -32,7 +32,7 @@ F test/expr.test 52be5592143a88479e0006dfd7e2023e43294636
 F test/in.test 17cd46a9ca0e5d4a804483e6fb496458494858e6
 F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
 F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf
-F test/select1.test a0b00df77e85adff75c338e487718c5d31f69e3a
+F test/select1.test 2311bddd40bca257c27a7d141ed2a359bbdbc906
 F test/select2.test 3cd3c0f9d67e98b1b54af5853679b4a111224410
 F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5
 F test/table.test 85d6f410d127ec508c6640f02d7c40d218414e81
@@ -49,7 +49,7 @@ F www/c_interface.tcl 8867d76ddd416d2fbd41e4cb3de8efa9cef105a5
 F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77
 F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44
 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
-P b52dd82fe32c38c999aef4f07d046d0428336965
-R d1b48bd641877701893f6074a4e397f8
+P 54d198189b58366e4e40139102bc6de94ac55e18
+R dcd329d5daa579a072c3c6f09022ae9f
 U drh
-Z 3b39b6691fb1f454562ec994b26db409
+Z 6732a1b7035c88b38d7888f45d2d7142
index 247d36057520599657cfb115d13dafe5c8460e7f..586e4fee4e18766710d135a21afe3d0f72d987f9 100644 (file)
@@ -1 +1 @@
-54d198189b58366e4e40139102bc6de94ac55e18
\ No newline at end of file
+db88a0c2d4b5c5cd05e0172f061fc33763fe3829
\ No newline at end of file
index ab07a54f633925eda27ec21e0d732fe47702ee30..2870a484d9bd6c998501adfca280dd36a977bbce 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** This file contains C code routines used for processing expressions
 **
-** $Id: expr.c,v 1.9 2000/06/06 13:54:15 drh Exp $
+** $Id: expr.c,v 1.10 2000/06/06 17:27:05 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -326,6 +326,7 @@ int sqliteFuncId(Token *pToken){
      { "min",    3, FN_Min   },
      { "max",    3, FN_Max   },
      { "sum",    3, FN_Sum   },
+     { "avg",    3, FN_Avg   },
   };
   int i;
   for(i=0; i<ArraySize(aFunc); i++){
@@ -349,7 +350,6 @@ int sqliteFuncId(Token *pToken){
 int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
   int nErr = 0;
   if( pExpr==0 ) return 0;
-  if( pIsAgg ) *pIsAgg = 0;
   switch( pExpr->op ){
     case TK_FUNCTION: {
       int id = sqliteFuncId(&pExpr->token);
@@ -359,6 +359,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
       int too_few_args = 0;
       int is_agg = 0;
       int i;
+      pExpr->iField = id;
       switch( id ){
         case FN_Unknown: { 
           no_such_func = 1;
@@ -376,6 +377,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
           is_agg = n==1;
           break;
         }
+        case FN_Avg:
         case FN_Sum: {
           no_such_func = !allowAgg;
           too_many_args = n>1;
@@ -401,6 +403,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
         pParse->nErr++;
         nErr++;
       }
+      if( is_agg ) pExpr->op = TK_AGG_FUNCTION;
       if( is_agg && pIsAgg ) *pIsAgg = 1;
       for(i=0; nErr==0 && i<n; i++){
         nErr = sqliteExprCheck(pParse, pExpr->pList->a[i].pExpr, 0, 0);
@@ -408,16 +411,17 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
     }
     default: {
       if( pExpr->pLeft ){
-        nErr = sqliteExprCheck(pParse, pExpr->pLeft, 0, 0);
+        nErr = sqliteExprCheck(pParse, pExpr->pLeft, allowAgg, pIsAgg);
       }
       if( nErr==0 && pExpr->pRight ){
-        nErr = sqliteExprCheck(pParse, pExpr->pRight, 0, 0);
+        nErr = sqliteExprCheck(pParse, pExpr->pRight, allowAgg, pIsAgg);
       }
       if( nErr==0 && pExpr->pList ){
         int n = pExpr->pList->nExpr;
         int i;
         for(i=0; nErr==0 && i<n; i++){
-          nErr = sqliteExprCheck(pParse, pExpr->pList->a[i].pExpr, 0, 0);
+          Expr *pE2 = pExpr->pList->a[i].pExpr;
+          nErr = sqliteExprCheck(pParse, pE2, allowAgg, pIsAgg);
         }
       }
       break;
@@ -456,7 +460,11 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
   }
   switch( pExpr->op ){
     case TK_FIELD: {
-      sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
+      if( pParse->useAgg ){
+        sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg, 0, 0);
+      }else{
+        sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
+      }
       break;
     }
     case TK_INTEGER: {
@@ -523,8 +531,17 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       sqliteVdbeAddOp(v, OP_AddImm, -1, 0, 0, 0);
       break;
     }
+    case TK_AGG_FUNCTION: {
+      sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg, 0, 0);
+      if( pExpr->iField==FN_Avg ){
+        assert( pParse->iAggCount>=0 && pParse->iAggCount<pParse->nAgg );
+        sqliteVdbeAddOp(v, OP_AggGet, 0, pParse->iAggCount, 0, 0);
+        sqliteVdbeAddOp(v, OP_Divide, 0, 0, 0, 0);
+      }
+      break;
+    }
     case TK_FUNCTION: {
-      int id = sqliteFuncId(&pExpr->token);
+      int id = pExpr->iField;
       int op;
       int i;
       ExprList *pList = pExpr->pList;
@@ -744,3 +761,142 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
     }
   }
 }
+
+/*
+** Do a deep comparison of two expression trees.  Return TRUE (non-zero)
+** if they are identical and return FALSE if they differ in any way.
+*/
+static int exprDeepCompare(Expr *pA, Expr *pB){
+  int i;
+  if( pA==0 ){
+    return pB==0;
+  }else if( pB==0 ){
+    return 0;
+  }
+  if( pA->op!=pB->op ) return 0;
+  if( !exprDeepCompare(pA->pLeft, pB->pLeft) ) return 0;
+  if( !exprDeepCompare(pA->pRight, pB->pRight) ) return 0;
+  if( pA->pList ){
+    if( pB->pList==0 ) return 0;
+    if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
+    for(i=0; i<pA->pList->nExpr; i++){
+      if( !exprDeepCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
+        return 0;
+      }
+    }
+  }else if( pB->pList ){
+    return 0;
+  }
+  if( pA->pSelect || pB->pSelect ) return 0;
+  if( pA->token.z ){
+    if( pB->token.z==0 ) return 0;
+    if( pB->token.n!=pA->token.n ) return 0;
+    if( sqliteStrNICmp(pA->token.z, pB->token.z, pA->token.n)!=0 ) return 0;
+  }
+  return 1;
+}
+
+/*
+** Add a new element to the pParse->aAgg[] array and return its index.
+*/
+static int appendAggInfo(Parse *pParse){
+  if( (pParse->nAgg & 0x7)==0 ){
+    int amt = pParse->nAgg + 8;
+    pParse->aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0]));
+    if( pParse->aAgg==0 ){
+      sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
+      pParse->nErr++;
+      return -1;
+    }
+  }
+  memset(&pParse->aAgg[pParse->nAgg], 0, sizeof(pParse->aAgg[0]));
+  return pParse->nAgg++;
+}
+
+/*
+** Analyze the given expression looking for aggregate functions and
+** for variables that need to be added to the pParse->aAgg[] array.
+** Make additional entries to the pParse->aAgg[] array as necessary.
+**
+** This routine should only be called after the expression has been
+** analyzed by sqliteExprResolveIds() and sqliteExprCheck().
+**
+** If errors are seen, leave an error message in zErrMsg and return
+** the number of errors.
+*/
+int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
+  int i;
+  AggExpr *aAgg;
+  int nErr = 0;
+
+  if( pExpr==0 ) return 0;
+  switch( pExpr->op ){
+    case TK_FIELD: {
+      aAgg = pParse->aAgg;
+      for(i=0; i<pParse->nAgg; i++){
+        if( aAgg[i].isAgg ) continue;
+        if( aAgg[i].pExpr->iTable==pExpr->iTable
+         && aAgg[i].pExpr->iField==pExpr->iField ){
+          pExpr->iAgg = i;
+          break;
+        }
+      }
+      if( i>=pParse->nAgg ){
+        i = appendAggInfo(pParse);
+        if( i<0 ) return 1;
+        pParse->aAgg[i].isAgg = 0;
+        pParse->aAgg[i].pExpr = pExpr;
+      }
+      break;
+    }
+    case TK_AGG_FUNCTION: {
+      if( pExpr->iField==FN_Count || pExpr->iField==FN_Avg ){
+        if( pParse->iAggCount>=0 ){
+          i = pParse->iAggCount;
+        }else{
+          i = appendAggInfo(pParse);
+          if( i<0 ) return 1;
+          pParse->aAgg[i].isAgg = 1;
+          pParse->aAgg[i].pExpr = 0;
+          pParse->iAggCount = i;
+        }
+        if( pExpr->iField==FN_Count ){
+          pExpr->iAgg = i;
+          break;
+        }
+      }
+      aAgg = pParse->aAgg;
+      for(i=0; i<pParse->nAgg; i++){
+        if( !aAgg[i].isAgg ) continue;
+        if( exprDeepCompare(aAgg[i].pExpr, pExpr) ){
+          break;
+        }
+      }
+      if( i>=pParse->nAgg ){
+        i = appendAggInfo(pParse);
+        if( i<0 ) return 1;
+        pParse->aAgg[i].isAgg = 1;
+        pParse->aAgg[i].pExpr = pExpr;
+      }
+      pExpr->iAgg = i;
+      break;
+    }
+    default: {
+      if( pExpr->pLeft ){
+        nErr = sqliteExprAnalyzeAggregates(pParse, pExpr->pLeft);
+      }
+      if( nErr==0 && pExpr->pRight ){
+        nErr = sqliteExprAnalyzeAggregates(pParse, pExpr->pRight);
+      }
+      if( nErr==0 && pExpr->pList ){
+        int n = pExpr->pList->nExpr;
+        int i;
+        for(i=0; nErr==0 && i<n; i++){
+          nErr = sqliteExprAnalyzeAggregates(pParse, pExpr->pList->a[i].pExpr);
+        }
+      }
+      break;
+    }
+  }
+  return nErr;
+}
index ab16c8b4cf4b9f18e1f6e843ca2a0e27ac6426a6..082ac270ca376f69d79d1ab73151c2bdd8d68caf 100644 (file)
@@ -26,7 +26,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.12 2000/06/06 13:54:15 drh Exp $
+** @(#) $Id: parse.y,v 1.13 2000/06/06 17:27:05 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -51,7 +51,7 @@ input ::= cmdlist.
 // add them to the sqliteTokens.h output file.
 //
 input ::= END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
-          UMINUS FIELD.
+          UMINUS FIELD AGG_FUNCTION.
 
 // A list of commands is zero or more commands
 //
@@ -141,8 +141,8 @@ cmd ::= select(X).  {
 %destructor select {sqliteSelectDelete($$);}
 
 select(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
-              orderby_opt(Z). {
-  A = sqliteSelectNew(W,X,Y,0,0,Z,D);
+              groupby_opt(P) having_opt(Q) orderby_opt(Z). {
+  A = sqliteSelectNew(W,X,Y,P,Q,Z,D);
 }
 
 // The "distinct" nonterminal is true (1) if the DISTINCT keyword is
@@ -212,6 +212,16 @@ sortorder(A) ::= ASC.      {A = 0;}
 sortorder(A) ::= DESC.     {A = 1;}
 sortorder(A) ::= .         {A = 0;}
 
+%type groupby_opt {ExprList*}
+%destructor groupby_opt {sqliteExprListDelete($$);}
+groupby_opt(A) ::= .     {A = 0;}
+groupby_opt(A) ::= GROUP BY exprlist(X).  {A = X;}
+
+%type having_opt {Expr*}
+%destructor having_opt {sqliteExprDelete($$);}
+having_opt(A) ::= .      {A = 0;}
+having_opt(A) ::= HAVING expr(X).  {A = X;}
+
 cmd ::= DELETE FROM ID(X) where_opt(Y).
     {sqliteDeleteFrom(pParse, &X, Y);}
 
index cf2587e9e4d6e6a8a9851d90ae9ccbd2d16923d8..d3690ac9ef88d7d4f5f59654d080fb12ead74687 100644 (file)
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements.
 **
-** $Id: select.c,v 1.10 2000/06/06 13:54:15 drh Exp $
+** $Id: select.c,v 1.11 2000/06/06 17:27:05 drh Exp $
 */
 #include "sqliteInt.h"
 
-
 /*
 ** Allocate a new Select structure and return a pointer to that
 ** structure.
@@ -68,6 +67,105 @@ void sqliteSelectDelete(Select *p){
   sqliteFree(p);
 }
 
+/*
+** Delete the aggregate information from the parse structure.
+*/
+void sqliteParseInfoReset(Parse *pParse){
+  sqliteFree(pParse->aAgg);
+  pParse->aAgg = 0;
+  pParse->nAgg = 0;
+  pParse->iAggCount = -1;
+  pParse->useAgg = 0;
+}
+
+/*
+** This routine generates the code for the inside of the inner loop
+** of a SELECT.
+*/
+static int selectInnerLoop(
+  Parse *pParse,          /* The parser context */
+  ExprList *pEList,       /* List of values being extracted */
+  ExprList *pOrderBy,     /* If not NULL, sort results using this key */
+  int distinct,           /* If >=0, make sure results are distinct */
+  int eDest,              /* How to dispose of the results */
+  int iParm,              /* An argument to the disposal method */
+  int iContinue,          /* Jump here to continue with next row */
+  int iBreak              /* Jump here to break out of the inner loop */
+){
+  Vdbe *v = pParse->pVdbe;
+  int i;
+
+  /* Pull the requested fields.
+  */
+  for(i=0; i<pEList->nExpr; i++){
+    sqliteExprCode(pParse, pEList->a[i].pExpr);
+  }
+
+  /* If the current result is not distinct, skip the rest
+  ** of the processing for the current row.
+  */
+  if( distinct>=0 ){
+    int lbl = sqliteVdbeMakeLabel(v);
+    sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
+    sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl, 0, 0);
+    sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Goto, 0, iContinue, 0, 0);
+    sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl);
+    sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
+  }
+  /* If there is an ORDER BY clause, then store the results
+  ** in a sorter.
+  */
+  if( pOrderBy ){
+    char *zSortOrder;
+    sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
+    zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
+    if( zSortOrder==0 ) return 1;
+    for(i=0; i<pOrderBy->nExpr; i++){
+      zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
+      sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
+    }
+    zSortOrder[pOrderBy->nExpr] = 0;
+    sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
+    sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
+  }else 
+
+  /* If we are writing to a table, then write the results to the table.
+  */
+  if( eDest==SRT_Table ){
+    sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+  }else 
+
+  /* If we are creating a set for an "expr IN (SELECT ...)" construct,
+  ** then there should be a single item on the stack.  Write this
+  ** item into the set table with bogus data.
+  */
+  if( eDest==SRT_Set ){
+    assert( pEList->nExpr==1 );
+    sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+    sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+  }else 
+
+  /* If this is a scalar select that is part of an expression, then
+  ** store the results in the appropriate memory cell and break out
+  ** of the scan loop.
+  */
+  if( eDest==SRT_Mem ){
+    sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0);
+  }else
+
+  /* If none of the above, send the data to the callback function.
+  */
+  {
+    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+  }
+  return 0;
+}
+
 /*
 ** Generate code for the given SELECT statement.
 **
@@ -105,6 +203,8 @@ int sqliteSelect(
   IdList *pTabList;      /* List of tables to select from */
   Expr *pWhere;          /* The WHERE clause.  May be NULL */
   ExprList *pOrderBy;    /* The ORDER BY clause.  May be NULL */
+  ExprList *pGroupBy;    /* The GROUP BY clause.  May be NULL */
+  Expr *pHaving;         /* The HAVING clause.  May be NULL */
   int isDistinct;        /* True if the DISTINCT keyword is present */
   int distinct;          /* Table to use for the distinct set */
 
@@ -112,6 +212,8 @@ int sqliteSelect(
   pTabList = p->pSrc;
   pWhere = p->pWhere;
   pOrderBy = p->pOrderBy;
+  pGroupBy = p->pGroupBy;
+  pHaving = p->pHaving;
   isDistinct = p->isDistinct;
 
   /* 
@@ -119,6 +221,7 @@ int sqliteSelect(
   ** errors before this routine starts.
   */
   if( pParse->nErr>0 ) return 0;
+  sqliteParseInfoReset(pParse);
 
   /* Look up every table in the table list.
   */
@@ -133,10 +236,13 @@ int sqliteSelect(
   }
 
   /* Allocate a temporary table to use for the DISTINCT set, if
-  ** necessary.
+  ** necessary.  This must be done early to allocate the cursor before
+  ** any calls to sqliteExprResolveIds().
   */
   if( isDistinct ){
     distinct = pParse->nTab++;
+  }else{
+    distinct = -1;
   }
 
   /* If the list of fields to retrieve is "*" then replace it with
@@ -154,7 +260,8 @@ int sqliteSelect(
     }
   }
 
-  /* If writing to memory, only a single column may be output.
+  /* If writing to memory or generating a set
+  ** only a single column may be output.
   */
   if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){
     sqliteSetString(&pParse->zErrMsg, "only a single result allowed for "
@@ -163,7 +270,13 @@ int sqliteSelect(
     return 1;
   }
 
-  /* Resolve the field names and do a semantics check on all the expressions.
+  /* ORDER BY is ignored if we are not sending the result to a callback.
+  */
+  if( eDest!=SRT_Callback ){
+    pOrderBy = 0;
+  }
+
+  /* Allocate cursors for "expr IN (SELECT ...)" constructs.
   */
   for(i=0; i<pEList->nExpr; i++){
     sqliteExprResolveInSelect(pParse, pEList->a[i].pExpr);
@@ -174,25 +287,23 @@ int sqliteSelect(
       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);
+
+  /* Resolve the field names and do a semantics check on all the expressions.
+  */
   for(i=0; i<pEList->nExpr; i++){
     if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){
       return 1;
     }
-    if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &pEList->a[i].isAgg) ){
+    if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &isAgg) ){
       return 1;
     }
   }
-  if( pEList->nExpr>0 ){
-    isAgg = pEList->a[0].isAgg;
-    for(i=1; i<pEList->nExpr; i++){
-      if( pEList->a[i].isAgg!=isAgg ){
-        sqliteSetString(&pParse->zErrMsg, "some selected items are aggregates "
-          "and others are not", 0);
-        pParse->nErr++;
-        return 1;
-      }
-    }
-  }
   if( pWhere ){
     if( sqliteExprResolveIds(pParse, pTabList, pWhere) ){
       return 1;
@@ -203,25 +314,59 @@ int sqliteSelect(
   }
   if( pOrderBy ){
     for(i=0; i<pOrderBy->nExpr; i++){
-      if( sqliteExprResolveIds(pParse, pTabList, pOrderBy->a[i].pExpr) ){
+      Expr *pE = pOrderBy->a[i].pExpr;
+      if( sqliteExprResolveIds(pParse, pTabList, pE) ){
         return 1;
       }
-      if( sqliteExprCheck(pParse, pOrderBy->a[i].pExpr, 0, 0) ){
+      if( sqliteExprCheck(pParse, pE, isAgg, 0) ){
         return 1;
       }
     }
   }
-
-  /* ORDER BY is ignored if we are not invoking callbacks.
-  */
-  if( isAgg || eDest!=SRT_Callback ){
-    pOrderBy = 0;
+  if( pGroupBy ){
+    for(i=0; i<pGroupBy->nExpr; i++){
+      Expr *pE = pGroupBy->a[i].pExpr;
+      if( sqliteExprResolveIds(pParse, pTabList, pE) ){
+        return 1;
+      }
+      if( sqliteExprCheck(pParse, pE, isAgg, 0) ){
+        return 1;
+      }
+    }
+  }
+  if( pHaving ){
+    if( pGroupBy==0 ){
+      sqliteSetString(&pParse->zErrMsg, "a GROUP BY clause is required to "
+         "use HAVING", 0);
+      pParse->nErr++;
+      return 1;
+    }
+    if( sqliteExprResolveIds(pParse, pTabList, pHaving) ){
+      return 1;
+    }
+    if( sqliteExprCheck(pParse, pHaving, 0, 0) ){
+      return 1;
+    }
   }
 
-  /* Turn off distinct if this is an aggregate or writing to memory.
+  /* Do an analysis of aggregate expressions.
   */
-  if( isAgg || eDest==SRT_Mem ){
-    isDistinct = 0;
+  if( isAgg ){
+    for(i=0; i<pEList->nExpr; i++){
+      if( sqliteExprAnalyzeAggregates(pParse, pEList->a[i].pExpr) ){
+        return 1;
+      }
+    }
+    if( pGroupBy ){
+      for(i=0; i<pGroupBy->nExpr; i++){
+        if( sqliteExprAnalyzeAggregates(pParse, pGroupBy->a[i].pExpr) ){
+          return 1;
+        }
+      }
+    }
+    if( pHaving && sqliteExprAnalyzeAggregates(pParse, pHaving) ){
+      return 1;
+    }
   }
 
   /* Begin generating code.
@@ -239,7 +384,7 @@ int sqliteSelect(
     sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
   }
 
-  /* Identify column names if we will be using a callback.  This
+  /* Identify column names if we will be using in the callback.  This
   ** step is skipped if the output is going to a table or a memory cell.
   */
   if( eDest==SRT_Callback ){
@@ -279,23 +424,10 @@ int sqliteSelect(
     }
   }
 
-  /* Initialize the stack to contain aggregate seed values
+  /* Reset the aggregator
   */
   if( isAgg ){
-    for(i=0; i<pEList->nExpr; i++){
-      Expr *p = pEList->a[i].pExpr;
-      switch( sqliteFuncId(&p->token) ){
-        case FN_Min:
-        case FN_Max: {
-          sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
-          break;
-        }
-        default: {
-          sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
-          break;
-        }
-      }
-    }
+    sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg, 0, 0);
   }
 
   /* Initialize the memory cell to NULL
@@ -313,83 +445,98 @@ int sqliteSelect(
   pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
   if( pWInfo==0 ) return 1;
 
-  /* Pull the requested fields.
+  /* Use the standard inner loop if we are not dealing with
+  ** aggregates
   */
   if( !isAgg ){
-    for(i=0; i<pEList->nExpr; i++){
-      sqliteExprCode(pParse, pEList->a[i].pExpr);
+    if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
+                    pWInfo->iContinue, pWInfo->iBreak) ){
+       return 1;
     }
   }
 
-  /* If the current result is not distinct, script the remainder
-  ** of this processing.
-  */
-  if( isDistinct ){
-    int lbl = sqliteVdbeMakeLabel(v);
-    sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
-    sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl, 0, 0);
-    sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
-    sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl);
-    sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
-  }
-  
-  /* If there is no ORDER BY clause, then we can invoke the callback
-  ** right away.  If there is an ORDER BY, then we need to put the
-  ** data into an appropriate sorter record.
+  /* If we are dealing with aggregates, then to the special aggregate
+  ** processing.  
   */
-  if( pOrderBy ){
-    char *zSortOrder;
-    sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
-    zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
-    if( zSortOrder==0 ) return 1;
-    for(i=0; i<pOrderBy->nExpr; i++){
-      zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
-      sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
+  else{
+    int doFocus;
+    if( pGroupBy ){
+      for(i=0; i<pGroupBy->nExpr; i++){
+        sqliteExprCode(pParse, pGroupBy->a[i].pExpr);
+      }
+      sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0, 0, 0);
+      doFocus = 1;
+    }else{
+      doFocus = 0;
+      for(i=0; i<pParse->nAgg; i++){
+        if( !pParse->aAgg[i].isAgg ){
+          doFocus = 1;
+          break;
+        }
+      }
+      if( doFocus ){
+        sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+      }
     }
-    zSortOrder[pOrderBy->nExpr] = 0;
-    sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
-    sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
-  }else if( isAgg ){
-    int n = pEList->nExpr;
-    for(i=0; i<n; i++){
-      Expr *p = pEList->a[i].pExpr;
-      int id = sqliteFuncId(&p->token);
-      int op, p1;
-      if( n>1 ){
-        sqliteVdbeAddOp(v, OP_Pull, n-1, 0, 0, 0);
+    if( doFocus ){
+      int lbl1 = sqliteVdbeMakeLabel(v);
+      sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1, 0, 0);
+      for(i=0; i<pParse->nAgg; i++){
+        if( pParse->aAgg[i].isAgg ) continue;
+        sqliteExprCode(pParse, pParse->aAgg[i].pExpr);
+        sqliteVdbeAddOp(v, OP_AggSet, 0, i, 0, 0);
       }
-      if( id!=FN_Count && p->pList && p->pList->nExpr>=1 ){
-        sqliteExprCode(pParse, p->pList->a[0].pExpr);
-        sqliteVdbeAddOp(v, OP_Concat, 1, 0, 0, 0);
+      sqliteVdbeResolveLabel(v, lbl1);
+    }
+    for(i=0; i<pParse->nAgg; i++){
+      Expr *pE;
+      int op;
+      if( !pParse->aAgg[i].isAgg ) continue;
+      pE = pParse->aAgg[i].pExpr;
+      if( pE==0 ){
+        sqliteVdbeAddOp(v, OP_AggIncr, 1, i, 0, 0);
+        continue;
       }
-      switch( sqliteFuncId(&p->token) ){
-        case FN_Count: op = OP_AddImm; p1 = 1; break;
-        case FN_Sum:   op = OP_Add;    p1 = 0; break;
-        case FN_Min:   op = OP_Min;    p1 = 1; break;
-        case FN_Max:   op = OP_Max;    p1 = 0; break;
+      assert( pE->op==TK_AGG_FUNCTION );
+      assert( pE->pList!=0 && pE->pList->nExpr==1 );
+      sqliteExprCode(pParse, pE->pList->a[0].pExpr);
+      sqliteVdbeAddOp(v, OP_AggGet, 0, i, 0, 0);
+      switch( pE->iField ){
+        case FN_Min:  op = OP_Min;   break;
+        case FN_Max:  op = OP_Max;   break;
+        case FN_Avg:  op = OP_Add;   break;
+        case FN_Sum:  op = OP_Add;   break;
       }
-      sqliteVdbeAddOp(v, op, p1, 0, 0, 0);
+      sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_AggSet, 0, i, 0, 0);
     }
-  }else if( eDest==SRT_Table ){
-    sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
-  }else if( eDest==SRT_Set ){
-    sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
-    sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
-  }else if( eDest==SRT_Mem ){
-    sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iBreak, 0, 0);
-  }else{
-    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
   }
 
+
   /* End the database scan loop.
   */
   sqliteWhereEnd(pWInfo);
 
+  /* 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 = sqliteVdbeMakeLabel(v);
+    int startagg;
+    startagg = sqliteVdbeAddOp(v, OP_AggNext, 0, endagg, 0, 0);
+    pParse->useAgg = 1;
+    if( pHaving ){
+      sqliteExprIfFalse(pParse, pHaving, startagg);
+    }
+    if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
+                    startagg, endagg) ){
+      return 1;
+    }
+    sqliteVdbeAddOp(v, OP_Goto, 0, startagg, 0, 0);
+    sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, endagg);
+    pParse->useAgg = 0;
+  }
+
   /* If there is an ORDER BY clause, then we need to sort the results
   ** and send them to the callback one by one.
   */
@@ -402,24 +549,5 @@ int sqliteSelect(
     sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
     sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
   }
-
-  /* If this is an aggregate, then we need to invoke the callback
-  ** exactly once.
-  */
-  if( isAgg ){
-    if( eDest==SRT_Table ){
-      sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
-    }else if( eDest==SRT_Set ){
-      sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
-      sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
-    }else if( eDest==SRT_Mem ){
-      sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
-    }else{
-      sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
-    }
-  }
   return 0;
 }
index 42590f828136dd36d346a8a69796ccf3081067a4..8b1f94f97286d7fcc639c8b61fcb9a66aab059a6 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.17 2000/06/06 13:54:15 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.18 2000/06/06 17:27:05 drh Exp $
 */
 #include "sqlite.h"
 #include "dbbe.h"
@@ -88,6 +88,7 @@ typedef struct Token Token;
 typedef struct IdList IdList;
 typedef struct WhereInfo WhereInfo;
 typedef struct Select Select;
+typedef struct AggExpr AggExpr;
 
 /*
 ** Each database is an instance of the following structure
@@ -161,7 +162,10 @@ struct Expr {
   ExprList *pList;       /* A list of expressions used as a function argument */
   Token token;           /* An operand token */
   int iTable, iField;    /* When op==TK_FIELD, then this node means the
-                         ** iField-th field of the iTable-th table */
+                         ** iField-th field of the iTable-th table.  When
+                         ** op==TK_FUNCTION, iField holds the function id */
+  int iAgg;              /* When op==TK_FIELD and pParse->useAgg==TRUE, pull
+                         ** value from these element of the aggregator */
   Select *pSelect;       /* When the expression is a sub-select */
 };
 
@@ -234,6 +238,30 @@ struct Select {
 #define SRT_Set          3  /* Store result in a table for use with "IN" */
 #define SRT_Table        4  /* Store result in a regular table */
 
+/*
+** 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 */
+};
+
 /*
 ** An SQL parser context
 */
@@ -253,6 +281,11 @@ struct Parse {
   int nTab;            /* Number of previously allocated cursors */
   int nMem;            /* Number of memory cells used so far */
   int nSet;            /* Number of sets used so far */
+  int nAgg;            /* Number of aggregate expressions */
+  AggExpr *aAgg;       /* An array of aggregate expressions */
+  int iAggCount;       /* Index of the count(*) aggregate in aAgg[] */
+  int useAgg;          /* If true, extract field values from the aggregator
+                       ** while generating expressions.  Normally false */
 };
 
 /*
@@ -315,3 +348,5 @@ int sqliteExprCheck(Parse*, Expr*, int, int*);
 int sqliteFuncId(Token*);
 int sqliteExprResolveIds(Parse*, IdList*, Expr*);
 void sqliteExprResolveInSelect(Parse*, Expr*);
+int sqliteExprAnalyzeAggregates(Parse*, Expr*);
+void sqlitePArseInfoReset(Parse*);
index 989010e0499221b44aebdfa0d6029c7720cf7432..b64e32507a58b83feebffbae2f7b1c10d68e531d 100644 (file)
@@ -27,7 +27,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.6 2000/06/06 01:50:43 drh Exp $
+** $Id: tokenize.c,v 1.7 2000/06/06 17:27:06 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -68,6 +68,8 @@ static Keyword aKeywordTable[] = {
   { "EXPLAIN",           0, TK_EXPLAIN,          0 },
   { "FROM",              0, TK_FROM,             0 },
   { "GLOB",              0, TK_GLOB,             0 },
+  { "GROUP",             0, TK_GROUP,            0 },
+  { "HAVING",            0, TK_HAVING,           0 },
   { "IN",                0, TK_IN,               0 },
   { "INDEX",             0, TK_INDEX,            0 },
   { "INSERT",            0, TK_INSERT,           0 },
@@ -298,6 +300,7 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
   extern void sqliteParserTrace(FILE*, char *);
 
   i = 0;
+  sqliteParseInfoReset(pParse);
   pEngine = sqliteParserAlloc((void*(*)(int))malloc);
   if( pEngine==0 ){
     sqliteSetString(pzErrMsg, "out of memory", 0);
@@ -382,5 +385,6 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
     sqliteDeleteTable(pParse->db, pParse->pNewTable);
     pParse->pNewTable = 0;
   }
+  sqliteParseInfoReset(pParse);
   return nErr;
 }
index 6d31c1b9b1a041c5860176d5856dad0570f39f38..e80a1d4e7479fdeb69a0a3e2a7e2111c428f18e9 100644 (file)
@@ -41,7 +41,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.20 2000/06/06 13:54:16 drh Exp $
+** $Id: vdbe.c,v 1.21 2000/06/06 17:27:06 drh Exp $
 */
 #include "sqliteInt.h"
 #include <unistd.h>
@@ -423,7 +423,8 @@ static void AggRehash(Agg *p, int nHash){
 */
 static int AggInsert(Agg *p, char *zKey){
   AggElem *pElem;
-  if( p->nHash < p->nElem*2 ){
+  int i;
+  if( p->nHash <= p->nElem*2 ){
     AggRehash(p, p->nElem*2 + 103);
   }
   if( p->nHash==0 ) return 1;
@@ -437,6 +438,9 @@ static int AggInsert(Agg *p, char *zKey){
   p->pFirst = pElem;
   p->nElem++;
   p->pCurrent = pElem;
+  for(i=0; i<p->nMem; i++){
+    pElem->aMem[i].s.flags = STK_Null;
+  }
   return 0;
 }
 
@@ -450,7 +454,7 @@ static AggElem *_AggInFocus(Agg *p){
     p->pCurrent = pFocus;
   }else{
     AggInsert(p,"");
-    pFocus = p->pCurrent;
+    pFocus = p->pCurrent = p->pFirst;
   }
   return pFocus;
 }
@@ -1145,8 +1149,7 @@ int sqliteVdbeExec(
       ** next on stack)
       ** and push the result back onto the stack.  If either element
       ** is a string then it is converted to a double using the atof()
-      ** function before the division.  Division by zero causes the
-      ** program to abort with an error.
+      ** function before the division.  Division by zero returns NULL.
       */
       case OP_Add:
       case OP_Subtract:
@@ -1164,11 +1167,7 @@ int sqliteVdbeExec(
             case OP_Subtract:    b -= a;       break;
             case OP_Multiply:    b *= a;       break;
             default: {
-              if( a==0 ){ 
-                sqliteSetString(pzErrMsg, "division by zero", 0);
-                rc = SQLITE_ERROR;
-                goto cleanup;
-              }
+              if( a==0 ) goto divide_by_zero;
               b /= a;
               break;
             }
@@ -1188,11 +1187,7 @@ int sqliteVdbeExec(
             case OP_Subtract:    b -= a;       break;
             case OP_Multiply:    b *= a;       break;
             default: {
-              if( a==0.0 ){ 
-                sqliteSetString(pzErrMsg, "division by zero", 0);
-                rc = SQLITE_ERROR;
-                goto cleanup;
-              }
+              if( a==0.0 ) goto divide_by_zero;
               b /= a;
               break;
             }
@@ -1203,6 +1198,12 @@ int sqliteVdbeExec(
           p->aStack[nos].flags = STK_Real;
         }
         break;
+
+      divide_by_zero:
+        PopStack(p, 2);
+        p->tos = nos;
+        p->aStack[nos].flags = STK_Null;
+        break;
       }
 
       /* Opcode: Max * * *
@@ -1248,9 +1249,6 @@ int sqliteVdbeExec(
       **
       ** Pop the top two elements from the stack then push back the
       ** smaller of the two. 
-      **
-      ** If P1==1, always choose TOS for the min and decrement P1.
-      ** This is self-altering code...
       */
       case OP_Min: {
         int tos = p->tos;
@@ -1260,11 +1258,10 @@ int sqliteVdbeExec(
         if( nos<0 ) goto not_enough_stack;
         ft = p->aStack[tos].flags;
         fn = p->aStack[nos].flags;
-        if( pOp->p1 ){
-          copy = 1;
-          pOp->p1 = 0;
-        }else if( fn & STK_Null ){
+        if( fn & STK_Null ){
           copy = 1;
+        }else if( ft & STK_Null ){
+          copy = 0;
         }else if( (ft & fn & STK_Int)==STK_Int ){
           copy = p->aStack[nos].i>p->aStack[tos].i;
         }else if( ( (ft|fn) & (STK_Int|STK_Real) ) !=0 ){
@@ -2728,6 +2725,7 @@ int sqliteVdbeExec(
         }else{
           AggInsert(&p->agg, zKey);
         }
+        PopStack(p, 1);
         break; 
       }
 
index a2d064d2685b244538762175179c8a6b2deaf675..c3cd08dd8855fe1e62cb670c909b0151f21309ef 100644 (file)
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select1.test,v 1.2 2000/05/31 18:20:14 drh Exp $
+# $Id: select1.test,v 1.3 2000/06/06 17:27:06 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
 
 # Try to select on a non-existant table.
 #
-do_test select-1.1 {
+do_test select1-1.1 {
   set v [catch {execsql {SELECT * FROM test1}} msg]
   lappend v $msg
 } {1 {no such table: test1}}
 
 execsql {CREATE TABLE test1(f1 int, f2 int)}
 
-do_test select-1.2 {
+do_test select1-1.2 {
   set v [catch {execsql {SELECT * FROM test1, test2}} msg]
   lappend v $msg
 } {1 {no such table: test2}}
-do_test select-1.3 {
+do_test select1-1.3 {
   set v [catch {execsql {SELECT * FROM test2, test1}} msg]
   lappend v $msg
 } {1 {no such table: test2}}
@@ -51,39 +51,39 @@ execsql {INSERT INTO test1(f1,f2) VALUES(11,22)}
 
 # Make sure the fields are extracted correctly.
 #
-do_test select-1.4 {
+do_test select1-1.4 {
   execsql {SELECT f1 FROM test1}
 } {11}
-do_test select-1.5 {
+do_test select1-1.5 {
   execsql {SELECT f2 FROM test1}
 } {22}
-do_test select-1.6 {
+do_test select1-1.6 {
   execsql {SELECT f2, f1 FROM test1}
 } {22 11}
-do_test select-1.7 {
+do_test select1-1.7 {
   execsql {SELECT f1, f2 FROM test1}
 } {11 22}
-do_test select-1.8 {
+do_test select1-1.8 {
   execsql {SELECT * FROM test1}
 } {11 22}
 
 execsql {CREATE TABLE test2(r1 real, r2 real)}
 execsql {INSERT INTO test2(r1,r2) VALUES(1.1,2.2)}
 
-do_test select-1.9 {
+do_test select1-1.9 {
   execsql {SELECT * FROM test1, test2}
 } {11 22 1.1 2.2}
-do_test select-1.10 {
+do_test select1-1.10 {
   execsql {SELECT test1.f1, test2.r1 FROM test1, test2}
 } {11 1.1}
-do_test select-1.11 {
+do_test select1-1.11 {
   execsql {SELECT test1.f1, test2.r1 FROM test2, test1}
 } {11 1.1}
-do_test select-1.12 {
+do_test select1-1.12 {
   execsql {SELECT max(test1.f1,test2.r1), min(test1.f2,test2.r2)
            FROM test2, test1}
 } {11 2.2}
-do_test select-1.13 {
+do_test select1-1.13 {
   execsql {SELECT min(test1.f1,test2.r1), max(test1.f2,test2.r2)
            FROM test1, test2}
 } {1.1 22}
@@ -95,148 +95,148 @@ execsql {INSERT INTO test1 VALUES(33,44)}
 
 # Error messges from sqliteExprCheck
 #
-do_test select-2.1 {
+do_test select1-2.1 {
   set v [catch {execsql {SELECT count(f1,f2) FROM test1}} msg]
   lappend v $msg
 } {1 {too many arguments to function count()}}
-do_test select-2.2 {
+do_test select1-2.2 {
   set v [catch {execsql {SELECT count(f1) FROM test1}} msg]
   lappend v $msg
 } {0 2}
-do_test select-2.3 {
+do_test select1-2.3 {
   set v [catch {execsql {SELECT Count() FROM test1}} msg]
   lappend v $msg
 } {0 2}
-do_test select-2.4 {
+do_test select1-2.4 {
   set v [catch {execsql {SELECT COUNT(*) FROM test1}} msg]
   lappend v $msg
 } {0 2}
-do_test select-2.5 {
+do_test select1-2.5 {
   set v [catch {execsql {SELECT COUNT(*)+1 FROM test1}} msg]
   lappend v $msg
-} {1 {no such function: COUNT}}
-do_test select-2.6 {
+} {0 3}
+do_test select1-2.6 {
   set v [catch {execsql {SELECT min(*) FROM test1}} msg]
   lappend v $msg
 } {1 {too few arguments to function min()}}
-do_test select-2.7 {
+do_test select1-2.7 {
   set v [catch {execsql {SELECT Min(f1) FROM test1}} msg]
   lappend v $msg
 } {0 11}
-do_test select-2.8 {
+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 select-2.9 {
+do_test select1-2.9 {
   set v [catch {execsql {SELECT MAX(*) FROM test1}} msg]
   lappend v $msg
 } {1 {too few arguments to function MAX()}}
-do_test select-2.10 {
+do_test select1-2.10 {
   set v [catch {execsql {SELECT Max(f1) FROM test1}} msg]
   lappend v $msg
 } {0 33}
-do_test select-2.11 {
+do_test select1-2.11 {
   set v [catch {execsql {SELECT max(f1,f2) FROM test1}} msg]
   lappend v [lsort $msg]
 } {0 {22 44}}
-do_test select-2.12 {
+do_test select1-2.12 {
   set v [catch {execsql {SELECT MAX(f1,f2)+1 FROM test1}} msg]
   lappend v [lsort $msg]
 } {0 {23 45}}
-do_test select-2.13 {
+do_test select1-2.13 {
   set v [catch {execsql {SELECT MAX(f1)+1 FROM test1}} msg]
   lappend v $msg
-} {1 {too few arguments to function MAX()}}
-do_test select-2.14 {
+} {0 34}
+do_test select1-2.14 {
   set v [catch {execsql {SELECT SUM(*) FROM test1}} msg]
   lappend v $msg
 } {1 {too few arguments to function SUM()}}
-do_test select-2.15 {
+do_test select1-2.15 {
   set v [catch {execsql {SELECT Sum(f1) FROM test1}} msg]
   lappend v $msg
 } {0 44}
-do_test select-2.16 {
+do_test select1-2.16 {
   set v [catch {execsql {SELECT sum(f1,f2) FROM test1}} msg]
   lappend v $msg
 } {1 {too many arguments to function sum()}}
-do_test select-2.17 {
+do_test select1-2.17 {
   set v [catch {execsql {SELECT SUM(f1)+1 FROM test1}} msg]
   lappend v $msg
-} {1 {no such function: SUM}}
-do_test select-2.18 {
+} {0 45}
+do_test select1-2.18 {
   set v [catch {execsql {SELECT XYZZY(f1) FROM test1}} msg]
   lappend v $msg
 } {1 {no such function: XYZZY}}
-do_test select-2.19 {
+do_test select1-2.19 {
   set v [catch {execsql {SELECT SUM(min(f1,f2)) FROM test1}} msg]
   lappend v $msg
 } {0 44}
-do_test select-2.20 {
+do_test select1-2.20 {
   set v [catch {execsql {SELECT SUM(min(f1)) FROM test1}} msg]
   lappend v $msg
 } {1 {too few arguments to function min()}}
 
 # WHERE clause expressions
 #
-do_test select-3.1 {
+do_test select1-3.1 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE f1<11}} msg]
   lappend v $msg
 } {0 {}}
-do_test select-3.2 {
+do_test select1-3.2 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE f1<=11}} msg]
   lappend v $msg
 } {0 11}
-do_test select-3.3 {
+do_test select1-3.3 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE f1=11}} msg]
   lappend v $msg
 } {0 11}
-do_test select-3.4 {
+do_test select1-3.4 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE f1>=11}} msg]
   lappend v [lsort $msg]
 } {0 {11 33}}
-do_test select-3.5 {
+do_test select1-3.5 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE f1>11}} msg]
   lappend v [lsort $msg]
 } {0 33}
-do_test select-3.6 {
+do_test select1-3.6 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE f1!=11}} msg]
   lappend v [lsort $msg]
 } {0 33}
-do_test select-3.7 {
+do_test select1-3.7 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE min(f1,f2)!=11}} msg]
   lappend v [lsort $msg]
 } {0 33}
-do_test select-3.8 {
+do_test select1-3.8 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE max(f1,f2)!=11}} msg]
   lappend v [lsort $msg]
 } {0 {11 33}}
-do_test select-3.9 {
+do_test select1-3.9 {
   set v [catch {execsql {SELECT f1 FROM test1 WHERE count(f1,f2)!=11}} msg]
   lappend v $msg
 } {1 {no such function: count}}
 
 # ORDER BY expressions
 #
-do_test select-4.1 {
+do_test select1-4.1 {
   set v [catch {execsql {SELECT f1 FROM test1 ORDER BY f1}} msg]
   lappend v $msg
 } {0 {11 33}}
-do_test select-4.2 {
+do_test select1-4.2 {
   set v [catch {execsql {SELECT f1 FROM test1 ORDER BY -f1}} msg]
   lappend v $msg
 } {0 {33 11}}
-do_test select-4.3 {
+do_test select1-4.3 {
   set v [catch {execsql {SELECT f1 FROM test1 ORDER BY min(f1,f2)}} msg]
   lappend v $msg
 } {0 {11 33}}
-do_test select-4.4 {
+do_test select1-4.4 {
   set v [catch {execsql {SELECT f1 FROM test1 ORDER BY min(f1)}} msg]
   lappend v $msg
 } {1 {too few arguments to function min()}}
 
 # ORDER BY ignored on an aggregate query
 #
-do_test select-5.1 {
+do_test select1-5.1 {
   set v [catch {execsql {SELECT max(f1) FROM test1 ORDER BY f2}} msg]
   lappend v $msg
 } {0 33}
@@ -246,47 +246,47 @@ execsql {INSERT INTO test2 VALUES('abc','xyz')}
 
 # Check for field naming
 #
-do_test select-6.1 {
+do_test select1-6.1 {
   set v [catch {execsql2 {SELECT f1 FROM test1 ORDER BY f2}} msg]
   lappend v $msg
 } {0 {f1 11 f1 33}}
-do_test select-6.2 {
+do_test select1-6.2 {
   set v [catch {execsql2 {SELECT f1 as xyzzy FROM test1 ORDER BY f2}} msg]
   lappend v $msg
 } {0 {xyzzy 11 xyzzy 33}}
-do_test select-6.3 {
+do_test select1-6.3 {
   set v [catch {execsql2 {SELECT f1 as "xyzzy" FROM test1 ORDER BY f2}} msg]
   lappend v $msg
 } {0 {xyzzy 11 xyzzy 33}}
-do_test select-6.4 {
+do_test select1-6.4 {
   set v [catch {execsql2 {SELECT f1+F2 as xyzzy FROM test1 ORDER BY f2}} msg]
   lappend v $msg
 } {0 {xyzzy 33 xyzzy 77}}
-do_test select-6.5 {
+do_test select1-6.5 {
   set v [catch {execsql2 {SELECT test1.f1+F2 FROM test1 ORDER BY f2}} msg]
   lappend v $msg
 } {0 {field1 33 field1 77}}
-do_test select-6.6 {
+do_test select1-6.6 {
   set v [catch {execsql2 {SELECT test1.f1+F2, t1 FROM test1, test2 
          ORDER BY f2}} msg]
   lappend v $msg
 } {0 {field1 33 test2.t1 abc field1 77 test2.t1 abc}}
-do_test select-6.7 {
+do_test select1-6.7 {
   set v [catch {execsql2 {SELECT A.f1, t1 FROM test1 as A, test2 
          ORDER BY f2}} msg]
   lappend v $msg
 } {0 {A.f1 11 test2.t1 abc A.f1 33 test2.t1 abc}}
-do_test select-6.8 {
+do_test select1-6.8 {
   set v [catch {execsql2 {SELECT A.f1, f1 FROM test1 as A, test1 as B 
          ORDER BY f2}} msg]
   lappend v $msg
 } {1 {ambiguous field name: f1}}
-do_test select-6.8 {
+do_test select1-6.8 {
   set v [catch {execsql2 {SELECT A.f1, B.f1 FROM test1 as A, test1 as B 
          ORDER BY f2}} msg]
   lappend v $msg
 } {1 {ambiguous field name: f2}}
-do_test select-6.9 {
+do_test select1-6.9 {
   set v [catch {execsql2 {SELECT A.f1, B.f1 FROM test1 as A, test1 as B 
          ORDER BY A.f1, B.f1}} msg]
   lappend v $msg