GROUP BY and HAVING installed (CVS 58)

FossilOrigin-Name: db88a0c2d4b5c5cd05e0172f061fc33763fe3829
This commit is contained in:
drh 2000-06-06 17:27:05 +00:00
parent 4794b98035
commit 2282792a4a
9 changed files with 565 additions and 234 deletions

View File

@ -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

View File

@ -1 +1 @@
54d198189b58366e4e40139102bc6de94ac55e18
db88a0c2d4b5c5cd05e0172f061fc33763fe3829

View 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;
}

View 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);}

View File

@ -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;
}

View 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*);

View 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;
}

View 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;
}

View File

@ -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