From 678ccce8b35a0abb69bbcd4b7ceccf9d5400f395 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 31 Mar 2008 18:19:54 +0000 Subject: [PATCH] Factor constant subexpressions out of loops. (CVS 4942) FossilOrigin-Name: 2126db39854c751aea6c95c67894ed9b9dfc0763 --- manifest | 20 +++++++++---------- manifest.uuid | 2 +- src/expr.c | 53 +++++++++++++++++++++++++++++++++++++++++-------- src/sqliteInt.h | 23 +++++++++++---------- src/vdbe.c | 4 ++-- src/where.c | 32 +++++++++++++++++++---------- 6 files changed, 92 insertions(+), 42 deletions(-) diff --git a/manifest b/manifest index b12265afe7..34311a79cb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\ssetQuotedToken(),\sonly\smake\sa\smalloced\scopy\sif\sthe\sargument\scontains\sone\sor\smore\s"\scharacters.\s(CVS\s4941) -D 2008-03-31T17:41:18 +C Factor\sconstant\ssubexpressions\sout\sof\sloops.\s(CVS\s4942) +D 2008-03-31T18:19:54 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7 F Makefile.in cf434ce8ca902e69126ae0f94fc9f7dc7428a5fa F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -95,7 +95,7 @@ F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131 F src/date.c e41ce4513fb0e359dc678d6bddb4ace135fe365d F src/delete.c 3dc7d7cc46c8675219a7776b7c67b626bba530df F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b -F src/expr.c 7e56d2a24af8137f4bebbfa1d7dd1dcf70107c88 +F src/expr.c d005510999ccc68301167fd3fcff21f133389379 F src/fault.c 83057e86815d473e526f7df0b0108dfdd022ff23 F src/func.c c9e8c7ff4c45027edee89bde7adbf86a3a3b2afe F src/hash.c 53655c312280211444bfe23af6490a460aec2980 @@ -140,7 +140,7 @@ F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c 22297fffa6f00a6c6d44020fa13b1184a1bb372d F src/sqlite.h.in b1ac824d9fc163a5d2226ebf5990b09a02a11117 F src/sqlite3ext.h faacd0e6a81aabee0861c6d7883c9172e74ef5b3 -F src/sqliteInt.h db668a07004d53a47c5d570963842489c6c4c3f3 +F src/sqliteInt.h 4d25edd2adb5f4be4c2b44c25533e0bbe69eb6e0 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/table.c 2c48c575dd59b3a6c5c306bc55f51a9402cf429a F src/tclsqlite.c 1367762764772a233643524c3585b4711a9adcda @@ -174,7 +174,7 @@ F src/update.c 2aefd3c9277792e9fa2414dfe14202119fa49fe7 F src/utf.c 8c94fa10efc78c2568d08d436acc59df4df7191b F src/util.c dba9e04121eb17ec4643d6ca231ff859452cf0e2 F src/vacuum.c 3524411bfb58aac0d87eadd3e5b7cd532772af30 -F src/vdbe.c 09dd6f4c5e69f17f7633d22e3f45edbafe83eeaf +F src/vdbe.c 07f500db3880a8555f7d76ef5f3cc405725ae9b8 F src/vdbe.h f72201a0657d5f3d6cc008d1f8d9cc65768518c9 F src/vdbeInt.h 0b96efdeecb0803e504bf1c16b198f87c91d6019 F src/vdbeapi.c ab6e99f8a6b7fcb82c2c698da7a36762a7593f0a @@ -183,7 +183,7 @@ F src/vdbeblob.c cc713c142c3d4952b380c98ee035f850830ddbdb F src/vdbefifo.c a30c237b2a3577e1415fb6e288cbb6b8ed1e5736 F src/vdbemem.c b96fea7b98cc662ad7b587f28560b0c5649ac39f F src/vtab.c 00cd16317b29495c185ff40e4b227917d5a371b2 -F src/where.c dc146152cd0eb3b3568e92e7fed55cd008ebb9b5 +F src/where.c 902ddf1e7a737d19324d02de755fc816cc9cdba7 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/all.test d12210212bada2bde6d5aeb90969b86c1aa977d2 @@ -619,7 +619,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P 618df68b8b78d376d30cea79a273fd39140f5033 -R 90b5f03ff5ebb83c1c1b0247d291a8ab -U danielk1977 -Z 3148119d5b8a2206c0e723ccd87c91b1 +P b266924b8975b69bdb9ab45cf462e41436f89cc2 +R 2c294fffeaf40038acaaca80e4e177dd +U drh +Z 39a34de4a0aac903f6ed893e46452e8f diff --git a/manifest.uuid b/manifest.uuid index 38a93cdbda..1be4929771 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b266924b8975b69bdb9ab45cf462e41436f89cc2 \ No newline at end of file +2126db39854c751aea6c95c67894ed9b9dfc0763 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index d35b3cd8e1..2fe48a5ff0 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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.357 2008/03/25 09:47:35 danielk1977 Exp $ +** $Id: expr.c,v 1.358 2008/03/31 18:19:54 drh Exp $ */ #include "sqliteInt.h" #include @@ -786,7 +786,8 @@ void sqlite3ExprListDelete(ExprList *pList){ } /* -** Walk an expression tree. Call xFunc for each node visited. +** Walk an expression tree. Call xFunc for each node visited. xFunc +** is called on the node before xFunc is called on the nodes children. ** ** The return value from xFunc determines whether the tree walk continues. ** 0 means continue walking the tree. 1 means do not walk children @@ -1932,13 +1933,13 @@ void sqlite3ExprCodeGetColumn( ** must check the return code and move the results to the desired ** register. */ -static int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ +int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ Vdbe *v = pParse->pVdbe; /* The VM under construction */ int op; /* The opcode being coded */ int inReg = target; /* Results stored in register inReg */ int regFree1 = 0; /* If non-zero free this temporary register */ int regFree2 = 0; /* If non-zero free this temporary register */ - int r1, r2, r3; /* Various register numbers */ + int r1, r2, r3, r4; /* Various register numbers */ assert( v!=0 || pParse->db->mallocFailed ); assert( target>0 && target<=pParse->nMem ); @@ -2227,7 +2228,7 @@ static int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ j2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, j1); if( eType==IN_INDEX_ROWID ){ - j3 = sqlite3VdbeAddOp3(v, OP_MustBeInt, r1, 0, 1); + j3 = sqlite3VdbeAddOp1(v, OP_MustBeInt, r1); j4 = sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, 0, r1); j5 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, j3); @@ -2262,15 +2263,17 @@ static int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); r3 = sqlite3GetTempReg(pParse); + r4 = sqlite3GetTempReg(pParse); codeCompare(pParse, pLeft, pRight, OP_Ge, r1, r2, r3, SQLITE_STOREP2); pLItem++; pRight = pLItem->pExpr; sqlite3ReleaseTempReg(pParse, regFree2); r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); - codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r2, SQLITE_STOREP2); - sqlite3VdbeAddOp3(v, OP_And, r3, r2, target); + codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2); + sqlite3VdbeAddOp3(v, OP_And, r3, r4, target); sqlite3ReleaseTempReg(pParse, r3); + sqlite3ReleaseTempReg(pParse, r4); break; } case TK_UPLUS: { @@ -2322,6 +2325,7 @@ static int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ cacheX = *pX; cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1); cacheX.op = TK_REGISTER; + cacheX.iColumn = 0; opCompare.op = TK_EQ; opCompare.pLeft = &cacheX; pTest = &opCompare; @@ -2381,7 +2385,7 @@ static int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ ** are stored. ** ** If the register is a temporary register that can be deallocated, -** then write its number into *pReg. If the result register is no +** then write its number into *pReg. If the result register is not ** a temporary, then set *pReg to zero. */ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ @@ -2435,11 +2439,44 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ iMem = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); pExpr->iTable = iMem; + pExpr->iColumn = pExpr->op; pExpr->op = TK_REGISTER; } return inReg; } +/* +** If pExpr is a constant expression, then evaluate the expression +** into a register and convert the expression into a TK_REGISTER +** expression. +*/ +static int evalConstExpr(void *pArg, Expr *pExpr){ + Parse *pParse = (Parse*)pArg; + if( pExpr->op==TK_REGISTER ){ + return 1; + } + if( sqlite3ExprIsConstantNotJoin(pExpr) ){ + int r1 = ++pParse->nMem; + int r2; + r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); + if( r1!=r2 ) pParse->nMem--; + pExpr->iColumn = pExpr->op; + pExpr->op = TK_REGISTER; + pExpr->iTable = r2; + return 1; + } + return 0; +} + +/* +** Preevaluate constant subexpressions within pExpr and store the +** results in registers. Modify pExpr so that the constant subexpresions +** are TK_REGISTER opcodes that refer to the precomputed values. +*/ +void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){ + walkExprTree(pExpr, evalConstExpr, pParse); +} + /* ** Generate code that pushes the value of every element of the given diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1b94f61a06..308ed3e692 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.681 2008/03/26 12:46:24 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.682 2008/03/31 18:19:54 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1130,15 +1130,16 @@ struct Expr { /* ** The following are the meanings of bits in the Expr.flags field. */ -#define EP_FromJoin 0x01 /* Originated in ON or USING clause of a join */ -#define EP_Agg 0x02 /* Contains one or more aggregate functions */ -#define EP_Resolved 0x04 /* IDs have been resolved to COLUMNs */ -#define EP_Error 0x08 /* Expression contains one or more errors */ -#define EP_Distinct 0x10 /* Aggregate function with DISTINCT keyword */ -#define EP_VarSelect 0x20 /* pSelect is correlated, not constant */ -#define EP_Dequoted 0x40 /* True if the string has been dequoted */ -#define EP_InfixFunc 0x80 /* True for an infix function: LIKE, GLOB, etc */ -#define EP_ExpCollate 0x100 /* Collating sequence specified explicitly */ +#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */ +#define EP_Agg 0x0002 /* Contains one or more aggregate functions */ +#define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */ +#define EP_Error 0x0008 /* Expression contains one or more errors */ +#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */ +#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */ +#define EP_Dequoted 0x0040 /* True if the string has been dequoted */ +#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */ +#define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */ +#define EP_Constant 0x0200 /* A constant expression */ /* ** These macros can be used to test, set, or clear bits in the @@ -1831,7 +1832,9 @@ void sqlite3WhereEnd(WhereInfo*); void sqlite3ExprCodeGetColumn(Vdbe*, Table*, int, int, int); int sqlite3ExprCode(Parse*, Expr*, int); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); +int sqlite3ExprCodeTarget(Parse*, Expr*, int); int sqlite3ExprCodeAndCache(Parse*, Expr*, int); +void sqlite3ExprCodeConstants(Parse*, Expr*); int sqlite3ExprCodeExprList(Parse*, ExprList*, int); void sqlite3ExprIfTrue(Parse*, Expr*, int, int); void sqlite3ExprIfFalse(Parse*, Expr*, int, int); diff --git a/src/vdbe.c b/src/vdbe.c index b27c635d56..f969bbcd56 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** 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.722 2008/03/28 19:16:34 danielk1977 Exp $ +** $Id: vdbe.c,v 1.723 2008/03/31 18:19:54 drh Exp $ */ #include "sqliteInt.h" #include @@ -103,7 +103,7 @@ static void updateMaxBlobsize(Mem *p){ ** Test a register to see if it exceeds the current maximum blob size. ** If it does, record the new maximum blob size. */ -#ifdef SQLITE_TEST +#if defined(SQLITE_TEST) && !defined(SQLITE_OMIT_BUILTIN_TEST) # define UPDATE_MAX_BLOBSIZE(P) updateMaxBlobsize(P) #else # define UPDATE_MAX_BLOBSIZE(P) diff --git a/src/where.c b/src/where.c index 1d8b50c87d..d0643f7e0e 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.294 2008/03/28 19:16:57 danielk1977 Exp $ +** $Id: where.c,v 1.295 2008/03/31 18:19:54 drh Exp $ */ #include "sqliteInt.h" @@ -535,7 +535,8 @@ static int isLikeOrGlob( #endif pList = pExpr->pList; pRight = pList->a[0].pExpr; - if( pRight->op!=TK_STRING ){ + if( pRight->op!=TK_STRING + && (pRight->op!=TK_REGISTER || pRight->iColumn!=TK_STRING) ){ return 0; } pLeft = pList->a[1].pExpr; @@ -1752,19 +1753,23 @@ static void buildIndexProbe( ** result is left on the stack. For constraints of the form X IN (...) ** this routine sets up a loop that will iterate over all values of X. */ -static void codeEqualityTerm( +static int codeEqualityTerm( Parse *pParse, /* The parsing context */ WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ WhereLevel *pLevel, /* When level of the FROM clause we are working on */ - int iReg /* Leave results in this register */ + int iTarget /* Attempt to leave results in this register */ ){ Expr *pX = pTerm->pExpr; Vdbe *v = pParse->pVdbe; + int iReg; /* Register holding results */ - assert( iReg>0 && iReg<=pParse->nMem ); + if( iTarget<=0 ){ + iReg = iTarget = sqlite3GetTempReg(pParse); + } if( pX->op==TK_EQ ){ - sqlite3ExprCode(pParse, pX->pRight, iReg); + iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); }else if( pX->op==TK_ISNULL ){ + iReg = iTarget; sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); #ifndef SQLITE_OMIT_SUBQUERY }else{ @@ -1773,6 +1778,7 @@ static void codeEqualityTerm( struct InLoop *pIn; assert( pX->op==TK_IN ); + iReg = iTarget; eType = sqlite3FindInIndex(pParse, pX, 1); iTab = pX->iTable; sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); @@ -1799,6 +1805,7 @@ static void codeEqualityTerm( #endif } disableTerm(pLevel, pTerm); + return iReg; } /* @@ -1852,11 +1859,15 @@ static int codeAllEqualityTerms( */ assert( pIdx->nColumn>=nEq ); for(j=0; jaiColumn[j]; pTerm = findTerm(pWC, iCur, k, notReady, pLevel->flags, pIdx); if( pTerm==0 ) break; assert( (pTerm->flags & TERM_CODED)==0 ); - codeEqualityTerm(pParse, pTerm, pLevel, regBase+j); + r1 = codeEqualityTerm(pParse, pTerm, pLevel, regBase+j); + if( r1!=regBase+j ){ + sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); + } if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->brk); } @@ -2022,6 +2033,7 @@ WhereInfo *sqlite3WhereBegin( */ initMaskSet(&maskSet); whereClauseInit(&wc, pParse, &maskSet); + sqlite3ExprCodeConstants(pParse, pWhere); whereSplit(&wc, pWhere, TK_AND); /* Allocate and initialize the WhereInfo structure that will become the @@ -2367,13 +2379,11 @@ WhereInfo *sqlite3WhereBegin( assert( pTerm->pExpr!=0 ); assert( pTerm->leftCursor==iCur ); assert( omitTable==0 ); - r1 = sqlite3GetTempReg(pParse); - codeEqualityTerm(pParse, pTerm, pLevel, r1); + r1 = codeEqualityTerm(pParse, pTerm, pLevel, 0); nxt = pLevel->nxt; - sqlite3VdbeAddOp3(v, OP_MustBeInt, r1, nxt, 1); + sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, nxt); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, nxt, r1); VdbeComment((v, "pk")); - sqlite3ReleaseTempReg(pParse, r1); pLevel->op = OP_Noop; }else if( pLevel->flags & WHERE_ROWID_RANGE ){ /* Case 2: We have an inequality comparison against the ROWID field.