From: drh Date: Thu, 20 Jan 2011 02:56:37 +0000 (+0000) Subject: The first of a planned series of enhancements to the query planner that X-Git-Tag: version-3.7.6~166^2~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e847d3247fbc3a7a5804894f60342b6331757bec;p=thirdparty%2Fsqlite.git The first of a planned series of enhancements to the query planner that enable it to make better use of sqlite_stat2 histograms when the table has many repeated values. FossilOrigin-Name: 2cd374cd23fa2fd38f49090d6eeb9b1e521d51d5 --- diff --git a/manifest b/manifest index f9bb697af7..00b3208ed7 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -C Comment\simprovements\sin\spcache1.c.\s\sNo\schanges\sto\scode. -D 2011-01-19T21:58:56.344 +C The\sfirst\sof\sa\splanned\sseries\sof\senhancements\sto\sthe\squery\splanner\sthat\nenable\sit\sto\smake\sbetter\suse\sof\ssqlite_stat2\shistograms\swhen\sthe\stable\nhas\smany\srepeated\svalues. +D 2011-01-20T02:56:37.736 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in de6498556d536ae60bb8bb10e8c1ba011448658c F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -243,7 +243,7 @@ F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30 F src/wal.c dbca424f71678f663a286ab2a98f947af1d412a7 F src/wal.h c1aac6593a0b02b15dc625987e619edeab39292e F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c af069e6b53234118014dabfece96a9515b69d76b +F src/where.c 5cd6b88d57bfc816ba7f753a3cdf03686d954b8a F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 51756962d522e474338e9b2ebb26e7364d4aa125 @@ -253,9 +253,10 @@ F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07 F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/analyze.test c1eb87067fc16ece7c07e823d6395fd831b270c5 -F test/analyze2.test 3bde8f0879d9c1f2df3af21fcf42e706d8ee1e43 +F test/analyze2.test f45ac8d54bdad822139e53fc6307fc6b5ee41c69 F test/analyze3.test 820ddfb7591b49607fbaf77240c7955ac3cabb04 F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045 +F test/analyze5.test 18987796646efdf009ca0b8c8f060874a8fe57fb F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3 F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e @@ -899,14 +900,18 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P e5ca59e63b18ac45a8c82ca39dc8cce1c4ce903c -R 246c042190fbeb83b3f1f3c471b7048c +P 9660a0a22547656cc3765b673d0cee9e1dd829ef +R 9ff1bb21abd03a28e074b829beb25c52 +T *bgcolor * #a8c7d3 +T *branch * stat2-enhancement +T *sym-stat2-enhancement * +T -sym-trunk * U drh -Z d4b4af53a8b712c86792c5f30e91ad66 +Z f191562671825ddb731a0f83c41674dd -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) -iD8DBQFNN16joxKgR168RlERApzpAJ9ub7vBM3MbU8WJio56+Ng2W8PfBQCeLClb -tiAK9YA5ekgfGTTQ91uwQlY= -=gzKU +iD8DBQFNN6RpoxKgR168RlERAtbyAJ4tlGP5CKHBEdaaRtF9LD6pnMJo9QCaAnxY +oc508+oZBxzr/UoIZL3o+G4= +=2eyE -----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 96c640d6cf..a32e6ed42f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9660a0a22547656cc3765b673d0cee9e1dd829ef \ No newline at end of file +2cd374cd23fa2fd38f49090d6eeb9b1e521d51d5 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 4a5026f5f4..734f24019f 100644 --- a/src/where.c +++ b/src/where.c @@ -2201,11 +2201,18 @@ static void bestVirtualIndex( /* ** Argument pIdx is a pointer to an index structure that has an array of ** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column -** stored in Index.aSample. The domain of values stored in said column -** may be thought of as divided into (SQLITE_INDEX_SAMPLES+1) regions. -** Region 0 contains all values smaller than the first sample value. Region -** 1 contains values larger than or equal to the value of the first sample, -** but smaller than the value of the second. And so on. +** stored in Index.aSample. These samples divide the domain of values stored +** the index into (SQLITE_INDEX_SAMPLES+1) regions. +** Region 0 contains all values less than the first sample value. Region +** 1 contains values between the first and second samples. Region 2 contains +** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES +** contains values larger than the last sample. +** +** If the index contains many duplicates of a single value, then it is +** possible that two or more adjacent samples can hold the same value. +** When that is the case, the smallest possible region code is returned +** when roundUp is false and the largest possible region code is returned +** when roundUp is true. ** ** If successful, this function determines which of the regions value ** pVal lies in, sets *piRegion to the region index (a value between 0 @@ -2218,8 +2225,10 @@ static int whereRangeRegion( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ sqlite3_value *pVal, /* Value to consider */ + int roundUp, /* Return largest valid region if true */ int *piRegion /* OUT: Region of domain in which value lies */ ){ + assert( roundUp==0 || roundUp==1 ); if( ALWAYS(pVal) ){ IndexSample *aSample = pIdx->aSample; int i = 0; @@ -2229,7 +2238,12 @@ static int whereRangeRegion( double r = sqlite3_value_double(pVal); for(i=0; i=SQLITE_TEXT || aSample[i].u.r>r ) break; + if( aSample[i].eType>=SQLITE_TEXT ) break; + if( roundUp ){ + if( aSample[i].u.r>r ) break; + }else{ + if( aSample[i].u.r>=r ) break; + } } }else{ sqlite3 *db = pParse->db; @@ -2260,7 +2274,7 @@ static int whereRangeRegion( n = sqlite3ValueBytes(pVal, pColl->enc); for(i=0; imallocFailed ); return SQLITE_NOMEM; } - r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); + c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); sqlite3DbFree(db, zSample); }else #endif { - r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); + c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); } - if( r>0 ) break; + if( c-roundUp>=0 ) break; } } @@ -2386,15 +2400,21 @@ static int whereRangeScanEst( int iEst; int iLower = 0; int iUpper = SQLITE_INDEX_SAMPLES; + int roundUpUpper; + int roundUpLower; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); + roundUpLower = (pLower->eOperator==WO_GT) ?1:0; } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); + roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0; } if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ @@ -2402,15 +2422,15 @@ static int whereRangeScanEst( sqlite3ValueFree(pUpperVal); goto range_est_fallback; }else if( pLowerVal==0 ){ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( pLower ) iLower = iUpper/2; }else if( pUpperVal==0 ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; }else{ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( rc==SQLITE_OK ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); } } @@ -2418,12 +2438,12 @@ static int whereRangeScanEst( testcase( iEst==SQLITE_INDEX_SAMPLES ); assert( iEst<=SQLITE_INDEX_SAMPLES ); if( iEst<1 ){ - iEst = 1; + *piEst = 50/SQLITE_INDEX_SAMPLES; + }else{ + *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES; } - sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); - *piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES; return rc; } range_est_fallback: diff --git a/test/analyze2.test b/test/analyze2.test index 039fb378cc..add73af43a 100644 --- a/test/analyze2.test +++ b/test/analyze2.test @@ -154,22 +154,22 @@ do_eqp_test 2.6 { do_eqp_test 2.7 { SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300 } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x? AND x? AND y? AND y? AND x? AND x? AND y? AND y? AND x? AND x'h' @@ -416,14 +416,14 @@ do_test analyze2-6.2.2 { t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.3 { sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.4 { execsql { PRAGMA writable_schema = 1; @@ -457,7 +457,7 @@ do_test analyze2-6.2.6 { t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} #-------------------------------------------------------------------- # These tests, analyze2-7.*, test that the sqlite_stat2 functionality @@ -501,7 +501,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.6 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db2 @@ -509,7 +509,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db2 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.7 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db1 @@ -517,7 +517,7 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.8 { execsql { DELETE FROM sqlite_stat2 } db2 @@ -526,14 +526,14 @@ ifcapable shared_cache { t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.9 { execsql { SELECT * FROM sqlite_master } db2 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db2 - } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.10 { incr_schema_cookie test.db diff --git a/test/analyze5.test b/test/analyze5.test new file mode 100644 index 0000000000..233e7ff9e7 --- /dev/null +++ b/test/analyze5.test @@ -0,0 +1,225 @@ +# 2011 January 19 +# +# 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 tests for SQLite library. The focus of the tests +# in this file is the use of the sqlite_stat2 histogram data on tables +# with many repeated values and only a few distinct values. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +set testprefix analyze5 + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +do_test analyze5-1.0 { + execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z) } + for {set i 0} {$i < 1000} {incr i} { + set j [expr {$i>=25 && $i<=50}] + set k [expr {($i>=400) + ($i>=700) + ($i>=875)}] + execsql { INSERT INTO t1 VALUES($i,$j,$k) } + } + execsql { + CREATE INDEX t1y ON t1(y); + CREATE INDEX t1z ON t1(z); + ANALYZE; + SELECT * FROM sqlite_stat2 ORDER BY 1, 2, 3; + } +} [list t1 t1y 0 0 \ + t1 t1y 1 0 \ + t1 t1y 2 0 \ + t1 t1y 3 0 \ + t1 t1y 4 0 \ + t1 t1y 5 0 \ + t1 t1y 6 0 \ + t1 t1y 7 0 \ + t1 t1y 8 0 \ + t1 t1y 9 0 \ + t1 t1z 0 0 \ + t1 t1z 1 0 \ + t1 t1z 2 0 \ + t1 t1z 3 0 \ + t1 t1z 4 1 \ + t1 t1z 5 1 \ + t1 t1z 6 1 \ + t1 t1z 7 2 \ + t1 t1z 8 2 \ + t1 t1z 9 3] + +# Verify that range queries generate the correct row count estimates +# +foreach {testid where rows} { + 1 {z>=0 AND z<=0} 400 + 2 {z>=1 AND z<=1} 300 + 3 {z>=2 AND z<=2} 200 + 4 {z>=3 AND z<=3} 100 + 5 {z>=4 AND z<=4} 50 + 6 {z>=-1 AND z<=-1} 50 + 7 {z>1 AND z<3} 200 + 8 {z>0 AND z<100} 600 + 9 {z>=1 AND z<100} 600 + 10 {z>1 AND z<100} 300 + 11 {z>=2 AND z<100} 300 + 12 {z>2 AND z<100} 100 + 13 {z>=3 AND z<100} 100 + 14 {z>3 AND z<100} 50 + 15 {z>=4 AND z<100} 50 + 16 {z>=-100 AND z<=-1} 50 + 17 {z>=-100 AND z<=0} 400 + 18 {z>=-100 AND z<0} 50 + 19 {z>=-100 AND z<=1} 700 + 20 {z>=-100 AND z<2} 700 + 21 {z>=-100 AND z<=2} 900 + 22 {z>=-100 AND z<3} 900 + + 31 {z>=0.0 AND z<=0.0} 400 + 32 {z>=1.0 AND z<=1.0} 300 + 33 {z>=2.0 AND z<=2.0} 200 + 34 {z>=3.0 AND z<=3.0} 100 + 35 {z>=4.0 AND z<=4.0} 50 + 36 {z>=-1.0 AND z<=-1.0} 50 + 37 {z>1.5 AND z<3.0} 200 + 38 {z>0.5 AND z<100} 600 + 39 {z>=1.0 AND z<100} 600 + 40 {z>1.5 AND z<100} 300 + 41 {z>=2.0 AND z<100} 300 + 42 {z>2.1 AND z<100} 100 + 43 {z>=3.0 AND z<100} 100 + 44 {z>3.2 AND z<100} 50 + 45 {z>=4.0 AND z<100} 50 + 46 {z>=-100 AND z<=-1.0} 50 + 47 {z>=-100 AND z<=0.0} 400 + 48 {z>=-100 AND z<0.0} 50 + 49 {z>=-100 AND z<=1.0} 700 + 50 {z>=-100 AND z<2.0} 700 + 51 {z>=-100 AND z<=2.0} 900 + 52 {z>=-100 AND z<3.0} 900 + +} { + do_test analyze5-1.$testid { + eqp "SELECT * FROM t1 WHERE $where" + } [format {0 0 0 {SEARCH TABLE t1 USING INDEX t1z (z>? AND z=0 AND z<=0} 400 + 2 {z>=1 AND z<=1} 300 + 3 {z>=2 AND z<=2} 200 + 4 {z>=3 AND z<=3} 100 + 5 {z>=4 AND z<=4} 50 + 6 {z>=-1 AND z<=-1} 50 + 7 {z>1 AND z<3} 200 + 8 {z>0 AND z<100} 600 + 9 {z>=1 AND z<100} 600 + 10 {z>1 AND z<100} 300 + 11 {z>=2 AND z<100} 300 + 12 {z>2 AND z<100} 100 + 13 {z>=3 AND z<100} 100 + 14 {z>3 AND z<100} 50 + 15 {z>=4 AND z<100} 50 + 16 {z>=-100 AND z<=-1} 50 + 17 {z>=-100 AND z<=0} 400 + 18 {z>=-100 AND z<0} 50 + 19 {z>=-100 AND z<=1} 700 + 20 {z>=-100 AND z<2} 700 + 21 {z>=-100 AND z<=2} 900 + 22 {z>=-100 AND z<3} 900 + + 31 {z>=0.0 AND z<=0.0} 400 + 32 {z>=1.0 AND z<=1.0} 300 + 33 {z>=2.0 AND z<=2.0} 200 + 34 {z>=3.0 AND z<=3.0} 100 + 35 {z>=4.0 AND z<=4.0} 50 + 36 {z>=-1.0 AND z<=-1.0} 50 + 37 {z>1.5 AND z<3.0} 200 + 38 {z>0.5 AND z<100} 600 + 39 {z>=1.0 AND z<100} 600 + 40 {z>1.5 AND z<100} 300 + 41 {z>=2.0 AND z<100} 300 + 42 {z>2.1 AND z<100} 100 + 43 {z>=3.0 AND z<100} 100 + 44 {z>3.2 AND z<100} 50 + 45 {z>=4.0 AND z<100} 50 + 46 {z>=-100 AND z<=-1.0} 50 + 47 {z>=-100 AND z<=0.0} 400 + 48 {z>=-100 AND z<0.0} 50 + 49 {z>=-100 AND z<=1.0} 700 + 50 {z>=-100 AND z<2.0} 700 + 51 {z>=-100 AND z<=2.0} 900 + 52 {z>=-100 AND z<3.0} 900 +} { + do_test analyze5-2.$testid { + eqp "SELECT * FROM t1 WHERE $where" + } [format {0 0 0 {SEARCH TABLE t1 USING INDEX t1z (z>? AND z='alpha' AND y<='alpha'} 400 + 2 {y>='bravo' AND y<='bravo'} 300 + 3 {y>='charlie' AND y<='charlie'} 200 + 4 {y>='delta' AND y<='delta'} 100 + 5 {y>='echo' AND y<='echo'} 50 + 6 {y>='' AND y<=''} 50 + 7 {y>'bravo' AND y<'delta'} 200 + 8 {y>'alpha' AND y<'zzz'} 600 + 9 {y>='bravo' AND y<'zzz'} 600 + 10 {y>'bravo' AND y<'zzz'} 300 + 11 {y>='charlie' AND y<'zzz'} 300 + 12 {y>'charlie' AND y<'zzz'} 100 + 13 {y>='delta' AND y<'zzz'} 100 + 14 {y>'delta' AND y<'zzz'} 50 + 15 {y>='echo' AND y<'zzz'} 50 + 16 {y>=0 AND y<=''} 50 + 17 {y>=0 AND y<='alpha'} 400 + 18 {y>=0 AND y<'alpha'} 50 + 19 {y>=0 AND y<='bravo'} 700 + 20 {y>=0 AND y<'charlie'} 700 + 21 {y>=0 AND y<='charlie'} 900 + 22 {y>=0 AND y<'delta'} 900 +} { + do_test analyze5-3.$testid { + eqp "SELECT * FROM t1 WHERE $where" + } [format {0 0 0 {SEARCH TABLE t1 USING INDEX t1y (y>? AND y