-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
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
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
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
-54d198189b58366e4e40139102bc6de94ac55e18
\ No newline at end of file
+db88a0c2d4b5c5cd05e0172f061fc33763fe3829
\ No newline at end of file
*************************************************************************
** 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"
{ "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++){
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);
int too_few_args = 0;
int is_agg = 0;
int i;
+ pExpr->iField = id;
switch( id ){
case FN_Unknown: {
no_such_func = 1;
is_agg = n==1;
break;
}
+ case FN_Avg:
case FN_Sum: {
no_such_func = !allowAgg;
too_many_args = n>1;
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);
}
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;
}
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: {
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;
}
}
}
+
+/*
+** 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;
+}
** 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}
// 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
//
%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
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);}
** 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.
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.
**
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 */
pTabList = p->pSrc;
pWhere = p->pWhere;
pOrderBy = p->pOrderBy;
+ pGroupBy = p->pGroupBy;
+ pHaving = p->pHaving;
isDistinct = p->isDistinct;
/*
** errors before this routine starts.
*/
if( pParse->nErr>0 ) return 0;
+ sqliteParseInfoReset(pParse);
/* Look up every table in the table list.
*/
}
/* 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
}
}
- /* 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 "
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);
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;
}
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.
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 ){
}
}
- /* 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
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
if( pWInfo==0 ) return 1;
- /* Pull the requested fields.
+ /* Use the standard inner loop if we are not dealing with
+ ** aggregates
*/
if( !isAgg ){
- for(i=0; i<pEList->nExpr; i++){
- sqliteExprCode(pParse, pEList->a[i].pExpr);
+ if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
+ pWInfo->iContinue, pWInfo->iBreak) ){
+ return 1;
}
}
- /* If the current result is not distinct, script the remainder
- ** of this processing.
- */
- if( isDistinct ){
- int lbl = sqliteVdbeMakeLabel(v);
- sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
- sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl, 0, 0);
- sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl);
- sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
- }
-
- /* If there is no ORDER BY clause, then we can invoke the callback
- ** right away. If there is an ORDER BY, then we need to put the
- ** data into an appropriate sorter record.
+ /* If we are dealing with aggregates, then to the special aggregate
+ ** processing.
*/
- if( pOrderBy ){
- char *zSortOrder;
- sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
- zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
- if( zSortOrder==0 ) return 1;
- for(i=0; i<pOrderBy->nExpr; i++){
- zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
- sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
+ else{
+ int doFocus;
+ if( pGroupBy ){
+ for(i=0; i<pGroupBy->nExpr; i++){
+ sqliteExprCode(pParse, pGroupBy->a[i].pExpr);
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0, 0, 0);
+ doFocus = 1;
+ }else{
+ doFocus = 0;
+ for(i=0; i<pParse->nAgg; i++){
+ if( !pParse->aAgg[i].isAgg ){
+ doFocus = 1;
+ break;
+ }
+ }
+ if( doFocus ){
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+ }
}
- zSortOrder[pOrderBy->nExpr] = 0;
- sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
- sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
- }else if( isAgg ){
- int n = pEList->nExpr;
- for(i=0; i<n; i++){
- Expr *p = pEList->a[i].pExpr;
- int id = sqliteFuncId(&p->token);
- int op, p1;
- if( n>1 ){
- sqliteVdbeAddOp(v, OP_Pull, n-1, 0, 0, 0);
+ if( doFocus ){
+ int lbl1 = sqliteVdbeMakeLabel(v);
+ sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1, 0, 0);
+ for(i=0; i<pParse->nAgg; i++){
+ if( pParse->aAgg[i].isAgg ) continue;
+ sqliteExprCode(pParse, pParse->aAgg[i].pExpr);
+ sqliteVdbeAddOp(v, OP_AggSet, 0, i, 0, 0);
}
- if( id!=FN_Count && p->pList && p->pList->nExpr>=1 ){
- sqliteExprCode(pParse, p->pList->a[0].pExpr);
- sqliteVdbeAddOp(v, OP_Concat, 1, 0, 0, 0);
+ sqliteVdbeResolveLabel(v, lbl1);
+ }
+ for(i=0; i<pParse->nAgg; i++){
+ Expr *pE;
+ int op;
+ if( !pParse->aAgg[i].isAgg ) continue;
+ pE = pParse->aAgg[i].pExpr;
+ if( pE==0 ){
+ sqliteVdbeAddOp(v, OP_AggIncr, 1, i, 0, 0);
+ continue;
}
- switch( sqliteFuncId(&p->token) ){
- case FN_Count: op = OP_AddImm; p1 = 1; break;
- case FN_Sum: op = OP_Add; p1 = 0; break;
- case FN_Min: op = OP_Min; p1 = 1; break;
- case FN_Max: op = OP_Max; p1 = 0; break;
+ assert( pE->op==TK_AGG_FUNCTION );
+ assert( pE->pList!=0 && pE->pList->nExpr==1 );
+ sqliteExprCode(pParse, pE->pList->a[0].pExpr);
+ sqliteVdbeAddOp(v, OP_AggGet, 0, i, 0, 0);
+ switch( pE->iField ){
+ case FN_Min: op = OP_Min; break;
+ case FN_Max: op = OP_Max; break;
+ case FN_Avg: op = OP_Add; break;
+ case FN_Sum: op = OP_Add; break;
}
- sqliteVdbeAddOp(v, op, p1, 0, 0, 0);
+ sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_AggSet, 0, i, 0, 0);
}
- }else if( eDest==SRT_Table ){
- sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
- }else if( eDest==SRT_Set ){
- sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
- sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
- }else if( eDest==SRT_Mem ){
- sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iBreak, 0, 0);
- }else{
- sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
}
+
/* End the database scan loop.
*/
sqliteWhereEnd(pWInfo);
+ /* If we are processing aggregates, we need to set up a second loop
+ ** over all of the aggregate values and process them.
+ */
+ if( isAgg ){
+ int endagg = sqliteVdbeMakeLabel(v);
+ int startagg;
+ startagg = sqliteVdbeAddOp(v, OP_AggNext, 0, endagg, 0, 0);
+ pParse->useAgg = 1;
+ if( pHaving ){
+ sqliteExprIfFalse(pParse, pHaving, startagg);
+ }
+ if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
+ startagg, endagg) ){
+ return 1;
+ }
+ sqliteVdbeAddOp(v, OP_Goto, 0, startagg, 0, 0);
+ sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, endagg);
+ pParse->useAgg = 0;
+ }
+
/* If there is an ORDER BY clause, then we need to sort the results
** and send them to the callback one by one.
*/
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;
}
*************************************************************************
** 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"
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
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 */
};
#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
*/
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 */
};
/*
int sqliteFuncId(Token*);
int sqliteExprResolveIds(Parse*, IdList*, Expr*);
void sqliteExprResolveInSelect(Parse*, Expr*);
+int sqliteExprAnalyzeAggregates(Parse*, Expr*);
+void sqlitePArseInfoReset(Parse*);
** 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>
{ "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 },
extern void sqliteParserTrace(FILE*, char *);
i = 0;
+ sqliteParseInfoReset(pParse);
pEngine = sqliteParserAlloc((void*(*)(int))malloc);
if( pEngine==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
sqliteDeleteTable(pParse->db, pParse->pNewTable);
pParse->pNewTable = 0;
}
+ sqliteParseInfoReset(pParse);
return nErr;
}
** 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>
*/
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;
p->pFirst = pElem;
p->nElem++;
p->pCurrent = pElem;
+ for(i=0; i<p->nMem; i++){
+ pElem->aMem[i].s.flags = STK_Null;
+ }
return 0;
}
p->pCurrent = pFocus;
}else{
AggInsert(p,"");
- pFocus = p->pCurrent;
+ pFocus = p->pCurrent = p->pFirst;
}
return pFocus;
}
** 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:
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;
}
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;
}
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 * * *
**
** 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;
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 ){
}else{
AggInsert(&p->agg, zKey);
}
+ PopStack(p, 1);
break;
}
# 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}}
# 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}
# 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}
# 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