aboutsummaryrefslogtreecommitdiff
path: root/src/resolve.c
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2008-08-29 02:14:02 +0000
committerdrh <drh@noemail.net>2008-08-29 02:14:02 +0000
commit8b213899e804561fe1df90dc1f9f32a165d563ab (patch)
tree3ec0a887a739708058bd73ff4ed323eb554828f9 /src/resolve.c
parent3fb120cb9d4118e59304dfb9f3abaec7806e8597 (diff)
downloadsqlite-8b213899e804561fe1df90dc1f9f32a165d563ab.tar.gz
sqlite-8b213899e804561fe1df90dc1f9f32a165d563ab.zip
Avoid reevaluating WHERE and ORDER BY expressions that alias terms in the
result set. Ticket #3343. Note that aliased GROUP BY expressions are still evaluated twice. (CVS 5637) FossilOrigin-Name: ab0292caa5887cc1bdc0e8c9d3f3502b83975440
Diffstat (limited to 'src/resolve.c')
-rw-r--r--src/resolve.c92
1 files changed, 67 insertions, 25 deletions
diff --git a/src/resolve.c b/src/resolve.c
index 439fc745f..dfbc3b17a 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -14,13 +14,76 @@
** resolve all identifiers by associating them with a particular
** table and column.
**
-** $Id: resolve.c,v 1.4 2008/08/25 17:23:29 drh Exp $
+** $Id: resolve.c,v 1.5 2008/08/29 02:14:03 drh Exp $
*/
#include "sqliteInt.h"
#include <stdlib.h>
#include <string.h>
/*
+** Turn the pExpr expression into an alias for the iCol-th column of the
+** result set in pEList.
+**
+** If the result set column is a simple column reference, then this routine
+** makes an exact copy. But for any other kind of expression, this
+** routine make a copy of the result set column as the argument to the
+** TK_AS operator. The TK_AS operator causes the expression to be
+** evaluated just once and then reused for each alias.
+**
+** The reason for suppressing the TK_AS term when the expression is a simple
+** column reference is so that the column reference will be recognized as
+** usable by indices within the WHERE clause processing logic.
+**
+** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means
+** that in a GROUP BY clause, the expression is evaluated twice. Hence:
+**
+** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
+**
+** Is equivalent to:
+**
+** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
+**
+** 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...
+*/
+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 "" */
+){
+ Expr *pOrig; /* The iCol-th column of the result set */
+ Expr *pDup; /* Copy of pOrig */
+ sqlite3 *db; /* The database connection */
+
+ assert( iCol>=0 && iCol<pEList->nExpr );
+ pOrig = pEList->a[iCol].pExpr;
+ assert( pOrig!=0 );
+ assert( pOrig->flags & EP_Resolved );
+ db = pParse->db;
+ pDup = sqlite3ExprDup(db, pOrig);
+ if( pDup==0 ) return;
+ if( pDup->op!=TK_COLUMN && zType[0]!='G' ){
+ pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
+ if( pDup==0 ) return;
+ if( pEList->a[iCol].iAlias==0 ){
+ pEList->a[iCol].iAlias = ++pParse->nAlias;
+ }
+ pDup->iTable = pEList->a[iCol].iAlias;
+ }
+ if( pExpr->flags & EP_ExpCollate ){
+ pDup->pColl = pExpr->pColl;
+ pDup->flags |= EP_ExpCollate;
+ }
+ if( pExpr->span.dyn ) sqlite3DbFree(db, (char*)pExpr->span.z);
+ if( pExpr->token.dyn ) sqlite3DbFree(db, (char*)pExpr->token.z);
+ memcpy(pExpr, pDup, sizeof(*pExpr));
+ sqlite3DbFree(db, pDup);
+}
+
+/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
** that name in the set of source tables in pSrcList and make the pExpr
** expression node refer back to that source column. The following changes
@@ -218,7 +281,7 @@ static int lookupName(
for(j=0; j<pEList->nExpr; j++){
char *zAs = pEList->a[j].zName;
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
- Expr *pDup, *pOrig;
+ Expr *pOrig;
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
assert( pExpr->pList==0 );
assert( pExpr->pSelect==0 );
@@ -228,15 +291,7 @@ static int lookupName(
sqlite3DbFree(db, zCol);
return 2;
}
- pDup = sqlite3ExprDup(db, pOrig);
- if( pExpr->flags & EP_ExpCollate ){
- pDup->pColl = pExpr->pColl;
- pDup->flags |= EP_ExpCollate;
- }
- if( pExpr->span.dyn ) sqlite3DbFree(db, (char*)pExpr->span.z);
- if( pExpr->token.dyn ) sqlite3DbFree(db, (char*)pExpr->token.z);
- memcpy(pExpr, pDup, sizeof(*pExpr));
- sqlite3DbFree(db, pDup);
+ resolveAlias(pParse, pEList, j, pExpr, "");
cnt = 1;
pMatch = 0;
assert( zTab==0 && zDb==0 );
@@ -731,24 +786,11 @@ int sqlite3ResolveOrderGroupBy(
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
if( pItem->iCol ){
- Expr *pE;
- CollSeq *pColl;
- int flags;
-
if( pItem->iCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- pE = pItem->pExpr;
- pColl = pE->pColl;
- flags = pE->flags & EP_ExpCollate;
- sqlite3ExprDelete(db, pE);
- pE = sqlite3ExprDup(db, pEList->a[pItem->iCol-1].pExpr);
- pItem->pExpr = pE;
- if( pE && flags ){
- pE->pColl = pColl;
- pE->flags |= flags;
- }
+ resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
}
}
return 0;