1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-19 21:43:15 +03:00

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
This commit is contained in:
drh
2008-08-29 02:14:02 +00:00
parent 3fb120cb9d
commit 8b213899e8
8 changed files with 285 additions and 51 deletions

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
** $Id: delete.c,v 1.173 2008/08/22 12:30:52 drh Exp $
** $Id: delete.c,v 1.174 2008/08/29 02:14:03 drh Exp $
*/
#include "sqliteInt.h"
@@ -104,8 +104,8 @@ void sqlite3MaterializeView(
Token viewName;
pWhere = sqlite3ExprDup(db, pWhere);
viewName.z = pView->zName;
viewName.n = strlen(viewName.z);
viewName.z = (u8*)pView->zName;
viewName.n = (unsigned int)strlen((const char*)viewName.z);
pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0);
pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
}

View File

@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.391 2008/08/22 17:34:45 drh Exp $
** $Id: expr.c,v 1.392 2008/08/29 02:14:03 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -701,6 +701,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
pItem->iCol = pOldItem->iCol;
pItem->iAlias = pOldItem->iAlias;
}
return pNew;
}
@@ -829,6 +830,7 @@ ExprList *sqlite3ExprListAppend(
memset(pItem, 0, sizeof(*pItem));
pItem->zName = sqlite3NameFromToken(db, pName);
pItem->pExpr = pExpr;
pItem->iAlias = 0;
}
return pList;
@@ -1664,12 +1666,47 @@ void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){
}
}
/*
** Generate code to store the value of the iAlias-th alias in register
** target. The first time this is called, pExpr is evaluated to compute
** the value of the alias. The value is stored in an auxiliary register
** and the number of that register is returned. On subsequent calls,
** the register number is returned without generating any code.
**
** Note that in order for this to work, code must be generated in the
** same order that it is executed.
**
** Aliases are numbered starting with 1. So iAlias is in the range
** of 1 to pParse->nAlias inclusive.
**
** pParse->aAlias[iAlias-1] records the register number where the value
** of the iAlias-th alias is stored. If zero, that means that the
** alias has not yet been computed.
*/
static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr){
sqlite3 *db = pParse->db;
int iReg;
if( pParse->aAlias==0 ){
pParse->aAlias = sqlite3DbMallocZero(db,
sizeof(pParse->aAlias[0])*pParse->nAlias );
if( db->mallocFailed ) return 0;
}
assert( iAlias>0 && iAlias<=pParse->nAlias );
iReg = pParse->aAlias[iAlias-1];
if( iReg==0 ){
iReg = ++pParse->nMem;
sqlite3ExprCode(pParse, pExpr, iReg);
pParse->aAlias[iAlias-1] = iReg;
}
return iReg;
}
/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
**
** With this routine, there is no guaranteed that results will
** With this routine, there is no guarantee that results will
** be stored in target. The result might be stored in some other
** register if it is convenient to do so. The calling function
** must check the return code and move the results to the desired
@@ -1682,8 +1719,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
int regFree1 = 0; /* If non-zero free this temporary register */
int regFree2 = 0; /* If non-zero free this temporary register */
int r1, r2, r3, r4; /* Various register numbers */
sqlite3 *db;
assert( v!=0 || pParse->db->mallocFailed );
db = pParse->db;
assert( v!=0 || db->mallocFailed );
assert( target>0 && target<=pParse->nMem );
if( v==0 ) return 0;
@@ -1729,7 +1768,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
break;
}
case TK_STRING: {
sqlite3DequoteExpr(pParse->db, pExpr);
sqlite3DequoteExpr(db, pExpr);
sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
(char*)pExpr->token.z, pExpr->token.n);
break;
@@ -1765,6 +1804,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
inReg = pExpr->iTable;
break;
}
case TK_AS: {
inReg = codeAlias(pParse, pExpr->iTable, pExpr->pLeft);
break;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
@@ -1921,7 +1964,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
const char *zId;
int constMask = 0;
int i;
sqlite3 *db = pParse->db;
u8 enc = ENC(db);
CollSeq *pColl = 0;
@@ -1929,7 +1971,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
testcase( op==TK_FUNCTION );
zId = (char*)pExpr->token.z;
nId = pExpr->token.n;
pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, enc, 0);
pDef = sqlite3FindFunction(db, zId, nId, nExpr, enc, 0);
assert( pDef!=0 );
if( pList ){
nExpr = pList->nExpr;
@@ -1966,7 +2008,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
}
if( pDef->needCollSeq ){
if( !pColl ) pColl = pParse->db->pDfltColl;
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
@@ -2206,7 +2248,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
assert( pExpr->iColumn==OE_Rollback ||
pExpr->iColumn == OE_Abort ||
pExpr->iColumn == OE_Fail );
sqlite3DequoteExpr(pParse->db, pExpr);
sqlite3DequoteExpr(db, pExpr);
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0,
(char*)pExpr->token.z, pExpr->token.n);
} else {
@@ -2422,7 +2464,13 @@ int sqlite3ExprCodeExprList(
assert( target>0 );
n = pList->nExpr;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
sqlite3ExprCode(pParse, pItem->pExpr, target+i);
if( pItem->iAlias ){
int iReg = codeAlias(pParse, pItem->iAlias, pItem->pExpr);
Vdbe *v = sqlite3GetVdbe(pParse);
sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target+i);
}else{
sqlite3ExprCode(pParse, pItem->pExpr, target+i);
}
if( doHardCopy ) sqlite3ExprHardCopy(pParse, target, n);
}
return n;
@@ -2811,6 +2859,8 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
testcase( pExpr->op==TK_AGG_COLUMN );
testcase( pExpr->op==TK_COLUMN );
/* Check to see if the column is in one of the tables in the FROM
** clause of the aggregate query */
if( pSrcList ){

View File

@@ -14,12 +14,75 @@
** 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
@@ -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;

View File

@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.761 2008/08/22 17:34:45 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.762 2008/08/29 02:14:03 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -1345,6 +1345,7 @@ struct ExprList {
u8 sortOrder; /* 1 for DESC or 0 for ASC */
u8 done; /* A flag to indicate when processing is finished */
u16 iCol; /* For ORDER BY, column number in result set */
u16 iAlias; /* Index into Parse.aAlias[] for zName */
} *a; /* One entry for each expression */
};
@@ -1681,6 +1682,8 @@ struct Parse {
int nVarExpr; /* Number of used slots in apVarExpr[] */
int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */
Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */
int nAlias; /* Number of aliased result set columns */
int *aAlias; /* Register used to hold aliased result */
u8 explain; /* True if the EXPLAIN flag is found on the query */
Token sErrToken; /* The token at which the error occurred */
Token sNameToken; /* Token with unqualified schema object name */

View File

@@ -15,7 +15,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
** $Id: tokenize.c,v 1.150 2008/08/08 14:19:41 drh Exp $
** $Id: tokenize.c,v 1.151 2008/08/29 02:14:03 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -503,6 +503,7 @@ abort_parse:
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
sqlite3DbFree(db, pParse->apVarExpr);
sqlite3DbFree(db, pParse->aAlias);
if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
pParse->rc = SQLITE_ERROR;
}