-C Add\sthe\s(highly\sexperimental)\somit_readlock\spragma\sthat\sdisables\sthe\suse\r\nof\sreadlocks\son\sread-only\sdatabases\sthat\sare\sconnected\susing\sATTACH.\s(CVS\s2317)
-D 2005-02-06T02:45:42
+C Fix\sfor\scorrelated\ssubqueries\swhere\sthe\sparent\sis\san\saggregate.\sTicket\s#1105.\s(CVS\s2318)
+D 2005-02-08T07:50:41
F Makefile.in d928187101fa3d78426cf48ca30e39d0fb714e57
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f
F src/delete.c 4b94395b52a8f7785acd71135c2ce54f3f5550b3
F src/experimental.c 8cc66b2be6a011055d75ef19ed2584bcfbb585ad
-F src/expr.c 1b6b6b16bcb6a6dcc4a5df451d9e652f84b269ae
+F src/expr.c b9ffd249cef8cd0f2d3681da8daf9a4292dcd005
F src/func.c f096b6771cc0aaa11790aca95773a50a8f74ba73
F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
F src/pragma.c 809b95acd9af67297a5f923a1a57d82179e0ad3a
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
-F src/select.c 6217e1f72cee7e173b21b252fd42a052f3b4decc
+F src/select.c 37dd84fd228db14e9006e3eafb389f08d823502f
F src/shell.c 3cb0ef124ed9cd582ce89aec59ff7c659bc6e61b
F src/sqlite.h.in c85f6bad9ca7de29f505fe886646cfff7df4c55e
-F src/sqliteInt.h 5fa59fd8369ec403bbdf35a9b6fbf7f60bd77cdb
+F src/sqliteInt.h 9a53c5b5c591526e22b89deed38467cb8875b121
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
F src/tclsqlite.c 101994a2c4c0eaa69f1de9bfe4a02167f6049e7d
F src/test1.c feac8a742aca920c8ab18a43b3208ae3a834fe9d
F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
F src/util.c 1b7b9a127b66743ab6cba8d44597aeb570723c99
F src/vacuum.c 4dbe45a5c41674a04ac45a7586031583386ab119
-F src/vdbe.c 5acf43749f44b0813d47f4b1801538f4aaa7ddbb
+F src/vdbe.c d9ec62c9f63768b4d4f8513b25aded8faf2de17b
F src/vdbe.h bb9186484f749a839c6c43953e79a6530253f7cd
F src/vdbeInt.h e80721cd8ff611789e20743eec43363a9fb5a48e
F src/vdbeapi.c 467caa6e6fb9247528b1c7ab9132ae1b4748e8ac
F test/select6.test 6e5a1a70a788cdbe515d1252dd0917d7e9d1d71e
F test/select7.test 8f3362336c10d828ab6fe9c1b8897b484da8b592
F test/sort.test 312eade533cb3c7667110ccfa6e818db1078fd6c
-F test/subquery.test e607b55276d2536e17e75896cd245ec1c8838f1d
+F test/subquery.test 958bf1752c6b1c5c0b45b243fc74899f2037821e
F test/subselect.test 3f3f7a940dc3195c3139f4d530385cb54665d614
F test/table.test a2a58cae70ef2511cbf27d40fb8f570106a2677e
F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd
-P 515e5033a5482f55e7edb66d69ff3da7e234ff2e
-R 46e8e0ae6e4ce5a599c5b024b5c2e241
-U drh
-Z dd83c9ab3e3ce7d0c16a2d058d1f0afb
+P 2155448d2128119f74241da0ea07d6713b71765c
+R 606c27806bb193ef40535820164d7cb2
+U danielk1977
+Z ab8a5b2917edd9042037cd2b7cc41ebd
-2155448d2128119f74241da0ea07d6713b71765c
\ No newline at end of file
+f0d3ca10c5bccf8fca7143028ebb3e604c0e3f20
\ No newline at end of file
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.192 2005/02/05 12:48:48 danielk1977 Exp $
+** $Id: expr.c,v 1.193 2005/02/08 07:50:41 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
pNew->op = op;
pNew->pLeft = pLeft;
pNew->pRight = pRight;
+ pNew->iAgg = -1;
if( pToken ){
assert( pToken->dyn==0 );
pNew->span = pNew->token = *pToken;
** The return value from this routine is 1 to abandon the tree walk
** and 0 to continue.
*/
+static int walkExprList(ExprList *, int (*)(void *, Expr*), void *);
static int walkExprTree(Expr *pExpr, int (*xFunc)(void*,Expr*), void *pArg){
- ExprList *pList;
int rc;
if( pExpr==0 ) return 0;
rc = (*xFunc)(pArg, pExpr);
if( rc==0 ){
if( walkExprTree(pExpr->pLeft, xFunc, pArg) ) return 1;
if( walkExprTree(pExpr->pRight, xFunc, pArg) ) return 1;
- pList = pExpr->pList;
- if( pList ){
- int i;
- struct ExprList_item *pItem;
- for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
- if( walkExprTree(pItem->pExpr, xFunc, pArg) ) return 1;
- }
- }
+ if( walkExprList(pExpr->pList, xFunc, pArg) ) return 1;
}
return rc>1;
}
+/*
+** Call walkExprTree() for every expression in list p.
+*/
+static int walkExprList(ExprList *p, int (*xFunc)(void *, Expr*), void *pArg){
+ int i;
+ struct ExprList_item *pItem;
+ if( !p ) return 0;
+ for(i=p->nExpr, pItem=p->a; i>0; i--, pItem++){
+ if( walkExprTree(pItem->pExpr, xFunc, pArg) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Call walkExprTree() for every expression in Select p, not including
+** expressions that are part of sub-selects in any FROM clause or the LIMIT
+** or OFFSET expressions..
+*/
+static int walkSelectExpr(Select *p, int (*xFunc)(void *, Expr*), void *pArg){
+ walkExprList(p->pEList, xFunc, pArg);
+ walkExprTree(p->pWhere, xFunc, pArg);
+ walkExprList(p->pGroupBy, xFunc, pArg);
+ walkExprTree(p->pHaving, xFunc, pArg);
+ walkExprList(p->pOrderBy, xFunc, pArg);
+ return 0;
+}
+
+
/*
** This routine is designed as an xFunc for walkExprTree().
**
op = pExpr->op;
switch( op ){
case TK_COLUMN: {
- if( pParse->useAgg ){
- sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg);
+ if( !pParse->fillAgg && pExpr->iAgg>=0 ){
+ sqlite3VdbeAddOp(v, OP_AggGet, pExpr->iAggCtx, pExpr->iAgg);
}else if( pExpr->iColumn>=0 ){
sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn);
#ifndef NDEBUG
static int analyzeAggregate(void *pArg, Expr *pExpr){
int i;
AggExpr *aAgg;
- Parse *pParse = (Parse*)pArg;
+ NameContext *pNC = (NameContext *)pArg;
+ Parse *pParse = pNC->pParse;
+ SrcList *pSrcList = pNC->pSrcList;
switch( pExpr->op ){
case TK_COLUMN: {
- 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->iColumn==pExpr->iColumn ){
- break;
+ for(i=0; pSrcList && i<pSrcList->nSrc; i++){
+ if( pExpr->iTable==pSrcList->a[i].iCursor ){
+ 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->iColumn==pExpr->iColumn ){
+ break;
+ }
+ }
+ if( i>=pParse->nAgg ){
+ i = appendAggInfo(pParse);
+ if( i<0 ) return 1;
+ pParse->aAgg[i].isAgg = 0;
+ pParse->aAgg[i].pExpr = pExpr;
+ }
+ pExpr->iAgg = i;
+ pExpr->iAggCtx = pNC->nDepth;
+ return 1;
}
}
- if( i>=pParse->nAgg ){
- i = appendAggInfo(pParse);
- if( i<0 ) return 1;
- pParse->aAgg[i].isAgg = 0;
- pParse->aAgg[i].pExpr = pExpr;
- }
- pExpr->iAgg = i;
return 1;
}
case TK_AGG_FUNCTION: {
- aAgg = pParse->aAgg;
- for(i=0; i<pParse->nAgg; i++){
- if( !aAgg[i].isAgg ) continue;
- if( sqlite3ExprCompare(aAgg[i].pExpr, pExpr) ){
- break;
+ if( pNC->nDepth==0 ){
+ aAgg = pParse->aAgg;
+ for(i=0; i<pParse->nAgg; i++){
+ if( !aAgg[i].isAgg ) continue;
+ if( sqlite3ExprCompare(aAgg[i].pExpr, pExpr) ){
+ break;
+ }
}
+ if( i>=pParse->nAgg ){
+ u8 enc = pParse->db->enc;
+ i = appendAggInfo(pParse);
+ if( i<0 ) return 1;
+ pParse->aAgg[i].isAgg = 1;
+ pParse->aAgg[i].pExpr = pExpr;
+ pParse->aAgg[i].pFunc = sqlite3FindFunction(pParse->db,
+ pExpr->token.z, pExpr->token.n,
+ pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
+ }
+ pExpr->iAgg = i;
+ return 1;
}
- if( i>=pParse->nAgg ){
- u8 enc = pParse->db->enc;
- i = appendAggInfo(pParse);
- if( i<0 ) return 1;
- pParse->aAgg[i].isAgg = 1;
- pParse->aAgg[i].pExpr = pExpr;
- pParse->aAgg[i].pFunc = sqlite3FindFunction(pParse->db,
- pExpr->token.z, pExpr->token.n,
- pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
- }
- pExpr->iAgg = i;
- return 1;
}
}
+ if( pExpr->pSelect ){
+ pNC->nDepth++;
+ walkSelectExpr(pExpr->pSelect, analyzeAggregate, pNC);
+ pNC->nDepth--;
+ }
return 0;
}
** If errors are seen, leave an error message in zErrMsg and return
** the number of errors.
*/
-int sqlite3ExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
- int nErr = pParse->nErr;
- walkExprTree(pExpr, analyzeAggregate, pParse);
- return pParse->nErr - nErr;
+int sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
+ int nErr = pNC->pParse->nErr;
+ walkExprTree(pExpr, analyzeAggregate, pNC);
+ return pNC->pParse->nErr - nErr;
}
/*
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.239 2005/02/05 12:48:48 danielk1977 Exp $
+** $Id: select.c,v 1.240 2005/02/08 07:50:41 danielk1977 Exp $
*/
#include "sqliteInt.h"
pE->op = TK_COLUMN;
pE->iColumn = iCol;
pE->iTable = iTable;
+ pE->iAgg = -1;
pOrderBy->a[i].done = 1;
}
if( iCol<0 && mustComplete ){
*/
p->isDistinct = p->isDistinct || pSub->isDistinct;
+ /*
+ ** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y;
+ */
if( pSub->pLimit ){
p->pLimit = pSub->pLimit;
pSub->pLimit = 0;
** saveAggregateInfo() and restoreAggregateInfo().
*/
struct AggregateInfo {
- u8 useAgg;
int nAgg;
AggExpr *aAgg;
};
static void saveAggregateInfo(Parse *pParse, AggregateInfo *pInfo){
pInfo->aAgg = pParse->aAgg;
pInfo->nAgg = pParse->nAgg;
- pInfo->useAgg = pParse->useAgg;
pParse->aAgg = 0;
pParse->nAgg = 0;
- pParse->useAgg = 0;
}
/*
sqliteFree(pParse->aAgg);
pParse->aAgg = pInfo->aAgg;
pParse->nAgg = pInfo->nAgg;
- pParse->useAgg = pInfo->useAgg;
}
/*
/* Do an analysis of aggregate expressions.
*/
if( isAgg || pGroupBy ){
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ sNC.pSrcList = pTabList;
+
assert( pParse->nAgg==0 );
isAgg = 1;
for(i=0; i<pEList->nExpr; i++){
- if( sqlite3ExprAnalyzeAggregates(pParse, pEList->a[i].pExpr) ){
+ if( sqlite3ExprAnalyzeAggregates(&sNC, pEList->a[i].pExpr) ){
goto select_end;
}
}
if( pGroupBy ){
for(i=0; i<pGroupBy->nExpr; i++){
- if( sqlite3ExprAnalyzeAggregates(pParse, pGroupBy->a[i].pExpr) ){
+ if( sqlite3ExprAnalyzeAggregates(&sNC, pGroupBy->a[i].pExpr) ){
goto select_end;
}
}
}
- if( pHaving && sqlite3ExprAnalyzeAggregates(pParse, pHaving) ){
+ if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
goto select_end;
}
if( pOrderBy ){
for(i=0; i<pOrderBy->nExpr; i++){
- if( sqlite3ExprAnalyzeAggregates(pParse, pOrderBy->a[i].pExpr) ){
+ if( sqlite3ExprAnalyzeAggregates(&sNC, pOrderBy->a[i].pExpr) ){
goto select_end;
}
}
*/
else{
AggExpr *pAgg;
+ int lbl1 = 0;
+ pParse->fillAgg = 1;
if( pGroupBy ){
- int lbl1;
for(i=0; i<pGroupBy->nExpr; i++){
sqlite3ExprCode(pParse, pGroupBy->a[i].pExpr);
}
sqlite3VdbeAddOp(v, OP_MakeRecord, pGroupBy->nExpr, 0);
lbl1 = sqlite3VdbeMakeLabel(v);
sqlite3VdbeAddOp(v, OP_AggFocus, 0, lbl1);
- for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
- if( pAgg->isAgg ) continue;
- sqlite3ExprCode(pParse, pAgg->pExpr);
- sqlite3VdbeAddOp(v, OP_AggSet, 0, i);
- }
+ }
+ for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
+ if( pAgg->isAgg ) continue;
+ sqlite3ExprCode(pParse, pAgg->pExpr);
+ sqlite3VdbeAddOp(v, OP_AggSet, 0, i);
+ }
+ pParse->fillAgg = 0;
+ if( lbl1<0 ){
sqlite3VdbeResolveLabel(v, lbl1);
}
for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
int endagg = sqlite3VdbeMakeLabel(v);
int startagg;
startagg = sqlite3VdbeAddOp(v, OP_AggNext, 0, endagg);
- pParse->useAgg = 1;
if( pHaving ){
sqlite3ExprIfFalse(pParse, pHaving, startagg, 1);
}
sqlite3VdbeAddOp(v, OP_Goto, 0, startagg);
sqlite3VdbeResolveLabel(v, endagg);
sqlite3VdbeAddOp(v, OP_Noop, 0, 0);
- pParse->useAgg = 0;
}
/* If there is an ORDER BY clause, then we need to sort the results
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.368 2005/02/06 02:45:43 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.369 2005/02/08 07:50:42 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
Token span; /* Complete text of the expression */
int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
** iColumn-th field of the iTable-th table. */
- int iAgg; /* When op==TK_COLUMN and pParse->useAgg==TRUE, pull
+ int iAgg; /* When op==TK_COLUMN and pParse->fillAgg==FALSE, pull
** result from the iAgg-th element of the aggregator */
+ int iAggCtx; /* The value to pass as P1 of OP_AggGet. */
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
};
int cookieValue[MAX_ATTACHED+2]; /* Values of cookies to verify */
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
u32 writeMask; /* Start a write transaction on these databases */
- u8 useAgg; /* If true, extract field values from the aggregator
- ** while generating expressions. Normally false */
+ u8 fillAgg; /* If true, ignore the Expr.iAgg field. Normally false */
/* Above is constant between recursions. Below is reset before and after
** each recursion */
int sqlite3ExprCompare(Expr*, Expr*);
int sqliteFuncId(Token*);
int sqlite3ExprResolveNames(NameContext *, Expr *);
-int sqlite3ExprAnalyzeAggregates(Parse*, Expr*);
+int sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
Vdbe *sqlite3GetVdbe(Parse*);
void sqlite3Randomness(int, void*);
void sqlite3RollbackAll(sqlite3*);
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.453 2005/02/05 12:48:48 danielk1977 Exp $
+** $Id: vdbe.c,v 1.454 2005/02/08 07:50:42 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
break;
}
-/* Opcode: AggGet * P2 *
+/* Opcode: AggGet P1 P2 *
**
** Push a new entry onto the stack which is a copy of the P2-th field
** of the current aggregate. Strings are not duplicated so
** string values will be ephemeral.
+**
+** If P1 is zero, then the value is pulled out of the current aggregate
+** in the current aggregate context. If P1 is greater than zero, then
+** the value is taken from the P1th outer aggregate context. (i.e. if
+** P1==1 then read from the aggregate context that will be restored
+** by the next OP_AggContextPop opcode).
*/
case OP_AggGet: {
AggElem *pFocus;
int i = pOp->p2;
- pFocus = p->pAgg->pCurrent;
+ Agg *pAgg = &p->pAgg[-pOp->p1];
+ assert( pAgg>=p->apAgg );
+ pFocus = pAgg->pCurrent;
if( pFocus==0 ){
int res;
if( sqlite3_malloc_failed ) goto no_mem;
- rc = sqlite3BtreeFirst(p->pAgg->pCsr, &res);
+ rc = sqlite3BtreeFirst(pAgg->pCsr, &res);
if( rc!=SQLITE_OK ){
return rc;
}
if( res!=0 ){
- rc = AggInsert(p->pAgg, "", 1);
- pFocus = p->pAgg->pCurrent;
+ rc = AggInsert(pAgg, "", 1);
+ pFocus = pAgg->pCurrent;
}else{
- rc = sqlite3BtreeData(p->pAgg->pCsr, 0, 4, (char *)&pFocus);
+ rc = sqlite3BtreeData(pAgg->pCsr, 0, 4, (char *)&pFocus);
}
}
- assert( i>=0 && i<p->pAgg->nMem );
+ assert( i>=0 && i<pAgg->nMem );
pTos++;
sqlite3VdbeMemShallowCopy(pTos, &pFocus->aMem[i], MEM_Ephem);
if( pTos->flags&MEM_Str ){
# This file implements regression tests for SQLite library. The
# focus of this script is testing correlated subqueries
#
-# $Id: subquery.test,v 1.4 2005/01/30 11:11:44 danielk1977 Exp $
+# $Id: subquery.test,v 1.5 2005/02/08 07:50:42 danielk1977 Exp $
#
set testdir [file dirname $argv0]
}
} {1}
+# Test Cases subquery-3.3.* test correlated subqueries where the
+# parent query is an aggregate query. Ticket #1105 is an example
+# of such a query.
+#
+do_test subquery-3.3.1 {
+ execsql {
+ SELECT a, (SELECT b) FROM t1 GROUP BY a;
+ }
+} {1 2}
+do_test subquery-3.3.2 {
+ catchsql {DROP TABLE t2}
+ execsql {
+ CREATE TABLE t2(c, d);
+ INSERT INTO t2 VALUES(1, 'one');
+ INSERT INTO t2 VALUES(2, 'two');
+ SELECT a, (SELECT d FROM t2 WHERE a=c) FROM t1 GROUP BY a;
+ }
+} {1 one}
+do_test subquery-3.3.3 {
+ execsql {
+ INSERT INTO t1 VALUES(2, 4);
+ SELECT max(a), (SELECT d FROM t2 WHERE a=c) FROM t1;
+ }
+} {2 two}
+do_test subquery-3.3.3 {
+ execsql {
+ SELECT a, (SELECT (SELECT d FROM t2 WHERE a=c)) FROM t1 GROUP BY a;
+ }
+} {1 one 2 two}
+
#------------------------------------------------------------------
# These tests - subquery-4.* - use the TCL statement cache to try
# and expose bugs to do with re-using statements that have been