From: drh Date: Thu, 28 Jul 2005 20:51:19 +0000 (+0000) Subject: Test cases and tuning of the new optimizer code. (CVS 2567) X-Git-Tag: version-3.6.10~3592 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a611040eea2c2f5e1da2e8f6ba8ae5a834b4ae71;p=thirdparty%2Fsqlite.git Test cases and tuning of the new optimizer code. (CVS 2567) FossilOrigin-Name: 4b02703dec71aa78e5f8d8cab5b950966a4c6abc --- diff --git a/manifest b/manifest index 89e1dd69b3..b3cd0fb55d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\snew\soptimizer\snow\spasses\sall\sregression\stests.\s(CVS\s2566) -D 2005-07-28T16:51:51 +C Test\scases\sand\stuning\sof\sthe\snew\soptimizer\scode.\s(CVS\s2567) +D 2005-07-28T20:51:19 F Makefile.in 22ea9c0fe748f591712d8fe3c6d972c6c173a165 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -78,14 +78,14 @@ F src/update.c a9d2c5f504212d62da1b094476f1389c0e02f83f F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c F src/util.c 668d31be592753e5b8ea00e69ea8d3eedb29fa22 F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c -F src/vdbe.c 0145d877d0e086d5d4bc28f52552883105373163 +F src/vdbe.c 69f33e22c7d0a64b23fbb69e6da95a1bb6869032 F src/vdbe.h 75e466d84d362b0c4498978a9d6b1e6bd32ecf3b F src/vdbeInt.h 9be9a6c43d38124bd03cc5cf05715605b1789fd9 F src/vdbeapi.c 7f392f0792d1258c958083d7de9eae7c3530c9a6 F src/vdbeaux.c 3732a86566a6be4da4c606e9334baf3fd98667af F src/vdbefifo.c b8805850afe13b43f1de78d58088cb5d66f88e1e F src/vdbemem.c da8e8d6f29dd1323f782f000d7cd120027c9ff03 -F src/where.c efed9d45672ea5fc550072272d86e28a2649c256 +F src/where.c 6c3de6ee253256d58ef07acb0f07458605477cd4 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3 F test/alter.test 9d6837a3d946b73df692b7cef2a7644d2e2f6bc6 @@ -225,6 +225,7 @@ F test/vacuum2.test 5d77e98c458bcdbeecc6327de5107179ba1aa095 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/view.test 3c79232a2ee45918c62a0cf90411525899404a76 F test/where.test b6ab0f64adc5fbb4259f284b19da6cd9aeadc711 +F test/where2.test 6427619390385bf4fc357b3907b8733012964a92 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/lemon.c c88936c67f6411608db8fa4254d254f509fa40f6 F tool/lempar.c f0c30abcae762a7d1eb37cd88b2232ab8dd625fb @@ -287,7 +288,7 @@ F www/tclsqlite.tcl 425be741b8ae664f55cb1ef2371aab0a75109cf9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 528299b8316726dbcc5548e9aa0648c8b1bd055b -P ee3a08e353f563c36e904479393fcb56f96ee975 -R 0f7ba5ada7ced4acb01d1d193cdec9c1 +P a21212843359fb9fdbd60799ae50ad3566f4399a +R b3b6fd7089d32b94d5badcc51bbe97b5 U drh -Z 28da42b8f7133daabbc46de020ba7352 +Z 603aecf655d7fb47a89068df78f35d0c diff --git a/manifest.uuid b/manifest.uuid index 9e111afca2..ad2b4e32c8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a21212843359fb9fdbd60799ae50ad3566f4399a \ No newline at end of file +4b02703dec71aa78e5f8d8cab5b950966a4c6abc \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 4215ae9950..b6cdcdc9ad 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.477 2005/07/23 03:18:40 drh Exp $ +** $Id: vdbe.c,v 1.478 2005/07/28 20:51:19 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -454,7 +454,6 @@ int sqlite3VdbeExec( int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ Mem *pTos; /* Top entry in the operand stack */ - char zBuf[100]; /* Space to sprintf() an integer */ #ifdef VDBE_PROFILE unsigned long long start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ @@ -2539,11 +2538,7 @@ case OP_OpenWrite: { /* no-push */ p2 = pTos->i; assert( (pTos->flags & MEM_Dyn)==0 ); pTos--; - if( p2<2 ){ - sqlite3SetString(&p->zErrMsg, "root page number less than 2", (char*)0); - rc = SQLITE_INTERNAL; - break; - } + assert( p2>=2 ); } assert( i>=0 ); pCur = allocateCursor(p, i); @@ -4605,9 +4600,7 @@ case OP_Expire: { /* no-push */ /* An other opcode is illegal... */ default: { - sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",pOp->opcode); - sqlite3SetString(&p->zErrMsg, "unknown opcode ", zBuf, (char*)0); - rc = SQLITE_INTERNAL; + assert( 0 ); break; } @@ -4644,10 +4637,7 @@ default: { if( pTos>=p->aStack ){ sqlite3VdbeMemSanity(pTos, db->enc); } - if( pc<-1 || pc>=p->nOp ){ - sqlite3SetString(&p->zErrMsg, "jump destination out of range", (char*)0); - rc = SQLITE_INTERNAL; - } + assert( pc>=-1 && pcnOp ); #ifdef SQLITE_DEBUG /* Code for tracing the vdbe stack. */ if( p->trace && pTos>=p->aStack ){ diff --git a/src/where.c b/src/where.c index 58bf1d475d..88f96f4235 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.154 2005/07/28 16:51:51 drh Exp $ +** $Id: where.c,v 1.155 2005/07/28 20:51:19 drh Exp $ */ #include "sqliteInt.h" @@ -149,9 +149,7 @@ struct ExprMaskSet { ** terms in the where clause. */ #define WO_IN 1 -#define WO_LIST 2 -#define WO_SELECT 4 -#define WO_EQ 8 +#define WO_EQ 2 #define WO_LT (WO_EQ<<(TK_LT-TK_EQ)) #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) @@ -470,13 +468,6 @@ static void exprAnalyze( pTerm->leftCursor = pLeft->iTable; pTerm->leftColumn = pLeft->iColumn; pTerm->operator = operatorMask(pExpr->op); - if( pTerm->operator==WO_IN ){ - if( pExpr->pSelect ){ - pTerm->operator |= WO_SELECT; - }else if( pExpr->pList ){ - pTerm->operator |= WO_LIST; - } - } } if( pRight && pRight->op==TK_COLUMN ){ WhereTerm *pNew; @@ -649,7 +640,7 @@ static double estLog(double N){ /* ** Find the best index for accessing a particular table. Return a pointer ** to the index, flags that describe how the index should be used, the -** number of equality constraints and the "cost" for this index. +** number of equality constraints, and the "cost" for this index. ** ** The lowest cost index wins. The cost is an estimate of the amount of ** CPU and disk I/O need to process the request using the selected index. @@ -692,6 +683,7 @@ static double bestIndex( */ pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); if( pTerm ){ + Expr *pExpr; *ppIndex = 0; bestFlags = WHERE_ROWID_EQ; if( pTerm->operator & WO_EQ ){ @@ -702,10 +694,10 @@ static double bestIndex( if( pOrderBy ) *pFlags |= WHERE_ORDERBY; TRACE(("... best is rowid\n")); return 0.0; - }else if( pTerm->operator & WO_LIST ){ + }else if( (pExpr = pTerm->pExpr)->pList!=0 ){ /* Rowid IN (LIST): cost is NlogN where N is the number of list ** elements. */ - lowestCost = pTerm->pExpr->pList->nExpr; + lowestCost = pExpr->pList->nExpr; lowestCost *= estLog(lowestCost); }else{ /* Rowid IN (SELECT): cost is NlogN where N is the number of rows @@ -777,11 +769,12 @@ static double bestIndex( if( pTerm==0 ) break; flags |= WHERE_COLUMN_EQ; if( pTerm->operator & WO_IN ){ + Expr *pExpr = pTerm->pExpr; flags |= WHERE_COLUMN_IN; - if( pTerm->operator & WO_SELECT ){ + if( pExpr->pSelect!=0 ){ inMultiplier *= 100.0; - }else if( pTerm->operator & WO_LIST ){ - inMultiplier *= pTerm->pExpr->pList->nExpr + 1.0; + }else if( pExpr->pList!=0 ){ + inMultiplier *= pExpr->pList->nExpr + 1.0; } } } @@ -795,7 +788,7 @@ static double bestIndex( int j = pProbe->aiColumn[nEq]; pTerm = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pProbe); if( pTerm ){ - flags = WHERE_COLUMN_RANGE; + flags |= WHERE_COLUMN_RANGE; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pProbe) ){ flags |= WHERE_TOP_LIMIT; cost *= 0.333; @@ -851,9 +844,7 @@ static double bestIndex( if( cost < lowestCost ){ bestIdx = pProbe; lowestCost = cost; - if( flags==0 ){ - flags = WHERE_COLUMN_RANGE; - } + assert( flags!=0 ); bestFlags = flags; bestNEq = nEq; } @@ -963,6 +954,8 @@ static void codeEqualityTerm( aIn[0] = OP_Next; aIn[1] = iTab; aIn[2] = sqlite3VdbeAddOp(v, OP_Column, iTab, 0); + }else{ + pLevel->nIn = 0; } #endif } @@ -1370,18 +1363,8 @@ WhereInfo *sqlite3WhereBegin( WhereTerm *pStart, *pEnd; assert( omitTable==0 ); - if( pLevel->flags & WHERE_BTM_LIMIT ){ - pStart = findTerm(&wc, iCur, -1, notReady, WO_GT|WO_GE, 0); - assert( pStart!=0 ); - }else{ - pStart = 0; - } - if( pLevel->flags & WHERE_TOP_LIMIT ){ - pEnd = findTerm(&wc, iCur, -1, notReady, WO_LT|WO_LE, 0); - assert( pEnd!=0 ); - }else{ - pEnd = 0; - } + pStart = findTerm(&wc, iCur, -1, notReady, WO_GT|WO_GE, 0); + pEnd = findTerm(&wc, iCur, -1, notReady, WO_LT|WO_LE, 0); if( bRev ){ pTerm = pStart; pStart = pEnd; @@ -1603,18 +1586,11 @@ WhereInfo *sqlite3WhereBegin( /* Case 5: There is no usable index. We must do a complete ** scan of the entire table. */ - int opRewind; - assert( omitTable==0 ); - if( bRev ){ - opRewind = OP_Last; - pLevel->op = OP_Prev; - }else{ - opRewind = OP_Rewind; - pLevel->op = OP_Next; - } + assert( bRev==0 ); + pLevel->op = OP_Next; pLevel->p1 = iCur; - pLevel->p2 = 1 + sqlite3VdbeAddOp(v, opRewind, iCur, brk); + pLevel->p2 = 1 + sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk); } notReady &= ~getMask(&maskSet, iCur); diff --git a/test/where2.test b/test/where2.test new file mode 100644 index 0000000000..ba5dcd2f58 --- /dev/null +++ b/test/where2.test @@ -0,0 +1,207 @@ +# 2005 July 28 +# +# 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. The +# focus of this file is testing the use of indices in WHERE clauses +# based on recent changes to the optimizer. +# +# $Id: where2.test,v 1.1 2005/07/28 20:51:19 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Build some test data +# +do_test where2-1.0 { + execsql { + BEGIN; + CREATE TABLE t1(w int, x int, y int, z int); + } + for {set i 1} {$i<=100} {incr i} { + set w $i + set x [expr {int(log($i)/log(2))}] + set y [expr {$i*$i + 2*$i + 1}] + set z [expr {$x+$y}] + execsql {INSERT INTO t1 VALUES($::w,$::x,$::y,$::z)} + } + execsql { + CREATE UNIQUE INDEX i1w ON t1(w); + CREATE INDEX i1xy ON t1(x,y); + CREATE INDEX i1zyx ON t1(z,y,x); + COMMIT; + } +} {} + +# Do an SQL statement. Append the search count to the end of the result. +# +proc count sql { + set ::sqlite_search_count 0 + return [concat [execsql $sql] $::sqlite_search_count] +} + +# This procedure executes the SQL. Then it checks to see if the OP_Sort +# opcode was executed. If an OP_Sort did occur, then "sort" is appended +# to the result. If no OP_Sort happened, then "nosort" is appended. +# +# This procedure is used to check to make sure sorting is or is not +# occurring as expected. +# +proc cksort {sql} { + set ::sqlite_sort_count 0 + set data [execsql $sql] + if {$::sqlite_sort_count} {set x sort} {set x nosort} + lappend data $x + return $data +} + +# This procedure executes the SQL. Then it appends to the result the +# "sort" or "nosort" keyword (as in the cksort procedure above) then +# it appends the ::sqlite_query_plan variable. +# +proc queryplan {sql} { + set ::sqlite_sort_count 0 + set data [execsql $sql] + if {$::sqlite_sort_count} {set x sort} {set x nosort} + lappend data $x + return [concat $data $::sqlite_query_plan] +} + + +# Prefer a UNIQUE index over another index. +# +do_test where2-1.1 { + queryplan { + SELECT * FROM t1 WHERE w=85 AND x=6 AND y=7396 + } +} {85 6 7396 7402 nosort t1 i1w} + +# Always prefer a rowid== constraint over any other index. +# +do_test where2-1.3 { + queryplan { + SELECT * FROM t1 WHERE w=85 AND x=6 AND y=7396 AND rowid=85 + } +} {85 6 7396 7402 nosort t1 *} + +# When constrained by a UNIQUE index, the ORDER BY clause is always ignored. +# +do_test where2-2.1 { + queryplan { + SELECT * FROM t1 WHERE w=85 ORDER BY random(5); + } +} {85 6 7396 7402 nosort t1 i1w} +do_test where2-2.2 { + queryplan { + SELECT * FROM t1 WHERE x=6 AND y=7396 ORDER BY random(5); + } +} {85 6 7396 7402 sort t1 i1xy} +do_test where2-2.3 { + queryplan { + SELECT * FROM t1 WHERE rowid=85 AND x=6 AND y=7396 ORDER BY random(5); + } +} {85 6 7396 7402 nosort t1 *} + + +# Efficient handling of forward and reverse table scans. +# +do_test where2-3.1 { + queryplan { + SELECT * FROM t1 ORDER BY rowid LIMIT 2 + } +} {1 0 4 4 2 1 9 10 nosort t1 *} +do_test where2-3.2 { + queryplan { + SELECT * FROM t1 ORDER BY rowid DESC LIMIT 2 + } +} {100 6 10201 10207 99 6 10000 10006 nosort t1 *} + +# The IN operator can be used by indices at multiple layers +# +do_test where2-4.1 { + queryplan { + SELECT * FROM t1 WHERE z IN (10207,10006) AND y IN (10000,10201) + AND x>0 AND x<10 + ORDER BY w + } +} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} +do_test where2-4.2 { + queryplan { + SELECT * FROM t1 WHERE z IN (10207,10006) AND y=10000 + AND x>0 AND x<10 + ORDER BY w + } +} {99 6 10000 10006 sort t1 i1zyx} +do_test where2-4.3 { + queryplan { + SELECT * FROM t1 WHERE z=10006 AND y IN (10000,10201) + AND x>0 AND x<10 + ORDER BY w + } +} {99 6 10000 10006 sort t1 i1zyx} +do_test where2-4.4 { + queryplan { + SELECT * FROM t1 WHERE z IN (SELECT 10207 UNION SELECT 10006) + AND y IN (10000,10201) + AND x>0 AND x<10 + ORDER BY w + } +} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} +do_test where2-4.5 { + queryplan { + SELECT * FROM t1 WHERE z IN (SELECT 10207 UNION SELECT 10006) + AND y IN (SELECT 10000 UNION SELECT 10201) + AND x>0 AND x<10 + ORDER BY w + } +} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} +do_test where2-4.6 { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY 2 + } +} {99 6 10000 10006 sort t1 i1xy} + +# Duplicate entires on the RHS of an IN operator do not cause duplicate +# output rows. +# +do_test where2-4.6 { + queryplan { + SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207) + ORDER BY w + } +} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} +do_test where2-4.7 { + queryplan { + SELECT * FROM t1 WHERE z IN ( + SELECT 10207 UNION ALL SELECT 10006 + UNION ALL SELECT 10006 UNION ALL SELECT 10207) + ORDER BY w + } +} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} + +# The use of an IN operator disables the index as a sorter. +# +do_test where2-5.1 { + queryplan { + SELECT * FROM t1 WHERE w=99 ORDER BY w + } +} {99 6 10000 10006 nosort t1 i1w} +do_test where2-5.2 { + queryplan { + SELECT * FROM t1 WHERE w IN (99) ORDER BY w + } +} {99 6 10000 10006 sort t1 i1w} + + +integrity_check {where2-99.0} + +finish_test