Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improved handling of aggregate subqueries within an aggregate query. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
430bb59d798286a86c351de92c429345 |
User & Date: | drh 2012-04-17 16:38:53 |
References
2012-11-02
| ||
12:58 | • Ticket [bfbf38e5] Segfault on a nested join status still Open with 3 other changes (artifact: 23d9feb9 user: drh) | |
2012-05-21
| ||
04:31 | • Ticket [c2ad16f9] Segfault on query involving deeply nested aggregate views status still Open with 3 other changes (artifact: 956d8d76 user: mistachkin) | |
Context
2012-04-18
| ||
09:59 | Fix a typecast problem in lemon that could cause problems on 64-bit machines. (check-in: 4a5641cc user: drh tags: trunk) | |
05:57 | Import all the latest trunk changes into the WinRT branch. Refactor and/or remove WinCE-specific macros and functions used for file locking to improve clarity of presentation. (check-in: ad5cd15f user: mistachkin tags: winrt) | |
01:41 | Import all the latest trunk changes into the sessions branch. (check-in: 87a0eab5 user: drh tags: sessions) | |
2012-04-17
| ||
16:38 | Improved handling of aggregate subqueries within an aggregate query. (check-in: 430bb59d user: drh tags: trunk) | |
09:09 | Add an undocumented and possibly ephemeral ".breakpoint" command to the command-line shell, to call a no-op routine on which it is convenient to set a symbolic debugger breakpoint. (check-in: 8e2363ad user: drh tags: trunk) | |
Changes
Changes to src/expr.c.
︙ | ︙ | |||
3774 3775 3776 3777 3778 3779 3780 | if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2; if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2; if( ExprHasProperty(pA, EP_IntValue) ){ if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ return 2; } | | | 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 | if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2; if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2; if( ExprHasProperty(pA, EP_IntValue) ){ if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ return 2; } }else if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return 2; } } if( (pA->flags & EP_ExpCollate)!=(pB->flags & EP_ExpCollate) ) return 1; if( (pA->flags & EP_ExpCollate)!=0 && pA->pColl!=pB->pColl ) return 2; |
︙ | ︙ | |||
3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 | Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1; if( sqlite3ExprCompare(pExprA, pExprB) ) return 1; } return 0; } /* ** Add a new element to the pAggInfo->aCol[] array. Return the index of ** the new element. Return a negative number if malloc fails. */ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){ int i; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 | Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1; if( sqlite3ExprCompare(pExprA, pExprB) ) return 1; } return 0; } /* ** 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. */ static int exprUsesOtherSrc(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ int i; SrcList *pSrc = pWalker->u.pSrcList; for(i=0; i<pSrc->nSrc; i++){ if( pExpr->iTable==pSrc->a[i].iCursor ) return WRC_Continue; } return WRC_Abort; }else{ 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. */ static int sqlite3FunctionUsesOtherSrc(Expr *pExpr, SrcList *pSrcList){ Walker w; 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; } /* ** Add a new element to the pAggInfo->aCol[] array. Return the index of ** the new element. Return a negative number if malloc fails. */ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){ int i; |
︙ | ︙ | |||
3926 3927 3928 3929 3930 3931 3932 | break; } /* endif pExpr->iTable==pItem->iCursor */ } /* end loop over pSrcList */ } return WRC_Prune; } case TK_AGG_FUNCTION: { | | < < | 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 | break; } /* endif pExpr->iTable==pItem->iCursor */ } /* end loop over pSrcList */ } return WRC_Prune; } case TK_AGG_FUNCTION: { if( !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList) ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; i<pAggInfo->nFunc; i++, pItem++){ if( sqlite3ExprCompare(pItem->pExpr, pExpr)==0 ){ break; |
︙ | ︙ | |||
3972 3973 3974 3975 3976 3977 3978 | return WRC_Prune; } } } return WRC_Continue; } static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ | < < < < < < < | < > | 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 | return WRC_Prune; } } } return WRC_Continue; } static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ return WRC_Continue; } /* ** 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 sqlite3ResolveExprNames(). */ void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ Walker w; memset(&w, 0, sizeof(w)); w.xExprCallback = analyzeAggregate; w.xSelectCallback = analyzeAggregatesInSelect; w.u.pNC = pNC; assert( pNC->pSrcList!=0 ); sqlite3WalkExpr(&w, pExpr); } |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
2007 2008 2009 2010 2011 2012 2013 | SrcList *pSrcList; /* One or more tables used to resolve names */ ExprList *pEList; /* Optional list of named expressions */ int nRef; /* Number of names resolved by this context */ int nErr; /* Number of errors encountered while resolving names */ u8 allowAgg; /* Aggregate functions allowed here */ u8 hasAgg; /* True if aggregates are seen */ u8 isCheck; /* True if resolving names in a CHECK constraint */ | < | 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 | SrcList *pSrcList; /* One or more tables used to resolve names */ ExprList *pEList; /* Optional list of named expressions */ int nRef; /* Number of names resolved by this context */ int nErr; /* Number of errors encountered while resolving names */ u8 allowAgg; /* Aggregate functions allowed here */ u8 hasAgg; /* True if aggregates are seen */ u8 isCheck; /* True if resolving names in a CHECK constraint */ AggInfo *pAggInfo; /* Information about aggregates at this level */ NameContext *pNext; /* Next outer name context. NULL for outermost */ }; /* ** An instance of the following structure contains all information ** needed to generate code for a single SELECT statement. |
︙ | ︙ | |||
2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 | struct Walker { int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ Parse *pParse; /* Parser context. */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ } u; }; /* Forward declarations */ int sqlite3WalkExpr(Walker*, Expr*); int sqlite3WalkExprList(Walker*, ExprList*); int sqlite3WalkSelect(Walker*, Select*); | > | 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 | struct Walker { int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ Parse *pParse; /* Parser context. */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ SrcList *pSrcList; /* FROM clause */ } u; }; /* Forward declarations */ int sqlite3WalkExpr(Walker*, Expr*); int sqlite3WalkExprList(Walker*, ExprList*); int sqlite3WalkSelect(Walker*, Select*); |
︙ | ︙ |
Changes to test/subquery.test.
︙ | ︙ | |||
326 327 328 329 330 331 332 333 334 335 336 337 338 339 | } } {1 one 2 two} do_test subquery-3.3.5 { execsql { SELECT a, (SELECT count(*) FROM t2 WHERE a=c) FROM t1; } } {1 1 2 1} #------------------------------------------------------------------ # These tests - subquery-4.* - use the TCL statement cache to try # and expose bugs to do with re-using statements that have been # passed to sqlite3_reset(). # # One problem was that VDBE memory cells were not being initialised | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | } } {1 one 2 two} do_test subquery-3.3.5 { execsql { SELECT a, (SELECT count(*) FROM t2 WHERE a=c) FROM t1; } } {1 1 2 1} # The following tests check for aggregate subqueries in an aggregate # query. # do_test subquery-3.4.1 { execsql { CREATE TABLE t34(x,y); INSERT INTO t34 VALUES(106,4), (107,3), (106,5), (107,5); SELECT a.x, avg(a.y) FROM t34 AS a GROUP BY a.x HAVING NOT EXISTS( SELECT b.x, avg(b.y) FROM t34 AS b GROUP BY b.x HAVING avg(a.y) > avg(b.y)); } } {107 4.0} do_test subquery-3.4.2 { execsql { SELECT a.x, avg(a.y) AS avg1 FROM t34 AS a GROUP BY a.x HAVING NOT EXISTS( SELECT b.x, avg(b.y) AS avg2 FROM t34 AS b GROUP BY b.x HAVING avg1 > avg2); } } {107 4.0} do_test subquery-3.4.3 { execsql { SELECT a.x, avg(a.y), NOT EXISTS ( SELECT b.x, avg(b.y) FROM t34 AS b GROUP BY b.x HAVING avg(a.y) > avg(b.y)), EXISTS ( SELECT c.x, avg(c.y) FROM t34 AS c GROUP BY c.x HAVING avg(a.y) > avg(c.y)) FROM t34 AS a GROUP BY a.x ORDER BY a.x; } } {106 4.5 0 1 107 4.0 1 0} #------------------------------------------------------------------ # These tests - subquery-4.* - use the TCL statement cache to try # and expose bugs to do with re-using statements that have been # passed to sqlite3_reset(). # # One problem was that VDBE memory cells were not being initialised |
︙ | ︙ |