]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
added Agg opcodes to the vdbe (CVS 54)
authordrh <drh@noemail.net>
Mon, 5 Jun 2000 21:39:48 +0000 (21:39 +0000)
committerdrh <drh@noemail.net>
Mon, 5 Jun 2000 21:39:48 +0000 (21:39 +0000)
FossilOrigin-Name: e9ed5d2a3639161fb58856275ba9495f21d366bf

manifest
manifest.uuid
src/vdbe.c
src/vdbe.h
test/subselect.test

index 82b8b31a1a0370d89471c0d912b4f543e2074b9f..ff6a910d3cb1e856645c56a29db6e6e644541084 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C :-)\s(CVS\s53)
-D 2000-06-05T18:56:43
+C added\sAgg\sopcodes\sto\sthe\svdbe\s(CVS\s54)
+D 2000-06-05T21:39:49
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@@ -22,8 +22,8 @@ F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
 F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1
 F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78
 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
-F src/vdbe.c b8b78d0eaae9aa68e96b9d37da7d4571905396ca
-F src/vdbe.h 5fd02cb52af0efa0165dd67b6e0036b853e1620f
+F src/vdbe.c e14cf771e4f487feb433fc6c0c6bd9536167a953
+F src/vdbe.h 7b3cd6b122b72ce8d2f5ab40069748d32f1d9df0
 F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251
 F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
 F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226
@@ -33,7 +33,7 @@ F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
 F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf
 F test/select1.test a0b00df77e85adff75c338e487718c5d31f69e3a
 F test/select2.test 3cd3c0f9d67e98b1b54af5853679b4a111224410
-F test/subselect.test 9bba3573f3364b4ae4b8c48bbf51d782e806ce11
+F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5
 F test/table.test 85d6f410d127ec508c6640f02d7c40d218414e81
 F test/tester.tcl 44690d463c1dc83a4c76ccde07cc146a988600f6
 F test/update.test 69459302ea75cafac1479e60b0e36efb88123c0e
@@ -48,7 +48,7 @@ F www/c_interface.tcl 8867d76ddd416d2fbd41e4cb3de8efa9cef105a5
 F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77
 F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44
 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
-P c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa
-R 048dcd3eb7b277c896b652db05acc39f
+P 3dbfa558ef01ac9249a3f40da49bc71cc7b040bc
+R 2c42d01480de541004757564bdb21603
 U drh
-Z 435b2a37a755d776ddffa2796ea2a6cd
+Z 3c51c450bc15ba9b865ce0cc0b812208
index 63e8a6ccb1c1e497526e3b2f321ec1ed5ea9938a..36ad03c6660a051d58eeb61a9f582dcaa5cf68a6 100644 (file)
@@ -1 +1 @@
-3dbfa558ef01ac9249a3f40da49bc71cc7b040bc
\ No newline at end of file
+e9ed5d2a3639161fb58856275ba9495f21d366bf
\ No newline at end of file
index 1b836bb8fb115bf317fa65f4bae681a1546cf03f..04e8be71d21996c082a41fe2ac3b6332753f4d78 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.16 2000/06/05 18:54:47 drh Exp $
+** $Id: vdbe.c,v 1.17 2000/06/05 21:39:49 drh Exp $
 */
 #include "sqliteInt.h"
 #include <unistd.h>
@@ -106,6 +106,30 @@ struct Mem {
 };
 typedef struct Mem Mem;
 
+/*
+** An Agg structure describes and 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 */
+  int nElem;             /* The number of AggElems */
+  int nHash;             /* Number of slots in apHash[] */
+  AggElem **apHash;      /* A hash table for looking up AggElems by zKey */
+  AggElem *pFirst;       /* A list of all AggElems */
+};
+struct AggElem {
+  char *zKey;            /* The key to this AggElem */
+  AggElem *pHash;        /* Next AggElem with the same hash on zKey */
+  AggElem *pNext;        /* Next AggElem in a list of them all */
+  Mem aMem[1];           /* The values for this AggElem */
+};
+
 /*
 ** Allowed values for Stack.flags
 */
@@ -145,6 +169,7 @@ struct Vdbe {
   int nLineAlloc;     /* Number of spaces allocated for zLine */
   int nMem;           /* Number of memory locations currently allocated */
   Mem *aMem;          /* The memory locations */
+  Agg agg;            /* Aggregate information */
 };
 
 /*
@@ -327,6 +352,89 @@ int sqliteVdbeMakeLabel(Vdbe *p){
   return -1-i;
 }
 
+/*
+** Reset an Agg structure.  Delete all its contents.
+*/
+static void AggReset(Agg *p){
+  int i;
+  while( p->pFirst ){
+    AggElem *pElem = p->pFirst;
+    p->pFirst = pElem->pNext;
+    for(i=0; i<p->nMem; i++){
+      if( pElem->aMem[i].s.flags & STK_Dyn ){
+        sqliteFree(pElem->aMem[i].z);
+      }
+    }
+    sqliteFree(pElem);
+  }
+  sqliteFree(p->apHash);
+  memset(p, 0, sizeof(*p));
+}
+
+/*
+** Add the given AggElem to the hash table
+*/
+static void AggEnhash(Agg *p, AggElem *pElem){
+  int h = sqliteHashNoCase(pElem->zKey, 0) % p->nHash;
+  pElem->pHash = p->apHash[h];
+  p->apHash[h] = pElem;
+}
+
+/*
+** Change the size of the hash table to the amount given.
+*/
+static void AggRehash(Agg *p, int nHash){
+  int size;
+  AggElem *pElem;
+  if( p->nHash==nHash ) return;
+  size = nHash * sizeof(AggElem*);
+  p->apHash = sqliteRealloc(p->apHash, size );
+  memset(p->apHash, 0, size);
+  p->nHash = nHash;
+  for(pElem=p->pFirst; pElem; pElem=pElem->pNext){
+    AggEnhash(p, pElem);
+  }
+}
+
+/*
+** Insert a new element and make it the current element.  
+**
+** Return 0 on success and 1 if memory is exhausted.
+*/
+static int AggInsert(Agg *p, char *zKey){
+  AggElem *pElem;
+  if( p->nHash < p->nElem*2 ){
+    AggRehash(p, p->nElem*2 + 103);
+  }
+  if( p->nHash==0 ) return 1;
+  pElem = sqliteMalloc( sizeof(AggElem) + strlen(zKey) + 1 +
+                        (p->nMem-1)*sizeof(pElem->aMem[0]) );
+  if( pElem==0 ) return 1;
+  pElem->zKey = (char*)&pElem->aMem[p->nMem];
+  strcpy(pElem->zKey, zKey);
+  AggEnhash(p, pElem);
+  pElem->pNext = p->pFirst;
+  p->pFirst = pElem;
+  p->nElem++;
+  p->pCurrent = pElem;
+  return 0;
+}
+
+/*
+** Get the AggElem currently in focus
+*/
+#define AggInFocus(P)   ((P).pCurrent ? (P).pCurrent : _AggInFocus(&(P)))
+static AggElem *_AggInFocus(Agg *p){
+  AggElem *pFocus = p->pFirst;
+  if( pFocus ){
+    p->pCurrent = pFocus;
+  }else{
+    AggInsert(p,"");
+    pFocus = p->pCurrent;
+  }
+  return pFocus;
+}
+
 /*
 ** Convert the given stack entity into a string if it isn't one
 ** already.  Return non-zero if we run out of memory.
@@ -519,6 +627,7 @@ static void Cleanup(Vdbe *p){
     p->zLine = 0;
   }
   p->nLineAlloc = 0;
+  AggReset(&p->agg);
 }
 
 /*
@@ -561,16 +670,17 @@ static char *zOpName[] = { 0,
   "SortOpen",       "SortPut",        "SortMakeRec",    "SortMakeKey",
   "Sort",           "SortNext",       "SortKey",        "SortCallback",
   "SortClose",      "FileOpen",       "FileRead",       "FileField",
-  "FileClose",      "MakeRecord",     "MakeKey",        "Goto",
-  "If",             "Halt",           "ColumnCount",    "ColumnName",
-  "Callback",       "Integer",        "String",         "Null",
-  "Pop",            "Dup",            "Pull",           "Add",
-  "AddImm",         "Subtract",       "Multiply",       "Divide",
-  "Min",            "Max",            "Like",           "Glob",
-  "Eq",             "Ne",             "Lt",             "Le",
-  "Gt",             "Ge",             "IsNull",         "NotNull",
-  "Negative",       "And",            "Or",             "Not",
-  "Concat",         "Noop",         
+  "FileClose",      "AggReset",       "AggFocus",       "AggIncr",
+  "AggNext",        "AggSet",         "AggGet",         "MakeRecord",
+  "MakeKey",        "Goto",           "If",             "Halt",
+  "ColumnCount",    "ColumnName",     "Callback",       "Integer",
+  "String",         "Null",           "Pop",            "Dup",
+  "Pull",           "Add",            "AddImm",         "Subtract",
+  "Multiply",       "Divide",         "Min",            "Max",
+  "Like",           "Glob",           "Eq",             "Ne",
+  "Lt",             "Le",             "Gt",             "Ge",
+  "IsNull",         "NotNull",        "Negative",       "And",
+  "Or",             "Not",            "Concat",         "Noop",
 };
 
 /*
@@ -2481,6 +2591,164 @@ int sqliteVdbeExec(
         break;
       }
 
+      /* Opcode: AggReset * P2 *
+      **
+      ** Reset the aggregator so that it no longer contains any data.
+      ** Future aggregator elements will contain P2 values each.
+      */
+      case OP_AggReset: {
+        AggReset(&p->agg);
+        p->agg.nMem = pOp->p2;
+        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.
+      **
+      ** This opcode should not be executed after an AggNext but before
+      ** the next AggReset.
+      */
+      case OP_AggFocus: {
+        int tos = p->tos;
+        AggElem *pElem;
+        char *zKey;
+        int nKey;
+
+        if( tos<0 ) goto not_enough_stack;
+        Stringify(p, tos);
+        zKey = p->zStack[tos]; 
+        nKey = p->aStack[tos].n;
+        if( p->agg.nHash<=0 ){
+          pElem = 0;
+        }else{
+          int h = sqliteHashNoCase(zKey, nKey-1) % p->agg.nHash;
+          for(pElem=p->agg.apHash[h]; pElem; pElem=pElem->pHash){
+            if( strcmp(pElem->zKey, zKey)==0 ) break;
+          }
+        }
+        if( pElem ){
+          p->agg.pCurrent = pElem;
+          pc = pOp->p2 - 1;
+        }else{
+          AggInsert(&p->agg, zKey);
+        }
+        break; 
+      }
+
+      /* Opcode: AggIncr P1 P2 *
+      **
+      ** Increment the P2-th field of the aggregate element current
+      ** in focus by an amount P1.
+      */
+      case OP_AggIncr: {
+        AggElem *pFocus = AggInFocus(p->agg);
+        int i = pOp->p2;
+        if( pFocus==0 ) goto no_mem;
+        if( i>=0 && i<p->agg.nMem ){
+          Mem *pMem = &pFocus->aMem[i];
+          if( pMem->s.flags!=STK_Int ){
+            if( pMem->s.flags & STK_Int ){
+              /* Do nothing */
+            }else if( pMem->s.flags & STK_Real ){
+              pMem->s.i = pMem->s.r;
+            }else if( pMem->s.flags & STK_Str ){
+              pMem->s.i = atoi(pMem->z);
+            }else{
+              pMem->s.i = 0;
+            }
+            if( pMem->s.flags & STK_Dyn ) sqliteFree(pMem->z);
+            pMem->z = 0;
+            pMem->s.flags = STK_Int;
+          }
+          pMem->s.i += pOp->p1;
+        }
+        break;
+      }
+
+      /* Opcode: AggSet * P2 *
+      **
+      ** Move the top of the stack into the P2-th field of the current
+      ** aggregate.  String values are duplicated into new memory.
+      */
+      case OP_AggSet: {
+        AggElem *pFocus = AggInFocus(p->agg);
+        int i = pOp->p2;
+        int tos = p->tos;
+        if( tos<0 ) goto not_enough_stack;
+        if( pFocus==0 ) goto no_mem;
+        if( i>=0 && i<p->agg.nMem ){
+          Mem *pMem = &pFocus->aMem[i];
+          pMem->s = p->aStack[tos];
+          if( pMem->s.flags & STK_Str ){
+            pMem->z = sqliteMalloc( p->aStack[tos].n );
+            if( pMem->z==0 ) goto no_mem;
+            memcpy(pMem->z, p->zStack[tos], pMem->s.n);
+            pMem->s.flags |= STK_Str|STK_Dyn;
+          }
+        }
+        PopStack(p, 1);
+        break;
+      }
+
+      /* Opcode: AggGet * P2 *
+      **
+      ** Push a new entry onto the stack which is a copy of the P2-th field
+      ** of the current aggregate.  String are not duplicated so
+      ** string values will be ephemeral.  
+      */
+      case OP_AggGet: {
+        AggElem *pFocus = AggInFocus(p->agg);
+        int i = pOp->p2;
+        int tos = ++p->tos;
+        if( NeedStack(p, tos) ) goto no_mem;
+        if( pFocus==0 ) goto no_mem;
+        if( i>=0 && i<p->agg.nMem ){
+          Mem *pMem = &pFocus->aMem[i];
+          p->aStack[tos] = pMem->s;
+          p->zStack[tos] = pMem->z;
+          p->aStack[tos].flags &= ~STK_Dyn;
+        }
+        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.
+      **
+      ** Do not execute an AggFocus after this opcode until after the
+      ** next AggReset.
+      */
+      case OP_AggNext: {
+        if( p->agg.nHash ){
+          p->agg.nHash = 0;
+          sqliteFree(p->agg.apHash);
+          p->agg.apHash = 0;
+          p->agg.pCurrent = p->agg.pFirst;
+        }else if( p->agg.pCurrent==p->agg.pFirst ){
+          int i;
+          AggElem *pElem = p->agg.pCurrent;
+          for(i=0; i<p->agg.nMem; i++){
+            if( pElem->aMem[i].s.flags & STK_Dyn ){
+              sqliteFree(pElem->aMem[i].z);
+            }
+          }
+          p->agg.pCurrent = p->agg.pFirst = pElem->pNext;
+          sqliteFree(pElem);
+          p->agg.nElem--;
+        }
+        if( p->agg.pCurrent==0 ){
+          pc = pOp->p2-1;
+        }
+        break;
+      }
+
       /* An other opcode is illegal...
       */
       default: {
index c85a1d3a812c8a5b18c1c25a0eb6ca0f845604d6..9aaa43abb6485239ebcd174185fb96dcec61e504 100644 (file)
@@ -27,7 +27,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.6 2000/06/05 18:54:47 drh Exp $
+** $Id: vdbe.h,v 1.7 2000/06/05 21:39:49 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -115,49 +115,56 @@ typedef struct VdbeOp VdbeOp;
 #define OP_FileField          36
 #define OP_FileClose          37
 
-#define OP_MakeRecord         38
-#define OP_MakeKey            39
-
-#define OP_Goto               40
-#define OP_If                 41
-#define OP_Halt               42
-
-#define OP_ColumnCount        43
-#define OP_ColumnName         44
-#define OP_Callback           45
-
-#define OP_Integer            46
-#define OP_String             47
-#define OP_Null               48
-#define OP_Pop                49
-#define OP_Dup                50
-#define OP_Pull               51
-
-#define OP_Add                52
-#define OP_AddImm             53
-#define OP_Subtract           54
-#define OP_Multiply           55
-#define OP_Divide             56
-#define OP_Min                57
-#define OP_Max                58
-#define OP_Like               59
-#define OP_Glob               60
-#define OP_Eq                 61
-#define OP_Ne                 62
-#define OP_Lt                 63
-#define OP_Le                 64
-#define OP_Gt                 65
-#define OP_Ge                 66
-#define OP_IsNull             67
-#define OP_NotNull            68
-#define OP_Negative           69
-#define OP_And                70
-#define OP_Or                 71
-#define OP_Not                72
-#define OP_Concat             73
-#define OP_Noop               74
-
-#define OP_MAX                74
+#define OP_AggReset           38
+#define OP_AggFocus           39
+#define OP_AggIncr            40
+#define OP_AggNext            41
+#define OP_AggSet             42
+#define OP_AggGet             43
+
+#define OP_MakeRecord         44
+#define OP_MakeKey            45
+
+#define OP_Goto               46
+#define OP_If                 47
+#define OP_Halt               48
+
+#define OP_ColumnCount        49
+#define OP_ColumnName         50
+#define OP_Callback           51
+
+#define OP_Integer            52
+#define OP_String             53
+#define OP_Null               54
+#define OP_Pop                55
+#define OP_Dup                56
+#define OP_Pull               57
+
+#define OP_Add                58
+#define OP_AddImm             59
+#define OP_Subtract           60
+#define OP_Multiply           61
+#define OP_Divide             62
+#define OP_Min                63
+#define OP_Max                64
+#define OP_Like               65
+#define OP_Glob               66
+#define OP_Eq                 67
+#define OP_Ne                 68
+#define OP_Lt                 69
+#define OP_Le                 70
+#define OP_Gt                 71
+#define OP_Ge                 72
+#define OP_IsNull             73
+#define OP_NotNull            74
+#define OP_Negative           75
+#define OP_And                76
+#define OP_Or                 77
+#define OP_Not                78
+#define OP_Concat             79
+#define OP_Noop               80
+
+#define OP_MAX                80
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index 113fab5e338567ef99a904c1a7fb26aaa63fe478..5a436cd572cef69d276ea84d1a641490e44f8053 100644 (file)
@@ -24,7 +24,7 @@
 # focus of this file is testing SELECT statements that are part of
 # expressions.
 #
-# $Id: subselect.test,v 1.2 2000/06/05 18:56:43 drh Exp $
+# $Id: subselect.test,v 1.3 2000/06/05 21:39:49 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -87,4 +87,12 @@ do_test subselect-1.5 {
   }
 } {8}
 
+# Try something useful.  Delete every entry from t2 where the
+# x value is less than half of the maximum.
+#
+do_test subselect-1.6 {
+  execsql {DELETE FROM t2 WHERE x < 0.5*(SELECT max(x) FROM t2)}
+  execsql {SELECT x FROM t2 ORDER BY x}
+} {2 3 4}
+
 finish_test