Evaluate expressions only once when the same expression is used in both the

result set and in the ORDER BY clause.

FossilOrigin-Name: c2f3bbad778504681b39ab9399a1eb3c1a35ab3f
This commit is contained in:
drh 2015-08-26 14:01:41 +00:00
parent 0576bc59a7
commit 5579d59fb3
8 changed files with 93 additions and 26 deletions

View File

@ -1,5 +1,5 @@
C Refactor\sWith.a.zErr\sinto\sWith.a.zCteErr.\s\sNo\slogic\schanges. C Evaluate\sexpressions\sonly\sonce\swhen\sthe\ssame\sexpression\sis\sused\sin\sboth\sthe\nresult\sset\sand\sin\sthe\sORDER\sBY\sclause.
D 2015-08-26T11:40:11.832 D 2015-08-26T14:01:41.658
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in e2218eb228374422969de7b1680eda6864affcef F Makefile.in e2218eb228374422969de7b1680eda6864affcef
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -289,7 +289,7 @@ F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
F src/date.c 8ec787fed4929d8ccdf6b1bc360fccc3e1d2ca58 F src/date.c 8ec787fed4929d8ccdf6b1bc360fccc3e1d2ca58
F src/dbstat.c f402e77e25089c6003d0c60b3233b9b3947d599a F src/dbstat.c f402e77e25089c6003d0c60b3233b9b3947d599a
F src/delete.c 813be7b5659d7658c8a71b5ae194b45c8f739c8b F src/delete.c 813be7b5659d7658c8a71b5ae194b45c8f739c8b
F src/expr.c 650ac7c4f659980a3315e2aaa02a0d71e87f14a5 F src/expr.c 5944e529891416f482250e16c598d8c26e149eb0
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 3ce33dd49f12c72376cec9adc7a4d8e7111cedcc F src/fkey.c 3ce33dd49f12c72376cec9adc7a4d8e7111cedcc
F src/func.c 824bea430d3a2b7dbc62806ad54da8fdb8ed9e3f F src/func.c 824bea430d3a2b7dbc62806ad54da8fdb8ed9e3f
@ -335,14 +335,14 @@ F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1 F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
F src/printf.c 2bc439ff20a4aad0e0ad50a37a67b5eae7d20edc F src/printf.c 2bc439ff20a4aad0e0ad50a37a67b5eae7d20edc
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 66b2740075fdb8baf90155180d33d9850cbcc976 F src/resolve.c f2ef256786a6435efddd64a632fea89c8be62215
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 50b0f02ec4482f92749e6630de9cd0d175a040e1 F src/select.c b52c80f2b1bdb62491f9ce40eea0c5f80c78d105
F src/shell.c b1f91e60918df3a68efad1e3a11696b9a7e23d23 F src/shell.c b1f91e60918df3a68efad1e3a11696b9a7e23d23
F src/sqlite.h.in 378bebc8fe6a88bade25e5f23b7e6123fdc64b00 F src/sqlite.h.in 378bebc8fe6a88bade25e5f23b7e6123fdc64b00
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h f700e6a9dd1fdcccc9951ab022b366fb66b9e413 F src/sqlite3ext.h f700e6a9dd1fdcccc9951ab022b366fb66b9e413
F src/sqliteInt.h d76e7c90775efeec72ea254a5da0a9f1ddcff765 F src/sqliteInt.h cac6c31a0c7e6aa5572cc97b68d2630034d6d212
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179 F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
@ -394,7 +394,7 @@ F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481 F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
F src/tokenize.c 57cb3720f53f84d811def2069c2b169b6be539a5 F src/tokenize.c 57cb3720f53f84d811def2069c2b169b6be539a5
F src/treeview.c 24950c6a79583016c83c43c8b741b3b79a096ce8 F src/treeview.c 46036cbbceada0836833531b2d963edbca3d9cfa
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
F src/update.c adc8b4b2b6fd2cca2e0d2b803e0cf6956aa3a030 F src/update.c adc8b4b2b6fd2cca2e0d2b803e0cf6956aa3a030
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
@ -899,6 +899,7 @@ F test/orderby5.test 8f08a54836d21fb7c70245360751aedd1c2286fb
F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859 F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
F test/orderby9.test 88a330ea5fc7bed7e407b28beb0d2b79485ae2cc
F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7 F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7
F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799 F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
@ -1379,7 +1380,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 65a8918776aa395009a690fa86bfc7d99eb973f9 P 58ba73630ecc4bc58b03a7962dd45b305ef605ef
R a6b3114ece5e49154c10e2c8118e9866 R 657299463d01da646731fb6b8ab7d9fe
U drh U drh
Z b1a98f3cfbd2154f3cffafb179efca91 Z 83e3936f8767c9500521d2082aa870a1

View File

@ -1 +1 @@
58ba73630ecc4bc58b03a7962dd45b305ef605ef c2f3bbad778504681b39ab9399a1eb3c1a35ab3f

View File

@ -2912,7 +2912,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
} }
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
sqlite3ExprCodeExprList(pParse, pFarg, r1, sqlite3ExprCodeExprList(pParse, pFarg, r1, 0,
SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */ sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */
}else{ }else{
@ -3328,11 +3328,13 @@ int sqlite3ExprCodeExprList(
Parse *pParse, /* Parsing context */ Parse *pParse, /* Parsing context */
ExprList *pList, /* The expression list to be coded */ ExprList *pList, /* The expression list to be coded */
int target, /* Where to write results */ int target, /* Where to write results */
int srcReg, /* Source registers if SQLITE_ECEL_REF */
u8 flags /* SQLITE_ECEL_* flags */ u8 flags /* SQLITE_ECEL_* flags */
){ ){
struct ExprList_item *pItem; struct ExprList_item *pItem;
int i, n; int i, j, n;
u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy; u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy;
Vdbe *v = pParse->pVdbe;
assert( pList!=0 ); assert( pList!=0 );
assert( target>0 ); assert( target>0 );
assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */
@ -3340,13 +3342,14 @@ int sqlite3ExprCodeExprList(
if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR; if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
for(pItem=pList->a, i=0; i<n; i++, pItem++){ for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr; Expr *pExpr = pItem->pExpr;
if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ if( (flags & SQLITE_ECEL_REF)!=0 && (j = pList->a[i].u.x.iOrderByCol)>0 ){
sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
}else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){
sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0);
}else{ }else{
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
if( inReg!=target+i ){ if( inReg!=target+i ){
VdbeOp *pOp; VdbeOp *pOp;
Vdbe *v = pParse->pVdbe;
if( copyOp==OP_Copy if( copyOp==OP_Copy
&& (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy
&& pOp->p1+pOp->p3+1==inReg && pOp->p1+pOp->p3+1==inReg

View File

@ -407,9 +407,9 @@ static int lookupName(
** resolved by the time the WHERE clause is resolved. ** resolved by the time the WHERE clause is resolved.
** **
** The ability to use an output result-set column in the WHERE, GROUP BY, ** The ability to use an output result-set column in the WHERE, GROUP BY,
** or HAVING clauses, or as part of a larger expression in the ORDRE BY ** or HAVING clauses, or as part of a larger expression in the ORDER BY
** clause is not standard SQL. This is a (goofy) SQLite extension, that ** clause is not standard SQL. This is a (goofy) SQLite extension, that
** is supported for backwards compatibility only. TO DO: Issue a warning ** is supported for backwards compatibility only. Hence, we issue a warning
** on sqlite3_log() whenever the capability is used. ** on sqlite3_log() whenever the capability is used.
*/ */
if( (pEList = pNC->pEList)!=0 if( (pEList = pNC->pEList)!=0

View File

@ -496,6 +496,7 @@ static void pushOntoSorter(
SortCtx *pSort, /* Information about the ORDER BY clause */ SortCtx *pSort, /* Information about the ORDER BY clause */
Select *pSelect, /* The whole SELECT statement */ Select *pSelect, /* The whole SELECT statement */
int regData, /* First register holding data to be sorted */ int regData, /* First register holding data to be sorted */
int regOrigData, /* First register holding data before packing */
int nData, /* Number of elements in the data array */ int nData, /* Number of elements in the data array */
int nPrefixReg /* No. of reg prior to regData available for use */ int nPrefixReg /* No. of reg prior to regData available for use */
){ ){
@ -509,6 +510,7 @@ static void pushOntoSorter(
int op; /* Opcode to add sorter record to sorter */ int op; /* Opcode to add sorter record to sorter */
assert( bSeq==0 || bSeq==1 ); assert( bSeq==0 || bSeq==1 );
assert( nData==1 || regData==regOrigData );
if( nPrefixReg ){ if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq ); assert( nPrefixReg==nExpr+bSeq );
regBase = regData - nExpr - bSeq; regBase = regData - nExpr - bSeq;
@ -516,7 +518,8 @@ static void pushOntoSorter(
regBase = pParse->nMem + 1; regBase = pParse->nMem + 1;
pParse->nMem += nBase; pParse->nMem += nBase;
} }
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP); sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData,
SQLITE_ECEL_DUP|SQLITE_ECEL_REF);
if( bSeq ){ if( bSeq ){
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr); sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
} }
@ -726,7 +729,7 @@ static void selectInnerLoop(
}else{ }else{
ecelFlags = 0; ecelFlags = 0;
} }
sqlite3ExprCodeExprList(pParse, pEList, regResult, ecelFlags); sqlite3ExprCodeExprList(pParse, pEList, regResult, 0, ecelFlags);
} }
/* If the DISTINCT keyword was present on the SELECT statement /* If the DISTINCT keyword was present on the SELECT statement
@ -842,7 +845,7 @@ static void selectInnerLoop(
} }
#endif #endif
if( pSort ){ if( pSort ){
pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, 1, nPrefixReg); pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg);
}else{ }else{
int r2 = sqlite3GetTempReg(pParse); int r2 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
@ -868,7 +871,7 @@ static void selectInnerLoop(
** ORDER BY in this case since the order of entries in the set ** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which ** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */ ** case the order does matter */
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg); pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
}else{ }else{
int r1 = sqlite3GetTempReg(pParse); int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1); sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
@ -894,7 +897,7 @@ static void selectInnerLoop(
case SRT_Mem: { case SRT_Mem: {
assert( nResultCol==1 ); assert( nResultCol==1 );
if( pSort ){ if( pSort ){
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg); pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
}else{ }else{
assert( regResult==iParm ); assert( regResult==iParm );
/* The LIMIT clause will jump out of the loop for us */ /* The LIMIT clause will jump out of the loop for us */
@ -908,7 +911,8 @@ static void selectInnerLoop(
testcase( eDest==SRT_Coroutine ); testcase( eDest==SRT_Coroutine );
testcase( eDest==SRT_Output ); testcase( eDest==SRT_Output );
if( pSort ){ if( pSort ){
pushOntoSorter(pParse, pSort, p, regResult, nResultCol, nPrefixReg); pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol,
nPrefixReg);
}else if( eDest==SRT_Coroutine ){ }else if( eDest==SRT_Coroutine ){
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}else{ }else{
@ -4667,7 +4671,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( pList ){ if( pList ){
nArg = pList->nExpr; nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg); regAgg = sqlite3GetTempRange(pParse, nArg);
sqlite3ExprCodeExprList(pParse, pList, regAgg, SQLITE_ECEL_DUP); sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{ }else{
nArg = 0; nArg = 0;
regAgg = 0; regAgg = 0;
@ -5287,7 +5291,7 @@ int sqlite3Select(
} }
regBase = sqlite3GetTempRange(pParse, nCol); regBase = sqlite3GetTempRange(pParse, nCol);
sqlite3ExprCacheClear(pParse); sqlite3ExprCacheClear(pParse);
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0); sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
j = nGroupBy; j = nGroupBy;
for(i=0; i<sAggInfo.nColumn; i++){ for(i=0; i<sAggInfo.nColumn; i++){
struct AggInfo_col *pCol = &sAggInfo.aCol[i]; struct AggInfo_col *pCol = &sAggInfo.aCol[i];

View File

@ -3376,9 +3376,10 @@ void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8);
int sqlite3ExprCodeTemp(Parse*, Expr*, int*); int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
int sqlite3ExprCodeTarget(Parse*, Expr*, int); int sqlite3ExprCodeTarget(Parse*, Expr*, int);
void sqlite3ExprCodeAndCache(Parse*, Expr*, int); void sqlite3ExprCodeAndCache(Parse*, Expr*, int);
int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8); int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
#define SQLITE_ECEL_REF 0x04 /* Use ExprList.u.x.iOrderByCol */
void sqlite3ExprIfTrue(Parse*, Expr*, int, int); void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
void sqlite3ExprIfFalse(Parse*, Expr*, int, int); void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
void sqlite3ExprIfFalseDup(Parse*, Expr*, int, int); void sqlite3ExprIfFalseDup(Parse*, Expr*, int, int);

View File

@ -432,7 +432,13 @@ void sqlite3TreeViewExprList(
}else{ }else{
sqlite3TreeViewLine(pView, "%s", zLabel); sqlite3TreeViewLine(pView, "%s", zLabel);
for(i=0; i<pList->nExpr; i++){ for(i=0; i<pList->nExpr; i++){
int j = pList->a[i].u.x.iOrderByCol;
if( j ){
sqlite3TreeViewPush(pView, 0);
sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
}
sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1); sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
if( j ) sqlite3TreeViewPop(pView);
} }
} }
sqlite3TreeViewPop(pView); sqlite3TreeViewPop(pView);

52
test/orderby9.test Normal file
View File

@ -0,0 +1,52 @@
# 2015-08-26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file seeks to verify that expressions (and especially functions)
# that are in both the ORDER BY clause and the result set are only
# evaluated once.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix orderby9
do_execsql_test setup {
-- create a table with many entries
CREATE TABLE t1(x);
WITH RECURSIVE
c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
INSERT INTO t1 SELECT x FROM c;
}
do_test 1.0 {
set l1 {}
# If random() is only evaluated once and then reused for each row, then
# the output should appear in sorted order. If random() is evaluated
# separately for the result set and the ORDER BY clause, then the output
# order will be random.
db eval {SELECT random() AS y FROM t1 ORDER BY 1;} {lappend l1 $y}
expr {$l1==[lsort -int $l1]}
} {1}
do_test 1.1 {
set l1 {}
db eval {SELECT random() AS y FROM t1 ORDER BY random();} {lappend l1 $y}
expr {$l1==[lsort -int $l1]}
} {1}
do_test 1.2 {
set l1 {}
db eval {SELECT random() AS y FROM t1 ORDER BY +random();} {lappend l1 $y}
expr {$l1==[lsort -int $l1]}
} {0}
finish_test