Here is an attempted enhancement to the query planner that didn't work out.
But it seems good to save this change for historical reference, even if it
does not belong on the trunk.

FossilOrigin-Name: 8daf6e1b425e65d7ee2f23b45a3258979e5242f3
diff --git a/manifest b/manifest
index 4f1bbb1..e41f53c 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sharmless\scompiler\swarnings\son\sunix.
-D 2011-07-09T16:17:18.236
+C Here\sis\san\sattempted\senhancement\sto\sthe\squery\splanner\sthat\sdidn't\swork\sout.\nBut\sit\sseems\sgood\sto\ssave\sthis\schange\sfor\shistorical\sreference,\seven\sif\sit\ndoes\snot\sbelong\son\sthe\strunk.
+D 2011-07-11T15:52:45.196
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in c1d7a7f4fd8da6b1815032efca950e3d5125407e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -250,7 +250,7 @@
 F src/wal.c 0c70ad7b1cac6005fa5e2cbefd23ee05e391c290
 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c ce7cce80c5cb07ba40e9bf38a33ef806c61f55f2
+F src/where.c bc46a3e6e3cf158360e2b2a0e7c9ee73124de581
 F test/8_3_names.test b93687beebd17f6ebf812405a6833bae5d1f4199
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -951,7 +951,11 @@
 F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings.sh 2ebae31e1eb352696f3c2f7706a34c084b28c262
-P 418a4da2a96cf33055f18c9a667754fad2111cf3
-R bd1403a9e638958833ef8e7e7178479a
+P 90b1aea17400bbda5ebc8ae4eb4e12127519e42e
+R 8ae68d7982d6f92b87ef21927fff413f
+T *bgcolor * #c0ffc0
+T *branch * query-planner-deadend
+T *sym-query-planner-deadend *
+T -sym-trunk *
 U drh
-Z 18ede22d9ea411d29db6ab17e40995c3
+Z 9d0d932c2d463d85cb7dbc21286fff38
diff --git a/manifest.uuid b/manifest.uuid
index 0650cd6..2b993c0 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-90b1aea17400bbda5ebc8ae4eb4e12127519e42e
\ No newline at end of file
+8daf6e1b425e65d7ee2f23b45a3258979e5242f3
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index bb3bee3..258514d 100644
--- a/src/where.c
+++ b/src/where.c
@@ -4712,9 +4712,10 @@
     int isOptimal;              /* Iterator for optimal/non-optimal search */
     int nUnconstrained;         /* Number tables without INDEXED BY */
     Bitmask notIndexed;         /* Mask of tables that cannot use an index */
+    double rBestOptimalIdx;     /* Cost of best optimal index plan */
 
     memset(&bestPlan, 0, sizeof(bestPlan));
-    bestPlan.rCost = SQLITE_BIG_DBL;
+    bestPlan.rCost = rBestOptimalIdx = SQLITE_BIG_DBL;
     WHERETRACE(("*** Begin search for loop %d ***\n", i));
 
     /* Loop through the remaining entries in the FROM clause to find the
@@ -4813,8 +4814,10 @@
         **   (1) The table must not depend on other tables that have not
         **       yet run.
         **
-        **   (2) A full-table-scan plan cannot supercede indexed plan unless
-        **       the full-table-scan is an "optimal" plan as defined above.
+        **   (2) A full-table-scan cannot supercede an indexed plan unless
+        **       (a) the full-table-scan is an "optimal" plan as defined above
+        **       or (b) the cost of the full-table-scan is less than the cost
+        **       of the best optimal index plan.
         **
         **   (3) All tables have an INDEXED BY clause or this table lacks an
         **       INDEXED BY clause or this table uses the specific
@@ -4822,20 +4825,19 @@
         **       that a best-so-far is always selected even if an impossible
         **       combination of INDEXED BY clauses are given.  The error
         **       will be detected and relayed back to the application later.
-        **       The NEVER() comes about because rule (2) above prevents
-        **       An indexable full-table-scan from reaching rule (3).
         **
         **   (4) The plan cost must be lower than prior plans or else the
         **       cost must be the same and the number of rows must be lower.
         */
-        if( (sCost.used&notReady)==0                       /* (1) */
-            && (bestJ<0 || (notIndexed&m)!=0               /* (2) */
-                || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
-                || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
-            && (nUnconstrained==0 || pTabItem->pIndex==0   /* (3) */
-                || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
-            && (bestJ<0 || sCost.rCost<bestPlan.rCost      /* (4) */
-                || (sCost.rCost<=bestPlan.rCost 
+        if( (sCost.used&notReady)==0                                  /* (1) */
+         && (bestJ<0 || (notIndexed&m)!=0                             /* (2) */
+             || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
+             || sCost.rCost<rBestOptimalIdx
+             || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
+         && (nUnconstrained==0 || pTabItem->pIndex==0                 /* (3) */
+             || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
+         && (bestJ<0 || sCost.rCost<bestPlan.rCost                    /* (4) */
+             || (sCost.rCost<=bestPlan.rCost 
                  && sCost.plan.nRow<bestPlan.plan.nRow))
         ){
           WHERETRACE(("=== table %d is best so far"
@@ -4844,6 +4846,12 @@
           bestPlan = sCost;
           bestJ = j;
         }
+        if( isOptimal
+         && (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0
+         && sCost.rCost<rBestOptimalIdx
+        ){
+          rBestOptimalIdx = bestPlan.rCost;
+        }
         if( doNotReorder ) break;
       }
     }