GROUP BY and HAVING installed (CVS 58)
FossilOrigin-Name: db88a0c2d4b5c5cd05e0172f061fc33763fe3829
This commit is contained in:
parent
4794b98035
commit
2282792a4a
24
manifest
24
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
|
||||
|
@ -1 +1 @@
|
||||
54d198189b58366e4e40139102bc6de94ac55e18
|
||||
db88a0c2d4b5c5cd05e0172f061fc33763fe3829
|
170
src/expr.c
170
src/expr.c
@ -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;
|
||||
}
|
||||
|
18
src/parse.y
18
src/parse.y
@ -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);}
|
||||
|
||||
|
376
src/select.c
376
src/select.c
@ -24,11 +24,10 @@
|
||||
** 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 we are dealing with aggregates, then to the special aggregate
|
||||
** 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( 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
sqliteVdbeAddOp(v, op, p1, 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;
|
||||
}
|
||||
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, 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;
|
||||
}
|
||||
|
@ -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*);
|
||||
|
@ -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;
|
||||
}
|
||||
|
42
src/vdbe.c
42
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.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;
|
||||
}
|
||||
|
||||
|
@ -23,25 +23,25 @@
|
||||
# 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user