mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
added Agg opcodes to the vdbe (CVS 54)
FossilOrigin-Name: e9ed5d2a3639161fb58856275ba9495f21d366bf
This commit is contained in:
16
manifest
16
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
|
||||
|
@@ -1 +1 @@
|
||||
3dbfa558ef01ac9249a3f40da49bc71cc7b040bc
|
||||
e9ed5d2a3639161fb58856275ba9495f21d366bf
|
290
src/vdbe.c
290
src/vdbe.c
@@ -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: {
|
||||
|
85
src/vdbe.h
85
src/vdbe.h
@@ -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_AggReset 38
|
||||
#define OP_AggFocus 39
|
||||
#define OP_AggIncr 40
|
||||
#define OP_AggNext 41
|
||||
#define OP_AggSet 42
|
||||
#define OP_AggGet 43
|
||||
|
||||
#define OP_Goto 40
|
||||
#define OP_If 41
|
||||
#define OP_Halt 42
|
||||
#define OP_MakeRecord 44
|
||||
#define OP_MakeKey 45
|
||||
|
||||
#define OP_ColumnCount 43
|
||||
#define OP_ColumnName 44
|
||||
#define OP_Callback 45
|
||||
#define OP_Goto 46
|
||||
#define OP_If 47
|
||||
#define OP_Halt 48
|
||||
|
||||
#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_ColumnCount 49
|
||||
#define OP_ColumnName 50
|
||||
#define OP_Callback 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_Integer 52
|
||||
#define OP_String 53
|
||||
#define OP_Null 54
|
||||
#define OP_Pop 55
|
||||
#define OP_Dup 56
|
||||
#define OP_Pull 57
|
||||
|
||||
#define OP_MAX 74
|
||||
#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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user