diff --git a/manifest b/manifest index 4671dd0433..09414718bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\schanges\sfor\sthe\snew\ssqlite3_win32_set_directory\sAPI\sto\strunk. -D 2012-08-23T22:45:34.016 +C Merge\sthe\snested\saggregate\squery\senhancements\sinto\strunk. +D 2012-08-24T01:07:52.214 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in abd5c10d21d1395f140d9e50ea999df8fa4d6376 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -132,7 +132,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 500d019da966631ad957c37705642be87524463b F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 4c20ea4f6213b3bc1c6a510586864b679946e05e -F src/expr.c e2927abf9c69ce4ff9a931bd201946961c34819a +F src/expr.c 217840a107dcc1e5dbb57cea311daad04bedbb9a F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/func.c 18dfedfb857e100b05755a1b12e88b389f957879 @@ -174,13 +174,13 @@ F src/pragma.c 97f9357f0e7e5fb46a2519f14539550aa07db49f F src/prepare.c 33291b83cca285718048d219c67b8298501fa3a5 F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c b3c70ab28cac60de33684c9aa9e5138dcf71d6dd +F src/resolve.c 9e28280ec98035f31900fdd1db01f86f68ca6c32 F src/rowset.c f6a49f3e9579428024662f6e2931832511f831a1 -F src/select.c a365da6d7a6d7d8a10ad60ca71837ab5e9369466 +F src/select.c 2c0291db072924cace54aadbff1996297e9b8de0 F src/shell.c 076e1c90d594644f36027c8ecff9a392cf2d3a06 F src/sqlite.h.in f664797c68ced43c2ea2c541d4ec8e1e04ec68ac F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h c8169801f8bbfdf5873cc6fa45cb5df720c04db4 +F src/sqliteInt.h f4748d18114510f61e6de8bac0d513dc76e8f21c F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -249,10 +249,11 @@ F src/vdbetrace.c 8bd5da325fc90f28464335e4cc4ad1407fe30835 F src/vtab.c bb8ea3a26608bb1357538a5d2fc72beba6638998 F src/wal.c 9294df6f96aae5909ae1a9b733fd1e1b4736978b F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 -F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f +F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b F src/where.c 24c7494d8875ead994b4dfe5461340c27fd424ca F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 +F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 52fc8dee494092031a556911d404ca30a749a30b F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748 @@ -1011,7 +1012,7 @@ F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9 -P b1dbf490869d7fc55ce797cf80cf3bf7141d2d15 1246f15b146ebf6518fb8f5c92a1ebc9495cd9dc -R 35479bceaf9a1b66b4c629b32ba58687 -U mistachkin -Z e3d10f82918ee1a10410c8802031890d +P 20f184f2d5908badd9d44d4fe2ad7c9e182c8803 00b1dc71be4c3420730b5f7840af824ea86165e7 +R c1fab8e17aa8935c76d7626b4d7fe139 +U drh +Z 3e9a3890be18a0a197379668b716e1a3 diff --git a/manifest.uuid b/manifest.uuid index a717c97ba2..78d216df97 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -20f184f2d5908badd9d44d4fe2ad7c9e182c8803 \ No newline at end of file +d4cd6017c9875947a05b1dc36538d4272fb18739 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 790aa45f3b..89172f94bf 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3123,9 +3123,12 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ }else{ pFarg = pExpr->x.pList; } - sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(", - op==TK_AGG_FUNCTION ? "AGG_" : "", - pExpr->u.zToken); + if( op==TK_AGG_FUNCTION ){ + sqlite3ExplainPrintf(pOut, "AGG_FUNCTION%d:%s(", + pExpr->op2, pExpr->u.zToken); + }else{ + sqlite3ExplainPrintf(pOut, "FUNCTION:%s(", pExpr->u.zToken); + } if( pFarg ){ sqlite3ExplainExprList(pOut, pFarg); } @@ -3816,38 +3819,60 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){ } /* -** This is the expression callback for sqlite3FunctionUsesOtherSrc(). -** -** Determine if an expression references any table other than one of the -** tables in pWalker->u.pSrcList and abort if it does. +** An instance of the following structure is used by the tree walker +** to count references to table columns in the arguments of an +** aggregate function, in order to implement the +** sqlite3FunctionThisSrc() routine. */ -static int exprUsesOtherSrc(Walker *pWalker, Expr *pExpr){ - if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ +struct SrcCount { + SrcList *pSrc; /* One particular FROM clause in a nested query */ + int nThis; /* Number of references to columns in pSrcList */ + int nOther; /* Number of references to columns in other FROM clauses */ +}; + +/* +** Count the number of references to columns. +*/ +static int exprSrcCount(Walker *pWalker, Expr *pExpr){ + /* The NEVER() on the second term is because sqlite3FunctionUsesThisSrc() + ** is always called before sqlite3ExprAnalyzeAggregates() and so the + ** TK_COLUMNs have not yet been converted into TK_AGG_COLUMN. If + ** sqlite3FunctionUsesThisSrc() is used differently in the future, the + ** NEVER() will need to be removed. */ + if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){ int i; - SrcList *pSrc = pWalker->u.pSrcList; + struct SrcCount *p = pWalker->u.pSrcCount; + SrcList *pSrc = p->pSrc; for(i=0; inSrc; i++){ - if( pExpr->iTable==pSrc->a[i].iCursor ) return WRC_Continue; + if( pExpr->iTable==pSrc->a[i].iCursor ) break; + } + if( inSrc ){ + p->nThis++; + }else{ + p->nOther++; } - return WRC_Abort; - }else{ - return WRC_Continue; } + return WRC_Continue; } /* -** Determine if any of the arguments to the pExpr Function references -** any SrcList other than pSrcList. Return true if they do. Return -** false if pExpr has no argument or has only constant arguments or -** only references tables named in pSrcList. +** Determine if any of the arguments to the pExpr Function reference +** pSrcList. Return true if they do. Also return true if the function +** has no arguments or has only constant arguments. Return false if pExpr +** references columns but not columns of tables found in pSrcList. */ -static int sqlite3FunctionUsesOtherSrc(Expr *pExpr, SrcList *pSrcList){ +int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ Walker w; + struct SrcCount cnt; assert( pExpr->op==TK_AGG_FUNCTION ); memset(&w, 0, sizeof(w)); - w.xExprCallback = exprUsesOtherSrc; - w.u.pSrcList = pSrcList; - if( sqlite3WalkExprList(&w, pExpr->x.pList)!=WRC_Continue ) return 1; - return 0; + w.xExprCallback = exprSrcCount; + w.u.pSrcCount = &cnt; + cnt.pSrc = pSrcList; + cnt.nThis = 0; + cnt.nOther = 0; + sqlite3WalkExprList(&w, pExpr->x.pList); + return cnt.nThis>0 || cnt.nOther==0; } /* @@ -3966,7 +3991,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ } case TK_AGG_FUNCTION: { if( (pNC->ncFlags & NC_InAggFunc)==0 - && !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList) + && pWalker->walkerDepth==pExpr->op2 ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure diff --git a/src/resolve.c b/src/resolve.c index a66f88fdc0..b87d231ac2 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -18,6 +18,29 @@ #include #include +/* +** Walk the expression tree pExpr and increase the aggregate function +** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. +** This needs to occur when copying a TK_AGG_FUNCTION node from an +** outer query into an inner subquery. +** +** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..) +** is a helper function - a callback for the tree walker. +*/ +static int incrAggDepth(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.i; + return WRC_Continue; +} +static void incrAggFunctionDepth(Expr *pExpr, int N){ + if( N>0 ){ + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = incrAggDepth; + w.u.i = N; + sqlite3WalkExpr(&w, pExpr); + } +} + /* ** Turn the pExpr expression into an alias for the iCol-th column of the ** result set in pEList. @@ -44,13 +67,20 @@ ** The result of random()%5 in the GROUP BY clause is probably different ** from the result in the result-set. We might fix this someday. Or ** then again, we might not... +** +** The nSubquery parameter specifies how many levels of subquery the +** alias is removed from the original expression. The usually value is +** zero but it might be more if the alias is contained within a subquery +** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION +** structures must be increased by the nSubquery amount. */ static void resolveAlias( Parse *pParse, /* Parsing context */ ExprList *pEList, /* A result set */ int iCol, /* A column in the result set. 0..pEList->nExpr-1 */ Expr *pExpr, /* Transform this into an alias to the result set */ - const char *zType /* "GROUP" or "ORDER" or "" */ + const char *zType, /* "GROUP" or "ORDER" or "" */ + int nSubquery /* Number of subqueries that the label is moving */ ){ Expr *pOrig; /* The iCol-th column of the result set */ Expr *pDup; /* Copy of pOrig */ @@ -63,6 +93,7 @@ static void resolveAlias( db = pParse->db; if( pOrig->op!=TK_COLUMN && zType[0]!='G' ){ pDup = sqlite3ExprDup(db, pOrig, 0); + incrAggFunctionDepth(pDup, nSubquery); pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); if( pDup==0 ) return; if( pEList->a[iCol].iAlias==0 ){ @@ -151,9 +182,10 @@ static int lookupName( NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ ){ - int i, j; /* Loop counters */ + int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ int cntTab = 0; /* Number of matching table names */ + int nSubquery = 0; /* How many levels of subquery */ sqlite3 *db = pParse->db; /* The database connection */ struct SrcList_item *pItem; /* Use for looping over pSrcList items */ struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ @@ -315,7 +347,7 @@ static int lookupName( sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; } - resolveAlias(pParse, pEList, j, pExpr, ""); + resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); cnt = 1; pMatch = 0; assert( zTab==0 && zDb==0 ); @@ -329,6 +361,7 @@ static int lookupName( */ if( cnt==0 ){ pNC = pNC->pNext; + nSubquery++; } } @@ -568,13 +601,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ nId, zId); pNC->nErr++; } - if( is_agg ){ - pExpr->op = TK_AGG_FUNCTION; - pNC->ncFlags |= NC_HasAgg; - } if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg; sqlite3WalkExprList(pWalker, pList); - if( is_agg ) pNC->ncFlags |= NC_AllowAgg; + if( is_agg ){ + NameContext *pNC2 = pNC; + pExpr->op = TK_AGG_FUNCTION; + pExpr->op2 = 0; + while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ + pExpr->op2++; + pNC2 = pNC2->pNext; + } + if( pNC2 ) pNC2->ncFlags |= NC_HasAgg; + pNC->ncFlags |= NC_AllowAgg; + } /* FIX ME: Compute pExpr->affinity based on the expected return ** type of the function */ @@ -853,7 +892,7 @@ int sqlite3ResolveOrderGroupBy( resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType); + resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0); } } return 0; diff --git a/src/select.c b/src/select.c index 491356acbb..a4bb2d4969 100644 --- a/src/select.c +++ b/src/select.c @@ -3149,7 +3149,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ if( IsVirtual(pTab) ) return 0; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - if( pAggInfo->nFunc==0 ) return 0; + if( NEVER(pAggInfo->nFunc==0) ) return 0; if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0; if( pExpr->flags&EP_Distinct ) return 0; @@ -3521,7 +3521,7 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ /* -** This routine sets of a SELECT statement for processing. The +** This routine sets up a SELECT statement for processing. The ** following is accomplished: ** ** * VDBE Cursor numbers are assigned to all FROM-clause terms. @@ -3553,7 +3553,8 @@ void sqlite3SelectPrep( ** ** The aggregate accumulator is a set of memory cells that hold ** intermediate results while calculating an aggregate. This -** routine simply stores NULLs in all of those memory cells. +** routine generates code that stores NULLs in all of those memory +** cells. */ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ Vdbe *v = pParse->pVdbe; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b3cdb2363c..443eaa4035 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1690,8 +1690,9 @@ struct Expr { i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 flags2; /* Second set of flags. EP2_... */ - u8 op2; /* If a TK_REGISTER, the original value of Expr.op */ - /* If TK_COLUMN, the value of p5 for OP_Column */ + u8 op2; /* TK_REGISTER: original value of Expr.op + ** TK_COLUMN: the value of p5 for OP_Column + ** TK_AGG_FUNCTION: nesting depth */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. */ #if SQLITE_MAX_EXPR_DEPTH>0 @@ -2498,10 +2499,12 @@ struct Walker { int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ Parse *pParse; /* Parser context. */ + int walkerDepth; /* Number of subqueries */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ SrcList *pSrcList; /* FROM clause */ + struct SrcCount *pSrcCount; /* Counting column references */ } u; }; @@ -2835,6 +2838,7 @@ int sqlite3ExprCompare(Expr*, Expr*); int sqlite3ExprListCompare(ExprList*, ExprList*); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); +int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); Vdbe *sqlite3GetVdbe(Parse*); void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); diff --git a/src/walker.c b/src/walker.c index c95a9c169d..eab96ea24d 100644 --- a/src/walker.c +++ b/src/walker.c @@ -125,12 +125,18 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){ int rc; if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue; rc = WRC_Continue; - while( p ){ + pWalker->walkerDepth++; + while( p ){ rc = pWalker->xSelectCallback(pWalker, p); if( rc ) break; - if( sqlite3WalkSelectExpr(pWalker, p) ) return WRC_Abort; - if( sqlite3WalkSelectFrom(pWalker, p) ) return WRC_Abort; + if( sqlite3WalkSelectExpr(pWalker, p) + || sqlite3WalkSelectFrom(pWalker, p) + ){ + pWalker->walkerDepth--; + return WRC_Abort; + } p = p->pPrior; } + pWalker->walkerDepth--; return rc & WRC_Abort; } diff --git a/test/aggnested.test b/test/aggnested.test new file mode 100644 index 0000000000..22f0fb6b9a --- /dev/null +++ b/test/aggnested.test @@ -0,0 +1,71 @@ +# 2012 August 23 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for processing aggregate queries with +# subqueries in which the subqueries hold the aggregate functions +# or in which the subqueries are themselves aggregate queries +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test aggnested-1.1 { + db eval { + CREATE TABLE t1(a1 INTEGER); + INSERT INTO t1 VALUES(1), (2), (3); + CREATE TABLE t2(b1 INTEGER); + INSERT INTO t2 VALUES(4), (5); + SELECT (SELECT group_concat(a1,'x') FROM t2) FROM t1; + } +} {1x2x3} +do_test aggnested-1.2 { + db eval { + SELECT + (SELECT group_concat(a1,'x') || '-' || group_concat(b1,'y') FROM t2) + FROM t1; + } +} {1x2x3-4y5} +do_test aggnested-1.3 { + db eval { + SELECT (SELECT group_concat(b1,a1) FROM t2) FROM t1; + } +} {415 425 435} +do_test aggnested-1.4 { + db eval { + SELECT (SELECT group_concat(a1,b1) FROM t2) FROM t1; + } +} {151 252 353} + + +# This test case is a copy of the one in +# http://www.mail-archive.com/sqlite-users@sqlite.org/msg70787.html +# +do_test aggnested-2.0 { + sqlite3 db2 :memory: + db2 eval { + CREATE TABLE t1 (A1 INTEGER NOT NULL,A2 INTEGER NOT NULL,A3 INTEGER NOT + NULL,A4 INTEGER NOT NULL,PRIMARY KEY(A1)); + REPLACE INTO t1 VALUES(1,11,111,1111); + REPLACE INTO t1 VALUES(2,22,222,2222); + REPLACE INTO t1 VALUES(3,33,333,3333); + CREATE TABLE t2 (B1 INTEGER NOT NULL,B2 INTEGER NOT NULL,B3 INTEGER NOT + NULL,B4 INTEGER NOT NULL,PRIMARY KEY(B1)); + REPLACE INTO t2 VALUES(1,88,888,8888); + REPLACE INTO t2 VALUES(2,99,999,9999); + SELECT (SELECT GROUP_CONCAT(CASE WHEN a1=1 THEN'A' ELSE 'B' END) FROM t2), + t1.* + FROM t1; + } +} {A,B,B 3 33 333 3333} +db2 close + +finish_test