From 2db144c33b39985b159ec9210cc13bdb53c00e1c Mon Sep 17 00:00:00 2001
From: drh <>
Date: Wed, 1 Dec 2021 16:31:02 +0000
Subject: [PATCH 01/18] Add a Bloom filter to the automatic-index mechanism.

FossilOrigin-Name: 50ac4de1d7cbb586ea7969e1ae80ea8b021e194edc2fa7db19374b4ee9369bee
---
 manifest        |  27 ++++++------
 manifest.uuid   |   2 +-
 src/sqliteInt.h |   1 +
 src/vdbe.c      | 106 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/vdbeInt.h   |   2 +-
 src/vdbemem.c   |   4 +-
 src/where.c     |   8 ++++
 src/whereInt.h  |   1 +
 src/wherecode.c |   8 ++++
 9 files changed, 143 insertions(+), 16 deletions(-)

diff --git a/manifest b/manifest
index 1799086100..8fae6a4e21 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C In\sthe\sautomatic\sindex\sgenerator\slogic,\sbe\smore\sprecise\sabout\swhen\sa\npartial\sautomatic\sindex\sis\sallowed\sin\sorder\sto\scapture\smore\scases\swhere\sit\nis\slegal\sto\suse\sa\spartial\sautomatic\sindex.
-D 2021-11-30T14:07:58.372
+C Add\sa\sBloom\sfilter\sto\sthe\sautomatic-index\smechanism.
+D 2021-12-01T16:31:02.486
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -555,7 +555,7 @@ F src/shell.c.in 975f268ef261773fcbed1e519dfa10c4f33e8b1cffc12120563e61857fff07c
 F src/sqlite.h.in 5cd209ac7dc4180f0e19292846f40440b8488015849ca0110c70b906b57d68f0
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h 193e716a67c877a6054a8c261c932bdc64f8c8be9b66388c74f21d94a259b7ce
+F src/sqliteInt.h a44a3474b2247a82decc17644c68e00db5ba1bc239a6c005f6c08f6599083c01
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -622,13 +622,13 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
 F src/util.c 30df8356e231dad33be10bb27897655002668343280004ba28c734489414a167
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c e98f1baf54a00db2c4669dbd04f8bbc89b5909a5b43e76fbbbf1a97007adba2b
+F src/vdbe.c a77525ddf690771b13db4114dce5305657efe6b7393920f2cbaa2447e8b83129
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
-F src/vdbeInt.h 31fbabdc1ed61d9695337dfe5269ea94e1cf615c17f5cafeaa1bb01066820bab
+F src/vdbeInt.h fd1103c7ecec8c84164038c8eacaa4a633cb3c10a2f725aae7bd865d4a4fcceb
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
 F src/vdbeaux.c 21db442d159fd745a7693d157b5f998260b6af4ca60de559fa3b7b68c7405af2
 F src/vdbeblob.c 29c4118f7ee615cdee829e8401f6ead1b96b95d545b4de0042f6de39c962c652
-F src/vdbemem.c a3d91dc9bb9ef725db77e4e9de7e1acef43192c9f8406c307665d503e3c2837c
+F src/vdbemem.c da4d594084d581be6436582bb44bb128feeb138a3e6c313eda6749ebdc3a65ec
 F src/vdbesort.c 513b481c8bab4a6578c92194a60cf3bc3b48736e4a53f8d2d7918121c5b594e7
 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
 F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
@@ -637,9 +637,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 4b276017881185d0e1dc984df66c835cde6faf03834ef9ff34f48653f9144dec
-F src/whereInt.h 83877a75a1bce056ea44aff02f1dfa958ad1d6038c213ddadb8652003b45151d
-F src/wherecode.c 1f5b62f46d284c8886945eb7438415bc27e23e87bb60b9ee468fa6bd31268f33
+F src/where.c 1b8a6c53c34c59c765190484d245c457bedc211325de4c75a2070432fd67e314
+F src/whereInt.h 23f990791dc428f0bbb31f8ef5026b78b6290272de86185f4822c85f4c774803
+F src/wherecode.c 620e81077588a727876a86f87aab310c02fc4e4960691e48153d8a87ca1fa453
 F src/whereexpr.c 17bdbf4f5b490e70a18635498f0b910a558f953a9bf80af7f19cbde6e60e6825
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1933,7 +1933,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 19c51b46e4095ee28badb10f4e08bbd330bda320c9a8806e93b8fc60ba211a2e
-R 0fbfb4a13255ae0bad405a99d2bdf9ec
+P 664b461bb5063d98047fc2e51a3827235cd9f55ca2e23cb66e719eac53fb5437
+R 157be6898a36d49d7a017b78b4cdbad6
+T *branch * bloom-filter
+T *sym-bloom-filter *
+T -sym-trunk *
 U drh
-Z 511f59b4656ae7d0825d672340080bad
+Z 5c7d4c8ea12d463e648cb44a868717dc
diff --git a/manifest.uuid b/manifest.uuid
index 8a15f64b8a..2cbf640244 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-664b461bb5063d98047fc2e51a3827235cd9f55ca2e23cb66e719eac53fb5437
\ No newline at end of file
+50ac4de1d7cbb586ea7969e1ae80ea8b021e194edc2fa7db19374b4ee9369bee
\ No newline at end of file
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index b45151104a..d404b7b4fc 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1761,6 +1761,7 @@ struct sqlite3 {
 #define SQLITE_SeekScan       0x00020000 /* The OP_SeekScan optimization */
 #define SQLITE_OmitOrderBy    0x00040000 /* Omit pointless ORDER BY */
    /* TH3 expects this value  ^^^^^^^^^^ to be 0x40000. Coordinate any change */
+#define SQLITE_BloomFilter    0x00080000 /* Use a Bloom filter on searches */
 #define SQLITE_AllOpts        0xffffffff /* All optimizations */
 
 /*
diff --git a/src/vdbe.c b/src/vdbe.c
index 3476c02daa..e38527356c 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -671,6 +671,35 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
   }
 }
 
+/*
+** Default size of a bloom filter, in bytes
+*/
+#define SQLITE_BLOOM_SZ 10000
+
+/*
+** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning
+** with pOp->p3.  Return the hash.
+*/
+static unsigned int filterHash(const Mem *aMem, const Op *pOp){
+  int i, mx;
+  u32 h = 0;
+
+  i = pOp->p3;
+  assert( pOp->p4type==P4_INT32 );
+  mx = i + pOp->p4.i;
+  for(i=pOp->p3, mx=i+pOp->p4.i; i<mx; i++){
+    const Mem *p = &aMem[i];
+    if( p->flags & (MEM_Int|MEM_IntReal) ){
+      h += (u32)(p->u.i&0xffffffff);
+    }else if( p->flags & MEM_Real ){
+      h += (u32)(sqlite3VdbeIntValue(p)&0xffffffff);
+    }else if( p->flags & (MEM_Str|MEM_Blob) ){
+      h += p->n;
+    }
+  }
+  return h % (SQLITE_BLOOM_SZ*8);
+}
+
 /*
 ** Return the symbolic name for the data type of a pMem
 */
@@ -8129,6 +8158,83 @@ case OP_Function: {            /* group */
   break;
 }
 
+/* Opcode: FilterInit P1 * * * *
+** Synopsis: filter(P1) = empty
+**
+** Initialize register P1 so that is an empty bloom filter.
+*/
+case OP_FilterInit: {
+  assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+  pIn1 = &aMem[pOp->p1];
+  sqlite3VdbeMemSetZeroBlob(pIn1, SQLITE_BLOOM_SZ);
+  if( sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem;
+  break;
+}
+
+/* Opcode: FilterAdd P1 * P3 P4 *
+** Synopsis: filter(P1) += key(P3@P4)
+**
+** Compute a hash on the P4 registers starting with r[P3] and
+** add that hash to the bloom filter contained in r[P1].
+*/
+case OP_FilterAdd: {
+  u32 h;
+
+  assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+  pIn1 = &aMem[pOp->p1];
+  assert( pIn1->flags & MEM_Blob );
+  assert( pIn1->n==SQLITE_BLOOM_SZ );
+  h = filterHash(aMem, pOp);
+#ifdef SQLITE_DEBUG
+  if( db->flags&SQLITE_VdbeTrace ){
+    int ii;
+    for(ii=pOp->p3; ii<pOp->p3+pOp->p4.i; ii++){
+      registerTrace(ii, &aMem[ii]);
+    }
+    printf("hash = %u\n", h);
+  }
+#endif
+  assert( h>=0 && h<SQLITE_BLOOM_SZ*8 );
+  pIn1->z[h/8] |= 1<<(h&7);
+  break;
+}
+
+/* Opcode: Filter P1 P2 P3 P4 *
+** Synopsis: if key(P3@P4) not in filter(P1) goto P2
+**
+** Compute a hash on the key contained in the P4 registers starting
+** with r[P3].  Check to see if that hash is found in the
+** bloom filter hosted by register P1.  If it is not present then
+** maybe jump to P2.  Otherwise fall through.
+**
+** False negatives are harmless.  It is always safe to fall through,
+** even if the value is in the bloom filter.  A false negative causes
+** more CPU cycles to be used, but it should still yield the correct
+** answer.  However, an incorrect answer may well arise from a
+** false positive - if the jump is taken when it should fall through.
+*/
+case OP_Filter: {          /* jump */
+  u32 h;
+
+  assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+  pIn1 = &aMem[pOp->p1];
+  assert( pIn1->flags & MEM_Blob );
+  assert( pIn1->n==SQLITE_BLOOM_SZ );
+  h = filterHash(aMem, pOp);
+#ifdef SQLITE_DEBUG
+  if( db->flags&SQLITE_VdbeTrace ){
+    int ii;
+    for(ii=pOp->p3; ii<pOp->p3+pOp->p4.i; ii++){
+      registerTrace(ii, &aMem[ii]);
+    }
+    printf("hash = %u\n", h);
+  }
+#endif
+  assert( h>=0 && h<SQLITE_BLOOM_SZ*8 );
+  if( (pIn1->z[h/8] & (1<<(h&7)))==0 ) goto jump_to_p2;
+  break;
+}
+
 /* Opcode: Trace P1 P2 * P4 *
 **
 ** Write P4 on the statement trace output if statement tracing is
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 599d064165..c76cdbfdbc 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -538,7 +538,7 @@ int sqlite3VdbeMemSetRowSet(Mem*);
 int sqlite3VdbeMemMakeWriteable(Mem*);
 int sqlite3VdbeMemStringify(Mem*, u8, u8);
 int sqlite3IntFloatCompare(i64,double);
-i64 sqlite3VdbeIntValue(Mem*);
+i64 sqlite3VdbeIntValue(const Mem*);
 int sqlite3VdbeMemIntegerify(Mem*);
 double sqlite3VdbeRealValue(Mem*);
 int sqlite3VdbeBooleanValue(Mem*, int ifNull);
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 570a2eb38c..5a9d15f465 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -596,12 +596,12 @@ static SQLITE_NOINLINE i64 doubleToInt64(double r){
 **
 ** If pMem represents a string value, its encoding might be changed.
 */
-static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){
+static SQLITE_NOINLINE i64 memIntValue(const Mem *pMem){
   i64 value = 0;
   sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
   return value;
 }
-i64 sqlite3VdbeIntValue(Mem *pMem){
+i64 sqlite3VdbeIntValue(const Mem *pMem){
   int flags;
   assert( pMem!=0 );
   assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
diff --git a/src/where.c b/src/where.c
index 54e4ea8196..eaa45b0198 100644
--- a/src/where.c
+++ b/src/where.c
@@ -904,6 +904,10 @@ static void constructAutomaticIndex(
   sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
   sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
   VdbeComment((v, "for %s", pTable->zName));
+  if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){
+    pLevel->regFilter = ++pParse->nMem;
+    sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
+  }
 
   /* Fill the automatic index with content */
   pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
@@ -926,6 +930,10 @@ static void constructAutomaticIndex(
   regBase = sqlite3GenerateIndexKey(
       pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0
   );
+  if( pLevel->regFilter ){
+    sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0,
+                         regBase, pLoop->u.btree.nEq);
+  }
   sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
   sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
   if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
diff --git a/src/whereInt.h b/src/whereInt.h
index f651e790cc..5b076e40e2 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -64,6 +64,7 @@ struct WhereLevel {
   u32 iLikeRepCntr;     /* LIKE range processing counter register (times 2) */
   int addrLikeRep;      /* LIKE range processing address */
 #endif
+  int regFilter;        /* Bloom filter */
   u8 iFrom;             /* Which entry in the FROM clause */
   u8 op, p3, p5;        /* Opcode, P3 & P5 of the opcode that ends the loop */
   int p1, p2;           /* Operands of the opcode used to end the loop */
diff --git a/src/wherecode.c b/src/wherecode.c
index 460ac4fe30..637db432ea 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1511,6 +1511,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
     iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
     if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
     addrNxt = pLevel->addrNxt;
+    if( pLevel->regFilter ){
+      sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
+                           iRowidReg, 1);
+    }
     sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
     VdbeCoverage(v);
     pLevel->op = OP_Noop;
@@ -1836,6 +1840,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
         sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull);
         VdbeComment((v, "NULL-scan pass ctr"));
       }
+      if( pLevel->regFilter ){
+        sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
+                             regBase, nConstraint);
+      }
 
       op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
       assert( op!=0 );

From fa35f5c5a7868902019c6a18eeec52f4a577fc8b Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sat, 4 Dec 2021 13:43:57 +0000
Subject: [PATCH 02/18] First attempt to use Bloom filters to optimize
 star-schema queries.

FossilOrigin-Name: 28161fba9bcde5ae4b36b22d766c881b795af111a3a323c90f6149d0fea9297d
---
 manifest       |  14 +++---
 manifest.uuid  |   2 +-
 src/where.c    | 122 +++++++++++++++++++++++++++++++++++++++++++++++--
 src/whereInt.h |   1 +
 4 files changed, 126 insertions(+), 13 deletions(-)

diff --git a/manifest b/manifest
index 1e420b2cce..1e7e0a1ce4 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sthe\ssqlite3WhereBegin()\ssimplification\sfrom\strunk.
-D 2021-12-03T19:10:17.141
+C First\sattempt\sto\suse\sBloom\sfilters\sto\soptimize\sstar-schema\squeries.
+D 2021-12-04T13:43:57.343
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -637,8 +637,8 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c a0787ad0a3447685e553725dab50e52c9763b5f10e2506255713dbd736c89998
-F src/whereInt.h 83ae6f7d0fce8b5164d97698790e0005a909b3d06d73df86a4a6bb0d9411861e
+F src/where.c e9cfeae040b60c21244f40fed1a3473f048ee20cb2fb987d5028350e4bead886
+F src/whereInt.h 2c9d149b1b41c59f977deb1882e95632800946fcf15c83eaffb227afece04e51
 F src/wherecode.c 620e81077588a727876a86f87aab310c02fc4e4960691e48153d8a87ca1fa453
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 11d97fb8be6b5155f38df130d7e828edd0f381e32f651458939b1cb9cb973fff 6225e9abcb0261fefca4a26530524ffc449f937f8ae1ece718af2c3c3d73d78d
-R 91b705f2730ed0122ffcaf8efbe0af73
+P 41ba2dfdd3a18671fc78d60935a16fa50f36af3d6481eff2ca9fba88e7093997
+R a2ed82c3d908c8671ab026e425f8c4f5
 U drh
-Z dab48a2f628412943ed7bfde21b0a086
+Z 92e3ffa168d28fa3c757a00a1d8afcc3
diff --git a/manifest.uuid b/manifest.uuid
index 6adb3fed23..383563aee3 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-41ba2dfdd3a18671fc78d60935a16fa50f36af3d6481eff2ca9fba88e7093997
\ No newline at end of file
+28161fba9bcde5ae4b36b22d766c881b795af111a3a323c90f6149d0fea9297d
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index 371bf52305..22ed0c9b5b 100644
--- a/src/where.c
+++ b/src/where.c
@@ -750,7 +750,7 @@ static int termCanDriveIndex(
 ** and to set up the WhereLevel object pLevel so that the code generator
 ** makes use of the automatic index.
 */
-static void constructAutomaticIndex(
+static SQLITE_NOINLINE void constructAutomaticIndex(
   Parse *pParse,              /* The parsing context */
   WhereClause *pWC,           /* The WHERE clause */
   SrcItem *pSrc,              /* The FROM clause term to get the next index */
@@ -965,6 +965,62 @@ end_auto_index_create:
 }
 #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
 
+/*
+** Create a Bloom filter for the WhereLevel in the parameter.
+*/
+static SQLITE_NOINLINE void constructBloomFilter(
+  WhereInfo *pWInfo,          /* The WHERE clause */
+  WhereLevel *pLevel          /* Make a Bloom filter for this FROM term */
+){
+  int addrTop;
+  int addrCont;
+  WhereTerm *pTerm;
+  WhereTerm *pWCEnd;
+  Parse *pParse = pWInfo->pParse;
+  Vdbe *v = pParse->pVdbe;
+  WhereLoop *pLoop = pLevel->pWLoop;
+  int iCur;
+  
+
+  assert( pLoop!=0 );
+  assert( v!=0 );
+  iCur = pLevel->iTabCur;
+  addrCont = sqlite3VdbeMakeLabel(pParse);
+  addrTop = sqlite3VdbeAddOp0(v, OP_Once);
+  pLevel->regFilter = ++pParse->nMem;
+  sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
+  sqlite3VdbeAddOp1(v, OP_Rewind, iCur);
+  pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
+  for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
+    Expr *pExpr = pTerm->pExpr;
+    if( (pTerm->wtFlags & TERM_VIRTUAL)==0
+     && sqlite3ExprIsTableConstant(pExpr, iCur)
+    ){
+      sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
+    }
+  }
+  if( pLoop->wsFlags & WHERE_IPK ){
+    int r1 = sqlite3GetTempReg(pParse);
+    sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
+    sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1);
+  }else{
+    Index *pIdx = pLoop->u.btree.pIndex;
+    int r1 = sqlite3GetTempRange(pParse, pIdx->nKeyCol);
+    int n = pIdx->nKeyCol;
+    int jj;
+    for(jj=0; jj<n; jj++){
+      int iCol = pIdx->aiColumn[jj];
+      sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj);
+    }
+    sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
+  }
+  sqlite3VdbeResolveLabel(v, addrCont);
+  sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+3);
+  sqlite3VdbeJumpHere(v, addrTop);
+  sqlite3VdbeJumpHere(v, addrTop+2);
+}
+
+
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 /*
 ** Allocate and populate an sqlite3_index_info structure. It is the 
@@ -4841,6 +4897,49 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
   return notReady;
 }
 
+/*
+** Check to see if there are any SEARCH loops that might benefit from
+** using a Bloom filter.  Consider a Bloom filter if:
+**
+**   (1)  The SEARCH happens more than N times where N is the number
+**        of rows in the table that is being considered for the Bloom
+**        filter.  (TO DO:  Make this condition more precise.)
+**   (2)  Most searches are expected to find zero rows
+**   (3)  The table being searched is not the right table of a LEFT JOIN
+**   (4)  Bloom-filter processing is not disabled
+**
+** This block of code merely checks to see if a Bloom filter would be
+** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the
+** WhereLoop.  The implementation of the Bloom filter comes further
+** down where the code for each WhereLoop is generated.
+*/
+static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
+  WhereInfo *pWInfo,
+  sqlite3 *db
+){
+  int i;
+  LogEst nSearch;
+  SrcItem *pItem;
+
+  assert( pWInfo->nLevel>=2 );
+  assert( OptimizationEnabled(db, SQLITE_BloomFilter) );
+  nSearch = pWInfo->a[0].pWLoop->nOut;
+  for(i=1; i<pWInfo->nLevel; i++){
+    WhereLoop *pLoop = pWInfo->a[i].pWLoop;
+    if( (pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0
+     && (pLoop->wsFlags & WHERE_COLUMN_EQ)!=0
+     && pLoop->nOut<0
+     && nSearch > (pItem = &pWInfo->pTabList->a[pLoop->iTab])->pTab->nRowLogEst
+     && (pItem->fg.jointype & JT_LEFT)==0
+    ){
+      pLoop->wsFlags |= WHERE_BLOOMFILTER;
+      pLoop->wsFlags &= ~WHERE_IDX_ONLY;
+      WHERETRACE(0xffff, ("-> use Bloom-filter on loop %c\n", pLoop->cId));
+    }
+    nSearch += pLoop->nOut;
+  }
+}
+
 /*
 ** Generate the beginning of the loop used for WHERE clause processing.
 ** The return value is a pointer to an opaque structure that contains
@@ -5230,6 +5329,15 @@ WhereInfo *sqlite3WhereBegin(
     assert( nTabList>0 );
   }
 
+  /* Check to see if there are any SEARCH loops that might benefit from
+  ** using a Bloom filter.
+  */
+  if( pWInfo->nLevel>=2
+   && OptimizationEnabled(db, SQLITE_BloomFilter)
+  ){
+    whereCheckIfBloomFilterIsUseful(pWInfo, db);
+  }
+
 #if defined(WHERETRACE_ENABLED)
   if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
     sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n");
@@ -5418,13 +5526,17 @@ WhereInfo *sqlite3WhereBegin(
     if( pParse->nErr ) goto whereBeginError;
     pLevel = &pWInfo->a[ii];
     wsFlags = pLevel->pWLoop->wsFlags;
+    if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){
+      if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){
 #ifndef SQLITE_OMIT_AUTOMATIC_INDEX
-    if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
-      constructAutomaticIndex(pParse, &pWInfo->sWC,
-                &pTabList->a[pLevel->iFrom], notReady, pLevel);
+        constructAutomaticIndex(pParse, &pWInfo->sWC,
+                  &pTabList->a[pLevel->iFrom], notReady, pLevel);
+#endif
+      }else{
+        constructBloomFilter(pWInfo, pLevel);
+      }
       if( db->mallocFailed ) goto whereBeginError;
     }
-#endif
     addrExplain = sqlite3WhereExplainOneScan(
         pParse, pTabList, pLevel, wctrlFlags
     );
diff --git a/src/whereInt.h b/src/whereInt.h
index ff6ac4c397..fbe84787f9 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -600,5 +600,6 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
 #define WHERE_BIGNULL_SORT 0x00080000  /* Column nEq of index is BIGNULL */
 #define WHERE_IN_SEEKSCAN  0x00100000  /* Seek-scan optimization for IN */
 #define WHERE_TRANSCONS    0x00200000  /* Uses a transitive constraint */
+#define WHERE_BLOOMFILTER  0x00400000  /* Consider using a Bloom-filter */
 
 #endif /* !defined(SQLITE_WHEREINT_H) */

From 19ce9aafdb200670d5ee9c3f1b139801a988f2ba Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sat, 4 Dec 2021 13:52:08 +0000
Subject: [PATCH 03/18] Add the "WITH BLOOM FILTER" clause to the EXPLAIN QUERY
 PLAN output for cases were a Bloom filter is used.

FossilOrigin-Name: 8e078c0e404fe4b3661dd4a11a992a8914c99e2a144cecc417421cbd68fa08cc
---
 manifest        | 12 ++++++------
 manifest.uuid   |  2 +-
 src/wherecode.c |  3 +++
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/manifest b/manifest
index 1e7e0a1ce4..dfe53bde8f 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C First\sattempt\sto\suse\sBloom\sfilters\sto\soptimize\sstar-schema\squeries.
-D 2021-12-04T13:43:57.343
+C Add\sthe\s"WITH\sBLOOM\sFILTER"\sclause\sto\sthe\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\ncases\swere\sa\sBloom\sfilter\sis\sused.
+D 2021-12-04T13:52:08.699
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -639,7 +639,7 @@ F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
 F src/where.c e9cfeae040b60c21244f40fed1a3473f048ee20cb2fb987d5028350e4bead886
 F src/whereInt.h 2c9d149b1b41c59f977deb1882e95632800946fcf15c83eaffb227afece04e51
-F src/wherecode.c 620e81077588a727876a86f87aab310c02fc4e4960691e48153d8a87ca1fa453
+F src/wherecode.c 26b0cbc985deafe702037ed00bcf40857d129be0d80d198ac87644777b59984d
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 41ba2dfdd3a18671fc78d60935a16fa50f36af3d6481eff2ca9fba88e7093997
-R a2ed82c3d908c8671ab026e425f8c4f5
+P 28161fba9bcde5ae4b36b22d766c881b795af111a3a323c90f6149d0fea9297d
+R 6eeaa3a323a583fb23be16ffd26ce73a
 U drh
-Z 92e3ffa168d28fa3c757a00a1d8afcc3
+Z a160cdc5e20e6c677a6900e142dccac2
diff --git a/manifest.uuid b/manifest.uuid
index 383563aee3..030f03de8f 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-28161fba9bcde5ae4b36b22d766c881b795af111a3a323c90f6149d0fea9297d
\ No newline at end of file
+8e078c0e404fe4b3661dd4a11a992a8914c99e2a144cecc417421cbd68fa08cc
\ No newline at end of file
diff --git a/src/wherecode.c b/src/wherecode.c
index 637db432ea..2e616ad592 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -196,6 +196,9 @@ int sqlite3WhereExplainOneScan(
                   pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
     }
 #endif
+    if( flags & WHERE_BLOOMFILTER ){
+      sqlite3_str_appendf(&str, " WITH BLOOM FILTER");
+    }
 #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
     if( pLoop->nOut>=10 ){
       sqlite3_str_appendf(&str, " (~%llu rows)",

From 770dade262f88ec531d7708ac2d0f2e7882dcb16 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sat, 4 Dec 2021 14:24:30 +0000
Subject: [PATCH 04/18] Apply the Bloom filter only on those terms of an index
 that have equality constraints.

FossilOrigin-Name: a70429596a3c6a413b03118b0d800521b3526d99dcf88a48acc3189b51518d82
---
 manifest        | 14 +++++++-------
 manifest.uuid   |  2 +-
 src/where.c     |  2 +-
 src/wherecode.c |  2 +-
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/manifest b/manifest
index dfe53bde8f..f576dec37b 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\s"WITH\sBLOOM\sFILTER"\sclause\sto\sthe\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\ncases\swere\sa\sBloom\sfilter\sis\sused.
-D 2021-12-04T13:52:08.699
+C Apply\sthe\sBloom\sfilter\sonly\son\sthose\sterms\sof\san\sindex\sthat\shave\sequality\nconstraints.
+D 2021-12-04T14:24:30.524
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -637,9 +637,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c e9cfeae040b60c21244f40fed1a3473f048ee20cb2fb987d5028350e4bead886
+F src/where.c d203a7acda584940eee49d80e38067ba354410ca08782792bdf4d6f56ca20aa3
 F src/whereInt.h 2c9d149b1b41c59f977deb1882e95632800946fcf15c83eaffb227afece04e51
-F src/wherecode.c 26b0cbc985deafe702037ed00bcf40857d129be0d80d198ac87644777b59984d
+F src/wherecode.c ab83c1f7c593ab5e9268c51dde80f478e48468e8c1e25866a4f445e7b4282b78
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 28161fba9bcde5ae4b36b22d766c881b795af111a3a323c90f6149d0fea9297d
-R 6eeaa3a323a583fb23be16ffd26ce73a
+P 8e078c0e404fe4b3661dd4a11a992a8914c99e2a144cecc417421cbd68fa08cc
+R a78591e6ea873c8de7a64c271c45d204
 U drh
-Z a160cdc5e20e6c677a6900e142dccac2
+Z 73637cbd9e62c30bcdc98bccabee0c03
diff --git a/manifest.uuid b/manifest.uuid
index 030f03de8f..23eed8d9fa 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-8e078c0e404fe4b3661dd4a11a992a8914c99e2a144cecc417421cbd68fa08cc
\ No newline at end of file
+a70429596a3c6a413b03118b0d800521b3526d99dcf88a48acc3189b51518d82
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index 22ed0c9b5b..87a00ef200 100644
--- a/src/where.c
+++ b/src/where.c
@@ -1006,7 +1006,7 @@ static SQLITE_NOINLINE void constructBloomFilter(
   }else{
     Index *pIdx = pLoop->u.btree.pIndex;
     int r1 = sqlite3GetTempRange(pParse, pIdx->nKeyCol);
-    int n = pIdx->nKeyCol;
+    int n = pLoop->u.btree.nEq;
     int jj;
     for(jj=0; jj<n; jj++){
       int iCol = pIdx->aiColumn[jj];
diff --git a/src/wherecode.c b/src/wherecode.c
index 2e616ad592..fbb40ad4e3 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1845,7 +1845,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       }
       if( pLevel->regFilter ){
         sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
-                             regBase, nConstraint);
+                             regBase, nEq);
       }
 
       op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];

From 067c60cfc9aa3ec493f3bdd8f62d225846743a2a Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sat, 4 Dec 2021 18:45:08 +0000
Subject: [PATCH 05/18] Add VdbeCoverage() macros.  Adjust the Bloom-filter
 hash function so that it correctly deals with zero-blobs.

FossilOrigin-Name: 629ee2e3e3125bfd2af435c6713d49e46691213ad15db0a5e93a63a77f1130c2
---
 manifest        | 16 ++++++++--------
 manifest.uuid   |  2 +-
 src/vdbe.c      |  8 +++++++-
 src/where.c     |  5 +++--
 src/wherecode.c |  2 ++
 5 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/manifest b/manifest
index f576dec37b..2c58aedeeb 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Apply\sthe\sBloom\sfilter\sonly\son\sthose\sterms\sof\san\sindex\sthat\shave\sequality\nconstraints.
-D 2021-12-04T14:24:30.524
+C Add\sVdbeCoverage()\smacros.\s\sAdjust\sthe\sBloom-filter\shash\sfunction\sso\sthat\sit\ncorrectly\sdeals\swith\szero-blobs.
+D 2021-12-04T18:45:08.377
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -622,7 +622,7 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
 F src/util.c 30df8356e231dad33be10bb27897655002668343280004ba28c734489414a167
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c d4435e88e8abb076c955ee8262f548afcba77daa28b75f937ed914404bb29be7
+F src/vdbe.c 94af4eba93ad9ca7dd929cd19792ce2a5feb4797a7a64ec3cb3b2277e1467a8b
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
 F src/vdbeInt.h fd1103c7ecec8c84164038c8eacaa4a633cb3c10a2f725aae7bd865d4a4fcceb
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
@@ -637,9 +637,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c d203a7acda584940eee49d80e38067ba354410ca08782792bdf4d6f56ca20aa3
+F src/where.c 2607f8008b27f03003d62ed3b90e1818fda1dd5727daf020c8b3e526aff57269
 F src/whereInt.h 2c9d149b1b41c59f977deb1882e95632800946fcf15c83eaffb227afece04e51
-F src/wherecode.c ab83c1f7c593ab5e9268c51dde80f478e48468e8c1e25866a4f445e7b4282b78
+F src/wherecode.c 2253f91bcded8932a4211238fa7dcf6ab40ff68dbd4c2d88eb4411f12b19552c
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8e078c0e404fe4b3661dd4a11a992a8914c99e2a144cecc417421cbd68fa08cc
-R a78591e6ea873c8de7a64c271c45d204
+P a70429596a3c6a413b03118b0d800521b3526d99dcf88a48acc3189b51518d82
+R f0b7f8e349624b23e895cc40d5e34cdd
 U drh
-Z 73637cbd9e62c30bcdc98bccabee0c03
+Z 186d2bb7a1014e61ff9f3f3c0fb31234
diff --git a/manifest.uuid b/manifest.uuid
index 23eed8d9fa..55275e45b8 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-a70429596a3c6a413b03118b0d800521b3526d99dcf88a48acc3189b51518d82
\ No newline at end of file
+629ee2e3e3125bfd2af435c6713d49e46691213ad15db0a5e93a63a77f1130c2
\ No newline at end of file
diff --git a/src/vdbe.c b/src/vdbe.c
index 9b6a4106d3..acbbee892f 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -695,6 +695,7 @@ static unsigned int filterHash(const Mem *aMem, const Op *pOp){
       h += (u32)(sqlite3VdbeIntValue(p)&0xffffffff);
     }else if( p->flags & (MEM_Str|MEM_Blob) ){
       h += p->n;
+      if( p->flags & MEM_Zero ) h += p->u.nZero;
     }
   }
   return h % (SQLITE_BLOOM_SZ*8);
@@ -8229,7 +8230,12 @@ case OP_Filter: {          /* jump */
   }
 #endif
   assert( h>=0 && h<SQLITE_BLOOM_SZ*8 );
-  if( (pIn1->z[h/8] & (1<<(h&7)))==0 ) goto jump_to_p2;
+  if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){
+    VdbeBranchTaken(1, 2);
+    goto jump_to_p2;
+  }else{
+    VdbeBranchTaken(0, 2);
+  }
   break;
 }
 
diff --git a/src/where.c b/src/where.c
index 87a00ef200..0d205125c8 100644
--- a/src/where.c
+++ b/src/where.c
@@ -986,10 +986,10 @@ static SQLITE_NOINLINE void constructBloomFilter(
   assert( v!=0 );
   iCur = pLevel->iTabCur;
   addrCont = sqlite3VdbeMakeLabel(pParse);
-  addrTop = sqlite3VdbeAddOp0(v, OP_Once);
+  addrTop = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
   pLevel->regFilter = ++pParse->nMem;
   sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
-  sqlite3VdbeAddOp1(v, OP_Rewind, iCur);
+  sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
   pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
   for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
     Expr *pExpr = pTerm->pExpr;
@@ -1016,6 +1016,7 @@ static SQLITE_NOINLINE void constructBloomFilter(
   }
   sqlite3VdbeResolveLabel(v, addrCont);
   sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+3);
+  VdbeCoverage(v);
   sqlite3VdbeJumpHere(v, addrTop);
   sqlite3VdbeJumpHere(v, addrTop+2);
 }
diff --git a/src/wherecode.c b/src/wherecode.c
index fbb40ad4e3..e02a736428 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1517,6 +1517,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
     if( pLevel->regFilter ){
       sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
                            iRowidReg, 1);
+      VdbeCoverage(v);
     }
     sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
     VdbeCoverage(v);
@@ -1846,6 +1847,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       if( pLevel->regFilter ){
         sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
                              regBase, nEq);
+        VdbeCoverage(v);
       }
 
       op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];

From fecbf0a179ae9c2aae0fbca17c80ce776ef78704 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sat, 4 Dec 2021 21:11:18 +0000
Subject: [PATCH 06/18] Miscellaneous cleanup of the new Bloom-filter code.

FossilOrigin-Name: 201b6dd875b0ae2bbc9969b098e88abfc09e37b59e857decd41f2dcbeeb13e01
---
 manifest      | 12 ++++++------
 manifest.uuid |  2 +-
 src/where.c   | 31 +++++++++++++++----------------
 3 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/manifest b/manifest
index 2c58aedeeb..e1808eea5b 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sVdbeCoverage()\smacros.\s\sAdjust\sthe\sBloom-filter\shash\sfunction\sso\sthat\sit\ncorrectly\sdeals\swith\szero-blobs.
-D 2021-12-04T18:45:08.377
+C Miscellaneous\scleanup\sof\sthe\snew\sBloom-filter\scode.
+D 2021-12-04T21:11:18.037
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -637,7 +637,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 2607f8008b27f03003d62ed3b90e1818fda1dd5727daf020c8b3e526aff57269
+F src/where.c 957543456a7d1683d21ed8c890bcdaa6414716082dfb076bb43decdc0f87909e
 F src/whereInt.h 2c9d149b1b41c59f977deb1882e95632800946fcf15c83eaffb227afece04e51
 F src/wherecode.c 2253f91bcded8932a4211238fa7dcf6ab40ff68dbd4c2d88eb4411f12b19552c
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a70429596a3c6a413b03118b0d800521b3526d99dcf88a48acc3189b51518d82
-R f0b7f8e349624b23e895cc40d5e34cdd
+P 629ee2e3e3125bfd2af435c6713d49e46691213ad15db0a5e93a63a77f1130c2
+R 5366235cd370e2a6adf5aabc2d25d6ed
 U drh
-Z 186d2bb7a1014e61ff9f3f3c0fb31234
+Z ab3ac181064c99ca5c8ac656f604bf60
diff --git a/manifest.uuid b/manifest.uuid
index 55275e45b8..5fe0ded6ae 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-629ee2e3e3125bfd2af435c6713d49e46691213ad15db0a5e93a63a77f1130c2
\ No newline at end of file
+201b6dd875b0ae2bbc9969b098e88abfc09e37b59e857decd41f2dcbeeb13e01
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index 0d205125c8..c1a538dcc9 100644
--- a/src/where.c
+++ b/src/where.c
@@ -717,9 +717,9 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
 ** index existed.
 */
 static int termCanDriveIndex(
-  WhereTerm *pTerm,              /* WHERE clause term to check */
-  SrcItem *pSrc,                 /* Table we are trying to access */
-  Bitmask notReady               /* Tables in outer loops of the join */
+  const WhereTerm *pTerm,        /* WHERE clause term to check */
+  const SrcItem *pSrc,           /* Table we are trying to access */
+  const Bitmask notReady         /* Tables in outer loops of the join */
 ){
   char aff;
   if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
@@ -752,9 +752,9 @@ static int termCanDriveIndex(
 */
 static SQLITE_NOINLINE void constructAutomaticIndex(
   Parse *pParse,              /* The parsing context */
-  WhereClause *pWC,           /* The WHERE clause */
-  SrcItem *pSrc,              /* The FROM clause term to get the next index */
-  Bitmask notReady,           /* Mask of cursors that are not available */
+  const WhereClause *pWC,     /* The WHERE clause */
+  const SrcItem *pSrc,        /* The FROM clause term to get the next index */
+  const Bitmask notReady,     /* Mask of cursors that are not available */
   WhereLevel *pLevel          /* Write new index here */
 ){
   int nKeyCol;                /* Number of columns in the constructed index */
@@ -969,16 +969,16 @@ end_auto_index_create:
 ** Create a Bloom filter for the WhereLevel in the parameter.
 */
 static SQLITE_NOINLINE void constructBloomFilter(
-  WhereInfo *pWInfo,          /* The WHERE clause */
+  const WhereInfo *pWInfo,    /* The WHERE clause */
   WhereLevel *pLevel          /* Make a Bloom filter for this FROM term */
 ){
   int addrTop;
   int addrCont;
-  WhereTerm *pTerm;
-  WhereTerm *pWCEnd;
+  const WhereTerm *pTerm;
+  const WhereTerm *pWCEnd;
   Parse *pParse = pWInfo->pParse;
   Vdbe *v = pParse->pVdbe;
-  WhereLoop *pLoop = pLevel->pWLoop;
+  const WhereLoop *pLoop = pLevel->pWLoop;
   int iCur;
   
 
@@ -4915,21 +4915,20 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
 ** down where the code for each WhereLoop is generated.
 */
 static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
-  WhereInfo *pWInfo,
-  sqlite3 *db
+  const WhereInfo *pWInfo
 ){
   int i;
   LogEst nSearch;
   SrcItem *pItem;
 
   assert( pWInfo->nLevel>=2 );
-  assert( OptimizationEnabled(db, SQLITE_BloomFilter) );
+  assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) );
   nSearch = pWInfo->a[0].pWLoop->nOut;
   for(i=1; i<pWInfo->nLevel; i++){
     WhereLoop *pLoop = pWInfo->a[i].pWLoop;
-    if( (pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0
+    if( pLoop->nOut<0
+     && (pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0
      && (pLoop->wsFlags & WHERE_COLUMN_EQ)!=0
-     && pLoop->nOut<0
      && nSearch > (pItem = &pWInfo->pTabList->a[pLoop->iTab])->pTab->nRowLogEst
      && (pItem->fg.jointype & JT_LEFT)==0
     ){
@@ -5336,7 +5335,7 @@ WhereInfo *sqlite3WhereBegin(
   if( pWInfo->nLevel>=2
    && OptimizationEnabled(db, SQLITE_BloomFilter)
   ){
-    whereCheckIfBloomFilterIsUseful(pWInfo, db);
+    whereCheckIfBloomFilterIsUseful(pWInfo);
   }
 
 #if defined(WHERETRACE_ENABLED)

From 35685d3e5e0e28a7631616809be450b090029482 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sun, 5 Dec 2021 00:45:55 +0000
Subject: [PATCH 07/18] Try to run all Bloom filters before any Seeks.  This
 gives a small performance gain on the Star-Schema Benchmark.

FossilOrigin-Name: 5be2470f8755ef454f813c880e659bdbf82f2396be9320cf3079cd4ca8e81a19
---
 manifest        | 16 +++++++-------
 manifest.uuid   |  2 +-
 src/where.c     |  8 ++++---
 src/whereInt.h  |  1 +
 src/wherecode.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/manifest b/manifest
index e1808eea5b..1a9a4b84ff 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Miscellaneous\scleanup\sof\sthe\snew\sBloom-filter\scode.
-D 2021-12-04T21:11:18.037
+C Try\sto\srun\sall\sBloom\sfilters\sbefore\sany\sSeeks.\s\sThis\sgives\sa\ssmall\sperformance\ngain\son\sthe\sStar-Schema\sBenchmark.
+D 2021-12-05T00:45:55.528
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -637,9 +637,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 957543456a7d1683d21ed8c890bcdaa6414716082dfb076bb43decdc0f87909e
-F src/whereInt.h 2c9d149b1b41c59f977deb1882e95632800946fcf15c83eaffb227afece04e51
-F src/wherecode.c 2253f91bcded8932a4211238fa7dcf6ab40ff68dbd4c2d88eb4411f12b19552c
+F src/where.c 3d29b27e345a28f76ecb20b0c789a224e9dc74534363c3ddbc44c283eb4cb708
+F src/whereInt.h 3c634e184de128d99c2b017f899f2309c77330de54193ff970597e9acdeaff3b
+F src/wherecode.c 48b456c910d552e1edf098071546c523837356ba0e3703d7b8fd9fc819da1c9b
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 629ee2e3e3125bfd2af435c6713d49e46691213ad15db0a5e93a63a77f1130c2
-R 5366235cd370e2a6adf5aabc2d25d6ed
+P 201b6dd875b0ae2bbc9969b098e88abfc09e37b59e857decd41f2dcbeeb13e01
+R 8ad2d75d69a2ffc06bdf1b52eb7537e1
 U drh
-Z ab3ac181064c99ca5c8ac656f604bf60
+Z 42400fd1219d022ab29b577e6d964b89
diff --git a/manifest.uuid b/manifest.uuid
index 5fe0ded6ae..cca08a44eb 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-201b6dd875b0ae2bbc9969b098e88abfc09e37b59e857decd41f2dcbeeb13e01
\ No newline at end of file
+5be2470f8755ef454f813c880e659bdbf82f2396be9320cf3079cd4ca8e81a19
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index c1a538dcc9..2f7eb06097 100644
--- a/src/where.c
+++ b/src/where.c
@@ -968,7 +968,7 @@ end_auto_index_create:
 /*
 ** Create a Bloom filter for the WhereLevel in the parameter.
 */
-static SQLITE_NOINLINE void constructBloomFilter(
+SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
   const WhereInfo *pWInfo,    /* The WHERE clause */
   WhereLevel *pLevel          /* Make a Bloom filter for this FROM term */
 ){
@@ -1003,16 +1003,18 @@ static SQLITE_NOINLINE void constructBloomFilter(
     int r1 = sqlite3GetTempReg(pParse);
     sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
     sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1);
+    sqlite3ReleaseTempReg(pParse, r1);
   }else{
     Index *pIdx = pLoop->u.btree.pIndex;
-    int r1 = sqlite3GetTempRange(pParse, pIdx->nKeyCol);
     int n = pLoop->u.btree.nEq;
+    int r1 = sqlite3GetTempRange(pParse, n);
     int jj;
     for(jj=0; jj<n; jj++){
       int iCol = pIdx->aiColumn[jj];
       sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj);
     }
     sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
+    sqlite3ReleaseTempRange(pParse, r1, n);
   }
   sqlite3VdbeResolveLabel(v, addrCont);
   sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+3);
@@ -5533,7 +5535,7 @@ WhereInfo *sqlite3WhereBegin(
                   &pTabList->a[pLevel->iFrom], notReady, pLevel);
 #endif
       }else{
-        constructBloomFilter(pWInfo, pLevel);
+        sqlite3ConstructBloomFilter(pWInfo, pLevel);
       }
       if( db->mallocFailed ) goto whereBeginError;
     }
diff --git a/src/whereInt.h b/src/whereInt.h
index fbe84787f9..19261ca397 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -484,6 +484,7 @@ struct WhereInfo {
 ** where.c:
 */
 Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
+void sqlite3ConstructBloomFilter(const WhereInfo*, WhereLevel*);
 #ifdef WHERETRACE_ENABLED
 void sqlite3WhereClausePrint(WhereClause *pWC);
 void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
diff --git a/src/wherecode.c b/src/wherecode.c
index e02a736428..adc7d5b8e1 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1304,6 +1304,60 @@ static void whereApplyPartialIndexConstraints(
   }
 }
 
+#if 1
+/*
+** An OP_Filter has just been generated, but the corresponding
+** index search has not yet been performed.  This routine
+** checks to see if there are additional WHERE_BLOOMFILTER in
+** inner loops that can be evaluated right away, and if there are,
+** it evaluates those filters as well, and removes the WHERE_BLOOMFILTER
+** tag.
+*/
+static SQLITE_NOINLINE void filterPullDown(
+  Parse *pParse,       /* Parsing context */
+  WhereInfo *pWInfo,   /* Complete information about the WHERE clause */
+  int iLevel,          /* Which level of pWInfo->a[] should be coded */
+  int addrNxt,         /* Jump here to bypass inner loops */
+  Bitmask notReady     /* Loops that are not ready */
+){
+  while( ++iLevel < pWInfo->nLevel ){
+    WhereLevel *pLevel = &pWInfo->a[iLevel];
+    WhereLoop *pLoop = pLevel->pWLoop;
+    if( (pLoop->wsFlags & WHERE_BLOOMFILTER)==0 ) continue;
+    if( pLoop->prereq & notReady ) continue;
+    sqlite3ConstructBloomFilter(pWInfo, &pWInfo->a[iLevel]);
+    if( pLoop->wsFlags & WHERE_IPK ){
+      WhereTerm *pTerm = pLoop->aLTerm[0];
+      int r1, regRowid;
+      assert( pTerm!=0 );
+      assert( pTerm->pExpr!=0 );
+      testcase( pTerm->wtFlags & TERM_VIRTUAL );
+      r1 = sqlite3GetTempReg(pParse);
+      regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, r1);
+      if( regRowid!=r1 ) sqlite3ReleaseTempReg(pParse, r1);
+      sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter,
+                           addrNxt, regRowid, 1);
+      VdbeCoverage(pParse->pVdbe);
+    }else{
+      u16 nEq = pLoop->u.btree.nEq;
+      int r1;
+      char *zStartAff;
+
+      assert( pLoop->wsFlags & WHERE_INDEXED );
+      r1 = codeAllEqualityTerms(pParse,pLevel,0,0,&zStartAff);
+      codeApplyAffinity(pParse, r1, nEq, zStartAff);
+      sqlite3DbFree(pParse->db, zStartAff);
+      sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter,
+                           addrNxt, r1, nEq);
+      VdbeCoverage(pParse->pVdbe);
+    }
+    pLoop->wsFlags &= ~WHERE_BLOOMFILTER;
+  }
+}
+#else
+#define filterPullDown(A,B,C,D,E)
+#endif
+
 /*
 ** Generate code for the start of the iLevel-th loop in the WHERE clause
 ** implementation described by pWInfo.
@@ -1518,6 +1572,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
                            iRowidReg, 1);
       VdbeCoverage(v);
+      filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady);
     }
     sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
     VdbeCoverage(v);
@@ -1848,6 +1903,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
         sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
                              regBase, nEq);
         VdbeCoverage(v);
+        filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady);
       }
 
       op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];

From 6ae49e67cc6e40538d1de2e0239dbdd59e487814 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Sun, 5 Dec 2021 20:19:47 +0000
Subject: [PATCH 08/18] Run as many Bloom filters as possible before index
 lookups.

FossilOrigin-Name: 06f6fefd67086896bc49272c6319545ff6c6792f18babe23aced27b60b032119
---
 manifest        |  18 +++----
 manifest.uuid   |   2 +-
 src/sqliteInt.h |   1 +
 src/where.c     | 129 ++++++++++++++++++++++++++++++------------------
 src/whereInt.h  |   7 ++-
 src/wherecode.c |  77 +++++++++++++++++++++++------
 6 files changed, 158 insertions(+), 76 deletions(-)

diff --git a/manifest b/manifest
index 1a9a4b84ff..47992d2a2e 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Try\sto\srun\sall\sBloom\sfilters\sbefore\sany\sSeeks.\s\sThis\sgives\sa\ssmall\sperformance\ngain\son\sthe\sStar-Schema\sBenchmark.
-D 2021-12-05T00:45:55.528
+C Run\sas\smany\sBloom\sfilters\sas\spossible\sbefore\sindex\slookups.
+D 2021-12-05T20:19:47.744
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -555,7 +555,7 @@ F src/shell.c.in e7ee6517544d075d9f06ee2571567026b89cf9fbeef16a74918019b1cb42576
 F src/sqlite.h.in 5cd209ac7dc4180f0e19292846f40440b8488015849ca0110c70b906b57d68f0
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h 4a19ff088cc3bb1467cca94011eaf7e8432476124bbb72c77053d9f1e6b0a6c7
+F src/sqliteInt.h 178eb899c1edc08dcddf37e79dfaa39404a1f5d44a1d512509cd5d41867aa836
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -637,9 +637,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 3d29b27e345a28f76ecb20b0c789a224e9dc74534363c3ddbc44c283eb4cb708
-F src/whereInt.h 3c634e184de128d99c2b017f899f2309c77330de54193ff970597e9acdeaff3b
-F src/wherecode.c 48b456c910d552e1edf098071546c523837356ba0e3703d7b8fd9fc819da1c9b
+F src/where.c b07c5eefecffa1b69b91c366a83c69d01a83f1c900b9d9b1ffb6eb5ab59902a1
+F src/whereInt.h 5c6601d6d0b7b8936482506d2d835981cc6efcd8e106a829893a27a14cfb10b8
+F src/wherecode.c fa667db48db1077b42731bfd97e9181b39409ffdc7051162ecae6895ca71ad2c
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 201b6dd875b0ae2bbc9969b098e88abfc09e37b59e857decd41f2dcbeeb13e01
-R 8ad2d75d69a2ffc06bdf1b52eb7537e1
+P 5be2470f8755ef454f813c880e659bdbf82f2396be9320cf3079cd4ca8e81a19
+R da3cb867f9ab99abba060de93457c8d9
 U drh
-Z 42400fd1219d022ab29b577e6d964b89
+Z d87a509afa829d1cf21b5a6dcadef441
diff --git a/manifest.uuid b/manifest.uuid
index cca08a44eb..669a897f37 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-5be2470f8755ef454f813c880e659bdbf82f2396be9320cf3079cd4ca8e81a19
\ No newline at end of file
+06f6fefd67086896bc49272c6319545ff6c6792f18babe23aced27b60b032119
\ No newline at end of file
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index e96947e81d..896b2aa422 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1762,6 +1762,7 @@ struct sqlite3 {
 #define SQLITE_OmitOrderBy    0x00040000 /* Omit pointless ORDER BY */
    /* TH3 expects this value  ^^^^^^^^^^ to be 0x40000. Coordinate any change */
 #define SQLITE_BloomFilter    0x00080000 /* Use a Bloom filter on searches */
+#define SQLITE_BloomPulldown  0x00100000 /* Run Bloom filters early */
 #define SQLITE_AllOpts        0xffffffff /* All optimizations */
 
 /*
diff --git a/src/where.c b/src/where.c
index 2f7eb06097..1d88c3ed30 100644
--- a/src/where.c
+++ b/src/where.c
@@ -966,61 +966,92 @@ end_auto_index_create:
 #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
 
 /*
-** Create a Bloom filter for the WhereLevel in the parameter.
+** Generate bytecode that will initialize a Bloom filter that is appropriate
+** for pLevel.
+**
+** If there are inner loops within pLevel that have the WHERE_BLOOMFILTER
+** flag set, initialize a Bloomfilter for them as well.  Except don't do
+** this recursive initialization if the SQLITE_BloomPulldown optimization has
+** been turned off.
+**
+** When the Bloom filter is initialized, the WHERE_BLOOMFILTER flag is cleared
+** from the loop, but the regFilter value is set to a register that implements
+** the Bloom filter.  When regFilter is positive, the
+** sqlite3WhereCodeOneLoopStart() will generate code to test the Bloom filter
+** and skip the subsequence B-Tree seek if the Bloom filter indicates that
+** no matching rows exist.
+**
+** This routine may only be called if it has previously been determined that
+** the loop would benefit from a Bloom filter, and the WHERE_BLOOMFILTER bit
+** is set.
 */
-SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
-  const WhereInfo *pWInfo,    /* The WHERE clause */
-  WhereLevel *pLevel          /* Make a Bloom filter for this FROM term */
+static SQLITE_NOINLINE void constructBloomFilter(
+  WhereInfo *pWInfo,    /* The WHERE clause */
+  int iLevel,           /* Index in pWInfo->a[] that is pLevel */
+  WhereLevel *pLevel    /* Make a Bloom filter for this FROM term */
 ){
-  int addrTop;
-  int addrCont;
-  const WhereTerm *pTerm;
-  const WhereTerm *pWCEnd;
-  Parse *pParse = pWInfo->pParse;
-  Vdbe *v = pParse->pVdbe;
-  const WhereLoop *pLoop = pLevel->pWLoop;
-  int iCur;
-  
+  int addrOnce;                        /* Address of opening OP_Once */
+  int addrTop;                         /* Address of OP_Rewind */
+  int addrCont;                        /* Jump here to skip a row */
+  const WhereTerm *pTerm;              /* For looping over WHERE clause terms */
+  const WhereTerm *pWCEnd;             /* Last WHERE clause term */
+  Parse *pParse = pWInfo->pParse;      /* Parsing context */
+  Vdbe *v = pParse->pVdbe;             /* VDBE under construction */
+  WhereLoop *pLoop = pLevel->pWLoop;   /* The loop being coded */
+  int iCur;                            /* Cursor for table getting the filter */
 
   assert( pLoop!=0 );
   assert( v!=0 );
-  iCur = pLevel->iTabCur;
-  addrCont = sqlite3VdbeMakeLabel(pParse);
-  addrTop = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
-  pLevel->regFilter = ++pParse->nMem;
-  sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
-  sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
-  pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
-  for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
-    Expr *pExpr = pTerm->pExpr;
-    if( (pTerm->wtFlags & TERM_VIRTUAL)==0
-     && sqlite3ExprIsTableConstant(pExpr, iCur)
-    ){
-      sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
+  assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
+
+  addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+  do{
+    sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
+    addrCont = sqlite3VdbeMakeLabel(pParse);
+    iCur = pLevel->iTabCur;
+    pLevel->regFilter = ++pParse->nMem;
+    sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
+    addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+    pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
+    for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
+      Expr *pExpr = pTerm->pExpr;
+      if( (pTerm->wtFlags & TERM_VIRTUAL)==0
+       && sqlite3ExprIsTableConstant(pExpr, iCur)
+      ){
+        sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
+      }
     }
-  }
-  if( pLoop->wsFlags & WHERE_IPK ){
-    int r1 = sqlite3GetTempReg(pParse);
-    sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
-    sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1);
-    sqlite3ReleaseTempReg(pParse, r1);
-  }else{
-    Index *pIdx = pLoop->u.btree.pIndex;
-    int n = pLoop->u.btree.nEq;
-    int r1 = sqlite3GetTempRange(pParse, n);
-    int jj;
-    for(jj=0; jj<n; jj++){
-      int iCol = pIdx->aiColumn[jj];
-      sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj);
+    if( pLoop->wsFlags & WHERE_IPK ){
+      int r1 = sqlite3GetTempReg(pParse);
+      sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
+      sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1);
+      sqlite3ReleaseTempReg(pParse, r1);
+    }else{
+      Index *pIdx = pLoop->u.btree.pIndex;
+      int n = pLoop->u.btree.nEq;
+      int r1 = sqlite3GetTempRange(pParse, n);
+      int jj;
+      for(jj=0; jj<n; jj++){
+        int iCol = pIdx->aiColumn[jj];
+        sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj);
+      }
+      sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
+      sqlite3ReleaseTempRange(pParse, r1, n);
     }
-    sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
-    sqlite3ReleaseTempRange(pParse, r1, n);
-  }
-  sqlite3VdbeResolveLabel(v, addrCont);
-  sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+3);
-  VdbeCoverage(v);
-  sqlite3VdbeJumpHere(v, addrTop);
-  sqlite3VdbeJumpHere(v, addrTop+2);
+    sqlite3VdbeResolveLabel(v, addrCont);
+    sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
+    VdbeCoverage(v);
+    sqlite3VdbeJumpHere(v, addrTop);
+    pLoop->wsFlags &= ~WHERE_BLOOMFILTER;
+    if( OptimizationDisabled(pParse->db, SQLITE_BloomPulldown) ) break;
+    while( iLevel < pWInfo->nLevel ){
+      iLevel++;
+      pLevel = &pWInfo->a[iLevel];
+      pLoop = pLevel->pWLoop;
+      if( pLoop && pLoop->wsFlags & WHERE_BLOOMFILTER ) break;
+    }
+  }while( iLevel < pWInfo->nLevel );
+  sqlite3VdbeJumpHere(v, addrOnce);
 }
 
 
@@ -5535,7 +5566,7 @@ WhereInfo *sqlite3WhereBegin(
                   &pTabList->a[pLevel->iFrom], notReady, pLevel);
 #endif
       }else{
-        sqlite3ConstructBloomFilter(pWInfo, pLevel);
+        constructBloomFilter(pWInfo, ii, pLevel);
       }
       if( db->mallocFailed ) goto whereBeginError;
     }
diff --git a/src/whereInt.h b/src/whereInt.h
index 19261ca397..4da6015f16 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -484,7 +484,6 @@ struct WhereInfo {
 ** where.c:
 */
 Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
-void sqlite3ConstructBloomFilter(const WhereInfo*, WhereLevel*);
 #ifdef WHERETRACE_ENABLED
 void sqlite3WhereClausePrint(WhereClause *pWC);
 void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
@@ -507,8 +506,14 @@ int sqlite3WhereExplainOneScan(
   WhereLevel *pLevel,             /* Scan to write OP_Explain opcode for */
   u16 wctrlFlags                  /* Flags passed to sqlite3WhereBegin() */
 );
+int sqlite3WhereExplainBloomFilter(
+  const Parse *pParse,            /* Parse context */
+  const WhereInfo *pWInfo,        /* WHERE clause */
+  const WhereLevel *pLevel        /* Bloom filter on this level */
+);
 #else
 # define sqlite3WhereExplainOneScan(u,v,w,x) 0
+# define sqlite3WhereExplainBloomFilter(u,v,w) 0
 #endif /* SQLITE_OMIT_EXPLAIN */
 #ifdef SQLITE_ENABLE_STMT_SCANSTATUS
 void sqlite3WhereAddScanStatus(
diff --git a/src/wherecode.c b/src/wherecode.c
index adc7d5b8e1..dfe697f244 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -196,9 +196,6 @@ int sqlite3WhereExplainOneScan(
                   pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
     }
 #endif
-    if( flags & WHERE_BLOOMFILTER ){
-      sqlite3_str_appendf(&str, " WITH BLOOM FILTER");
-    }
 #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
     if( pLoop->nOut>=10 ){
       sqlite3_str_appendf(&str, " (~%llu rows)",
@@ -214,6 +211,52 @@ int sqlite3WhereExplainOneScan(
   }
   return ret;
 }
+
+/*
+** Add a single OP_Explain opcode that describes a Bloom filter.
+**
+** Or if not processing EXPLAIN QUERY PLAN and not in a SQLITE_DEBUG and/or
+** SQLITE_ENABLE_STMT_SCANSTATUS build, then OP_Explain opcodes are not
+** required and this routine is a no-op.
+**
+** If an OP_Explain opcode is added to the VM, its address is returned.
+** Otherwise, if no OP_Explain is coded, zero is returned.
+*/
+int sqlite3WhereExplainBloomFilter(
+  const Parse *pParse,               /* Parse context */
+  const WhereInfo *pWInfo,           /* WHERE clause */
+  const WhereLevel *pLevel           /* Bloom filter on this level */
+){
+  int ret = 0;
+#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+  if( sqlite3ParseToplevel(pParse)->explain==2 )
+#endif
+  {
+    SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom];
+    Vdbe *v = pParse->pVdbe;      /* VM being constructed */
+    sqlite3 *db = pParse->db;     /* Database handle */
+    char *zMsg;                   /* Text to add to EQP output */
+    int i;                        /* Loop counter */
+    WhereLoop *pLoop;             /* The where loop */
+    StrAccum str;                 /* EQP output string */
+    char zBuf[100];               /* Initial space for EQP output string */
+
+    sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+    str.printfFlags = SQLITE_PRINTF_INTERNAL;
+    sqlite3_str_appendf(&str, "BLOOM FILTER ON %S(", pItem);
+    pLoop = pLevel->pWLoop;
+    for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){
+      const char *z = pItem->pTab->aCol[i].zCnName;
+      if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5);
+      sqlite3_str_appendf(&str, "%s=?", z);
+    }
+    sqlite3_str_append(&str, ")", 1);
+    zMsg = sqlite3StrAccumFinish(&str);
+    ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
+                            pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
+  }
+  return ret;
+}
 #endif /* SQLITE_OMIT_EXPLAIN */
 
 #ifdef SQLITE_ENABLE_STMT_SCANSTATUS
@@ -1304,14 +1347,20 @@ static void whereApplyPartialIndexConstraints(
   }
 }
 
-#if 1
 /*
-** An OP_Filter has just been generated, but the corresponding
-** index search has not yet been performed.  This routine
-** checks to see if there are additional WHERE_BLOOMFILTER in
-** inner loops that can be evaluated right away, and if there are,
-** it evaluates those filters as well, and removes the WHERE_BLOOMFILTER
-** tag.
+** This routine is called right after An OP_Filter has been generated and
+** before the corresponding index search has been performed.  This routine
+** checks to see if there are additional Bloom filters in inner loops that
+** can be checked prior to doing the index lookup.  If there are available
+** inner-loop Bloom filters, then evaluate those filters now, before the
+** index lookup.  The idea is that a Bloom filter check is way faster than
+** an index lookup, and the Bloom filter might return false, meaning that
+** the index lookup can be skipped.
+**
+** We know that an inner loop uses a Bloom filter because it has the
+** WhereLevel.regFilter set.  If an inner-loop Bloom filter is checked,
+** then clear the WhereLoeve.regFilter value to prevent the Bloom filter
+** from being checked a second time when the inner loop is evaluated.
 */
 static SQLITE_NOINLINE void filterPullDown(
   Parse *pParse,       /* Parsing context */
@@ -1323,9 +1372,8 @@ static SQLITE_NOINLINE void filterPullDown(
   while( ++iLevel < pWInfo->nLevel ){
     WhereLevel *pLevel = &pWInfo->a[iLevel];
     WhereLoop *pLoop = pLevel->pWLoop;
-    if( (pLoop->wsFlags & WHERE_BLOOMFILTER)==0 ) continue;
+    if( pLevel->regFilter==0 ) continue;
     if( pLoop->prereq & notReady ) continue;
-    sqlite3ConstructBloomFilter(pWInfo, &pWInfo->a[iLevel]);
     if( pLoop->wsFlags & WHERE_IPK ){
       WhereTerm *pTerm = pLoop->aLTerm[0];
       int r1, regRowid;
@@ -1351,12 +1399,9 @@ static SQLITE_NOINLINE void filterPullDown(
                            addrNxt, r1, nEq);
       VdbeCoverage(pParse->pVdbe);
     }
-    pLoop->wsFlags &= ~WHERE_BLOOMFILTER;
+    pLevel->regFilter = 0;
   }
 }
-#else
-#define filterPullDown(A,B,C,D,E)
-#endif
 
 /*
 ** Generate code for the start of the iLevel-th loop in the WHERE clause

From 5baaf40af1898bdcfcdb5c30405b6da1342e2011 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Mon, 6 Dec 2021 13:07:28 +0000
Subject: [PATCH 09/18] Attempt to vary the size of Bloom filters based on an
 estimate of how many keys the filter will hold.

FossilOrigin-Name: a7adcf69088cba4b86cc5731a45c9a5263af4355bc0a38f5225cab421c915f7f
---
 manifest        | 18 +++++------
 manifest.uuid   |  2 +-
 src/malloc.c    | 19 ++++++++++++
 src/sqliteInt.h |  1 +
 src/vdbe.c      | 81 +++++++++++++++++++++++++++++++++++++------------
 src/where.c     |  5 +--
 6 files changed, 95 insertions(+), 31 deletions(-)

diff --git a/manifest b/manifest
index 47992d2a2e..648c9a9161 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Run\sas\smany\sBloom\sfilters\sas\spossible\sbefore\sindex\slookups.
-D 2021-12-05T20:19:47.744
+C Attempt\sto\svary\sthe\ssize\sof\sBloom\sfilters\sbased\son\san\sestimate\sof\show\smany\nkeys\sthe\sfilter\swill\shold.
+D 2021-12-06T13:07:28.112
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -515,7 +515,7 @@ F src/insert.c e0293a6f686e18cb2c9dd0619a731518e0109d7e1f1db1932974659e7843cfd1
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c e1dcff1c916bf6834e150b492eddda5d9792453182d2ad64294d2266b6e93c4c
 F src/main.c 7bd4fdc41ef53535271a1816ff043ba153cda03842b444b6e2f57b27b2cb9090
-F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b
+F src/malloc.c 183c2bf45cee1589254e4047e220f1ffbcc0a3bc8e4fe46fe64ba5db447a79af
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
 F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
@@ -555,7 +555,7 @@ F src/shell.c.in e7ee6517544d075d9f06ee2571567026b89cf9fbeef16a74918019b1cb42576
 F src/sqlite.h.in 5cd209ac7dc4180f0e19292846f40440b8488015849ca0110c70b906b57d68f0
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h 178eb899c1edc08dcddf37e79dfaa39404a1f5d44a1d512509cd5d41867aa836
+F src/sqliteInt.h ab40ea9c294c656e0d6ab14e67d58f10b015a77e962dd075fdbe3ea3cc1a976b
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -622,7 +622,7 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
 F src/util.c 30df8356e231dad33be10bb27897655002668343280004ba28c734489414a167
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c 94af4eba93ad9ca7dd929cd19792ce2a5feb4797a7a64ec3cb3b2277e1467a8b
+F src/vdbe.c 6176125ea038f593597b5897898328142b5253201d321369df74e187b2b1abaa
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
 F src/vdbeInt.h fd1103c7ecec8c84164038c8eacaa4a633cb3c10a2f725aae7bd865d4a4fcceb
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
@@ -637,7 +637,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c b07c5eefecffa1b69b91c366a83c69d01a83f1c900b9d9b1ffb6eb5ab59902a1
+F src/where.c 04ead529a272341a4cae3ef0dcd2f7675d433627acc5fb87fed1407e7b3d8614
 F src/whereInt.h 5c6601d6d0b7b8936482506d2d835981cc6efcd8e106a829893a27a14cfb10b8
 F src/wherecode.c fa667db48db1077b42731bfd97e9181b39409ffdc7051162ecae6895ca71ad2c
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
@@ -1933,7 +1933,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5be2470f8755ef454f813c880e659bdbf82f2396be9320cf3079cd4ca8e81a19
-R da3cb867f9ab99abba060de93457c8d9
+P 06f6fefd67086896bc49272c6319545ff6c6792f18babe23aced27b60b032119
+R 5f90145148ed31800619d7b25d77beb7
 U drh
-Z d87a509afa829d1cf21b5a6dcadef441
+Z 3013773c2d9a03770ac02d1fdd22abd3
diff --git a/manifest.uuid b/manifest.uuid
index 669a897f37..7894b82c00 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-06f6fefd67086896bc49272c6319545ff6c6792f18babe23aced27b60b032119
\ No newline at end of file
+a7adcf69088cba4b86cc5731a45c9a5263af4355bc0a38f5225cab421c915f7f
\ No newline at end of file
diff --git a/src/malloc.c b/src/malloc.c
index 932cecc210..ab9b37ddad 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -211,6 +211,25 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
   return mx;
 }
 
+/*
+** Return an estimate of the amount of unallocated memory.
+**
+** This the hard heap limit minus the current memory usage.  It might
+** not be possible to allocate this much memory all at once.  This is
+** only an estimate.
+*/
+sqlite3_int64 sqlite3EstMemoryAvailable(void){
+  sqlite3_int64 n;
+  sqlite3_mutex_enter(mem0.mutex);
+  n = mem0.alarmThreshold;
+  if( n<=0 ) n = mem0.hardLimit;
+  sqlite3_mutex_leave(mem0.mutex);
+  if( n<=0 ) n = LARGEST_INT64;
+  n -= sqlite3_memory_used();
+  if( n<0 ) n = 0;
+  return n;
+}
+
 /*
 ** Trigger the alarm 
 */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 896b2aa422..9091f02115 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -4293,6 +4293,7 @@ void sqlite3MemSetDefault(void);
 void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
 #endif
 int sqlite3HeapNearlyFull(void);
+sqlite3_int64 sqlite3EstMemoryAvailable(void);
 
 /*
 ** On systems with ample stack space and that support alloca(), make
diff --git a/src/vdbe.c b/src/vdbe.c
index acbbee892f..487d731af3 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -672,17 +672,29 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
 }
 
 /*
-** Default size of a bloom filter, in bytes
+** The minimum size (in bytes) for a Bloom filter.
+**
+** No Bloom filter will be smaller than this many bytes.  But they
+** may be larger.
 */
-#define SQLITE_BLOOM_SZ 10000
+#ifndef SQLITE_BLOOM_MIN
+# define SQLITE_BLOOM_MIN 10000
+#endif
+
+/*
+** The maximum size (in bytes) for a Bloom filter.
+*/
+#ifndef SQLITE_BLOOM_MAX
+# define SQLITE_BLOOM_MAX 1000000
+#endif
 
 /*
 ** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning
 ** with pOp->p3.  Return the hash.
 */
-static unsigned int filterHash(const Mem *aMem, const Op *pOp){
+static u64 filterHash(const Mem *aMem, const Op *pOp){
   int i, mx;
-  u32 h = 0;
+  u64 h = 0;
 
   i = pOp->p3;
   assert( pOp->p4type==P4_INT32 );
@@ -690,15 +702,15 @@ static unsigned int filterHash(const Mem *aMem, const Op *pOp){
   for(i=pOp->p3, mx=i+pOp->p4.i; i<mx; i++){
     const Mem *p = &aMem[i];
     if( p->flags & (MEM_Int|MEM_IntReal) ){
-      h += (u32)(p->u.i&0xffffffff);
+      h += p->u.i;
     }else if( p->flags & MEM_Real ){
-      h += (u32)(sqlite3VdbeIntValue(p)&0xffffffff);
+      h += sqlite3VdbeIntValue(p);
     }else if( p->flags & (MEM_Str|MEM_Blob) ){
       h += p->n;
       if( p->flags & MEM_Zero ) h += p->u.nZero;
     }
   }
-  return h % (SQLITE_BLOOM_SZ*8);
+  return h;
 }
 
 /*
@@ -8157,15 +8169,44 @@ case OP_Function: {            /* group */
   break;
 }
 
-/* Opcode: FilterInit P1 * * * *
-** Synopsis: filter(P1) = empty
+/* Opcode: FilterInit P1 P2 * * *
 **
 ** Initialize register P1 so that is an empty bloom filter.
+**
+** If P2 is positive, it is a register that holds an estimate on
+** the number of entries to be added to the Bloom filter.  The
+** Bloom filter is sized accordingly.  If P2 is zero or negative,
+** then a default-size Bloom filter is created.
+**
+** It is ok for P1 and P2 to be the same register.  In that case the
+** integer value originally in that register will be overwritten
+** with the new empty bloom filter.
 */
 case OP_FilterInit: {
+  i64 n, mx;
   assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
   pIn1 = &aMem[pOp->p1];
-  sqlite3VdbeMemSetZeroBlob(pIn1, SQLITE_BLOOM_SZ);
+  if( pOp->p2>0 ){
+    assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
+    n = sqlite3VdbeIntValue(&aMem[pOp->p2]);
+    if( n<SQLITE_BLOOM_MIN ){
+      n = SQLITE_BLOOM_MIN;
+    }else if( n>SQLITE_BLOOM_MAX ){
+      n = SQLITE_BLOOM_MAX;
+    }
+  }else{
+    n = SQLITE_BLOOM_MIN;
+  }
+  mx = sqlite3EstMemoryAvailable()/2;
+  if( n>mx && mx>SQLITE_BLOOM_MIN ){
+    n = mx;
+  }
+#ifdef SQLITE_DEBUG
+  if( db->flags&SQLITE_VdbeTrace ){
+    printf("Bloom-filter size: %llu bytes\n", n);
+  }
+#endif
+  sqlite3VdbeMemSetZeroBlob(pIn1, n);
   if( sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem;
   break;
 }
@@ -8177,12 +8218,12 @@ case OP_FilterInit: {
 ** add that hash to the bloom filter contained in r[P1].
 */
 case OP_FilterAdd: {
-  u32 h;
+  u64 h;
 
   assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
   pIn1 = &aMem[pOp->p1];
   assert( pIn1->flags & MEM_Blob );
-  assert( pIn1->n==SQLITE_BLOOM_SZ );
+  assert( pIn1->n>0 );
   h = filterHash(aMem, pOp);
 #ifdef SQLITE_DEBUG
   if( db->flags&SQLITE_VdbeTrace ){
@@ -8190,10 +8231,10 @@ case OP_FilterAdd: {
     for(ii=pOp->p3; ii<pOp->p3+pOp->p4.i; ii++){
       registerTrace(ii, &aMem[ii]);
     }
-    printf("hash = %u\n", h);
+    printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n));
   }
 #endif
-  assert( h>=0 && h<SQLITE_BLOOM_SZ*8 );
+  h %= pIn1->n;
   pIn1->z[h/8] |= 1<<(h&7);
   break;
 }
@@ -8213,12 +8254,14 @@ case OP_FilterAdd: {
 ** false positive - if the jump is taken when it should fall through.
 */
 case OP_Filter: {          /* jump */
-  u32 h;
+  u64 h;
 
   assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
   pIn1 = &aMem[pOp->p1];
-  assert( pIn1->flags & MEM_Blob );
-  assert( pIn1->n==SQLITE_BLOOM_SZ );
+  if( (pIn1->flags & MEM_Blob)==0 || NEVER(pIn1->n<=0) ){
+    VdbeBranchTaken(0, 2);
+    break;
+  }
   h = filterHash(aMem, pOp);
 #ifdef SQLITE_DEBUG
   if( db->flags&SQLITE_VdbeTrace ){
@@ -8226,10 +8269,10 @@ case OP_Filter: {          /* jump */
     for(ii=pOp->p3; ii<pOp->p3+pOp->p4.i; ii++){
       registerTrace(ii, &aMem[ii]);
     }
-    printf("hash = %u\n", h);
+    printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n));
   }
 #endif
-  assert( h>=0 && h<SQLITE_BLOOM_SZ*8 );
+  h %= pIn1->n;
   if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){
     VdbeBranchTaken(1, 2);
     goto jump_to_p2;
diff --git a/src/where.c b/src/where.c
index 1d88c3ed30..233e77b6a7 100644
--- a/src/where.c
+++ b/src/where.c
@@ -1010,8 +1010,9 @@ static SQLITE_NOINLINE void constructBloomFilter(
     addrCont = sqlite3VdbeMakeLabel(pParse);
     iCur = pLevel->iTabCur;
     pLevel->regFilter = ++pParse->nMem;
-    sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
     addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+    sqlite3VdbeAddOp3(v, OP_Count, iCur, pLevel->regFilter, 1);
+    sqlite3VdbeAddOp2(v, OP_FilterInit, pLevel->regFilter, pLevel->regFilter);
     pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
     for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
       Expr *pExpr = pTerm->pExpr;
@@ -1039,7 +1040,7 @@ static SQLITE_NOINLINE void constructBloomFilter(
       sqlite3ReleaseTempRange(pParse, r1, n);
     }
     sqlite3VdbeResolveLabel(v, addrCont);
-    sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
+    sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+3);
     VdbeCoverage(v);
     sqlite3VdbeJumpHere(v, addrTop);
     pLoop->wsFlags &= ~WHERE_BLOOMFILTER;

From 50fb7e09b4247ce34a0b0266614303d00f929def Mon Sep 17 00:00:00 2001
From: drh <>
Date: Mon, 6 Dec 2021 20:16:53 +0000
Subject: [PATCH 10/18] Omit the OP_FilterInit opcode.  Use OP_Blob to
 initialize each Bloom filter instead.  Size the Bloom filter based on
 sqlite_stat1 size estimates rather than a run-time measurement for improved
 testability.

FossilOrigin-Name: 8a9036ee617a6ad93bfe827b0789773c49d3d45b085cb76fa4b9b20a41b79b97
---
 manifest        | 20 +++++++++---------
 manifest.uuid   |  2 +-
 src/malloc.c    |  2 ++
 src/sqliteInt.h |  7 +++----
 src/util.c      | 14 -------------
 src/vdbe.c      | 54 +++++++++----------------------------------------
 src/where.c     | 35 ++++++++++++++++++++++++++++----
 7 files changed, 56 insertions(+), 78 deletions(-)

diff --git a/manifest b/manifest
index 754a38701e..b4f5f5334e 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\strunk\sfixes\sinto\sthe\sbloom-filter\sbranch.
-D 2021-12-06T19:11:31.036
+C Omit\sthe\sOP_FilterInit\sopcode.\s\sUse\sOP_Blob\sto\sinitialize\seach\sBloom\sfilter\ninstead.\s\sSize\sthe\sBloom\sfilter\sbased\son\ssqlite_stat1\ssize\sestimates\srather\nthan\sa\srun-time\smeasurement\sfor\simproved\stestability.
+D 2021-12-06T20:16:53.814
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -516,7 +516,7 @@ F src/insert.c e0293a6f686e18cb2c9dd0619a731518e0109d7e1f1db1932974659e7843cfd1
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c e1dcff1c916bf6834e150b492eddda5d9792453182d2ad64294d2266b6e93c4c
 F src/main.c 1ea70751e6005ab6a9f784730fa0919efaa6639440a287deb73cb711e5aae57a
-F src/malloc.c 183c2bf45cee1589254e4047e220f1ffbcc0a3bc8e4fe46fe64ba5db447a79af
+F src/malloc.c d9172a3946f11384f2fd6a799554ee26c6bb407c4bd0874a456ed485a2e362e4
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
 F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
@@ -556,7 +556,7 @@ F src/shell.c.in 1458b700144c8326fda2514aaddeda49d6f01f1d1ccf7b9b696c53a3535a119
 F src/sqlite.h.in 5cd209ac7dc4180f0e19292846f40440b8488015849ca0110c70b906b57d68f0
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h ab40ea9c294c656e0d6ab14e67d58f10b015a77e962dd075fdbe3ea3cc1a976b
+F src/sqliteInt.h f4fbb14ea32d57b813aabf82f586d2ac042234dd89df1c03281f557907745b98
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -621,9 +621,9 @@ F src/trigger.c 2ef56f0b7b75349a5557d0604b475126329c2e1a02432e7d49c4c710613e8254
 F src/update.c d6f5c7b9e072660757ac7d58175aca11c07cb95ebbb297ae7f38853700f52328
 F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
-F src/util.c 30df8356e231dad33be10bb27897655002668343280004ba28c734489414a167
+F src/util.c 6dfbd0bd1954e9531e1c511e5d20390d7dab9ffbf1e20a37c960d1aaf8582b46
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c 6176125ea038f593597b5897898328142b5253201d321369df74e187b2b1abaa
+F src/vdbe.c 9cc221ebae54417b0b47b4ce6aa6eba7e919153f8ae6307f8d18a6749d453f9c
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
 F src/vdbeInt.h fd1103c7ecec8c84164038c8eacaa4a633cb3c10a2f725aae7bd865d4a4fcceb
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
@@ -638,7 +638,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 04ead529a272341a4cae3ef0dcd2f7675d433627acc5fb87fed1407e7b3d8614
+F src/where.c 21bd1078837afb127827243d7ad549a4b47022ffaa43c5baa74dcac7f89809a7
 F src/whereInt.h 5c6601d6d0b7b8936482506d2d835981cc6efcd8e106a829893a27a14cfb10b8
 F src/wherecode.c fa667db48db1077b42731bfd97e9181b39409ffdc7051162ecae6895ca71ad2c
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a7adcf69088cba4b86cc5731a45c9a5263af4355bc0a38f5225cab421c915f7f bb9b1a15f7e80483162049dfd981d059dc69d03348b521f7ac164a8cd3ae3cc4
-R 0f448dc1f18098ffcb6b2fdc27d44d39
+P edacf8034dc6bd892038c220c480ea512dbb4005db2a6b1f8e679e8a4929c6ed
+R 933b551d7a60e6c29e7de8b534d33928
 U drh
-Z a01a7e207ad8c3ff4f1aefb648f1cc10
+Z ded7cd3093221f3f9f8f684eb1a7389d
diff --git a/manifest.uuid b/manifest.uuid
index 63d6d10d76..0b0fb52902 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-edacf8034dc6bd892038c220c480ea512dbb4005db2a6b1f8e679e8a4929c6ed
\ No newline at end of file
+8a9036ee617a6ad93bfe827b0789773c49d3d45b085cb76fa4b9b20a41b79b97
\ No newline at end of file
diff --git a/src/malloc.c b/src/malloc.c
index ab9b37ddad..9a2eaeec15 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -211,6 +211,7 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
   return mx;
 }
 
+#if 0
 /*
 ** Return an estimate of the amount of unallocated memory.
 **
@@ -229,6 +230,7 @@ sqlite3_int64 sqlite3EstMemoryAvailable(void){
   if( n<0 ) n = 0;
   return n;
 }
+#endif
 
 /*
 ** Trigger the alarm 
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 9091f02115..90e60d30b0 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3927,6 +3927,7 @@ struct Sqlite3Config {
   int iOnceResetThreshold;          /* When to reset OP_Once counters */
   u32 szSorterRef;                  /* Min size in bytes to use sorter-refs */
   unsigned int iPrngSeed;           /* Alternative fixed seed for the PRNG */
+  int iEstCountScale;               /* Multiple RowCountEst() by this amount */
   /* vvvv--- must be last ---vvv */
 #ifdef SQLITE_DEBUG
   sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */
@@ -4293,7 +4294,9 @@ void sqlite3MemSetDefault(void);
 void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
 #endif
 int sqlite3HeapNearlyFull(void);
+#if 0
 sqlite3_int64 sqlite3EstMemoryAvailable(void);
+#endif
 
 /*
 ** On systems with ample stack space and that support alloca(), make
@@ -4780,11 +4783,7 @@ LogEst sqlite3LogEstAdd(LogEst,LogEst);
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 LogEst sqlite3LogEstFromDouble(double);
 #endif
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
-    defined(SQLITE_ENABLE_STAT4) || \
-    defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
 u64 sqlite3LogEstToInt(LogEst);
-#endif
 VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int);
 const char *sqlite3VListNumToName(VList*,int);
 int sqlite3VListNameToNum(VList*,const char*,int);
diff --git a/src/util.c b/src/util.c
index 8452aea665..d93c298116 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1603,14 +1603,8 @@ LogEst sqlite3LogEstFromDouble(double x){
 }
 #endif /* SQLITE_OMIT_VIRTUALTABLE */
 
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
-    defined(SQLITE_ENABLE_STAT4) || \
-    defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
 /*
 ** Convert a LogEst into an integer.
-**
-** Note that this routine is only used when one or more of various
-** non-standard compile-time options is enabled.
 */
 u64 sqlite3LogEstToInt(LogEst x){
   u64 n;
@@ -1618,17 +1612,9 @@ u64 sqlite3LogEstToInt(LogEst x){
   x /= 10;
   if( n>=5 ) n -= 2;
   else if( n>=1 ) n -= 1;
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
-    defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
   if( x>60 ) return (u64)LARGEST_INT64;
-#else
-  /* If only SQLITE_ENABLE_STAT4 is on, then the largest input
-  ** possible to this routine is 310, resulting in a maximum x of 31 */
-  assert( x<=60 );
-#endif
   return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x);
 }
-#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
 
 /*
 ** Add a new name/number pair to a VList.  This might require that the
diff --git a/src/vdbe.c b/src/vdbe.c
index 487d731af3..c2281e70a1 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -1367,12 +1367,18 @@ case OP_SoftNull: {
 ** Synopsis: r[P2]=P4 (len=P1)
 **
 ** P4 points to a blob of data P1 bytes long.  Store this
-** blob in register P2.
+** blob in register P2.  If P4 is a NULL pointer, then construct
+** a zero-filled blob that is P1 bytes long in P2.
 */
 case OP_Blob: {                /* out2 */
   assert( pOp->p1 <= SQLITE_MAX_LENGTH );
   pOut = out2Prerelease(p, pOp);
-  sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0);
+  if( pOp->p4.z==0 ){
+    sqlite3VdbeMemSetZeroBlob(pOut, pOp->p1);
+    if( sqlite3VdbeMemExpandBlob(pOut) ) goto no_mem;
+  }else{
+    sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0);
+  }
   pOut->enc = encoding;
   UPDATE_MAX_BLOBSIZE(pOut);
   break;
@@ -3383,7 +3389,7 @@ case OP_MakeRecord: {
   break;
 }
 
-/* Opcode: Count P1 P2 p3 * *
+/* Opcode: Count P1 P2 P3 * *
 ** Synopsis: r[P2]=count()
 **
 ** Store the number of entries (an integer value) in the table or index 
@@ -8169,48 +8175,6 @@ case OP_Function: {            /* group */
   break;
 }
 
-/* Opcode: FilterInit P1 P2 * * *
-**
-** Initialize register P1 so that is an empty bloom filter.
-**
-** If P2 is positive, it is a register that holds an estimate on
-** the number of entries to be added to the Bloom filter.  The
-** Bloom filter is sized accordingly.  If P2 is zero or negative,
-** then a default-size Bloom filter is created.
-**
-** It is ok for P1 and P2 to be the same register.  In that case the
-** integer value originally in that register will be overwritten
-** with the new empty bloom filter.
-*/
-case OP_FilterInit: {
-  i64 n, mx;
-  assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
-  pIn1 = &aMem[pOp->p1];
-  if( pOp->p2>0 ){
-    assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
-    n = sqlite3VdbeIntValue(&aMem[pOp->p2]);
-    if( n<SQLITE_BLOOM_MIN ){
-      n = SQLITE_BLOOM_MIN;
-    }else if( n>SQLITE_BLOOM_MAX ){
-      n = SQLITE_BLOOM_MAX;
-    }
-  }else{
-    n = SQLITE_BLOOM_MIN;
-  }
-  mx = sqlite3EstMemoryAvailable()/2;
-  if( n>mx && mx>SQLITE_BLOOM_MIN ){
-    n = mx;
-  }
-#ifdef SQLITE_DEBUG
-  if( db->flags&SQLITE_VdbeTrace ){
-    printf("Bloom-filter size: %llu bytes\n", n);
-  }
-#endif
-  sqlite3VdbeMemSetZeroBlob(pIn1, n);
-  if( sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem;
-  break;
-}
-
 /* Opcode: FilterAdd P1 * P3 P4 *
 ** Synopsis: filter(P1) += key(P3@P4)
 **
diff --git a/src/where.c b/src/where.c
index 233e77b6a7..e7246b1040 100644
--- a/src/where.c
+++ b/src/where.c
@@ -911,7 +911,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
   VdbeComment((v, "for %s", pTable->zName));
   if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){
     pLevel->regFilter = ++pParse->nMem;
-    sqlite3VdbeAddOp1(v, OP_FilterInit, pLevel->regFilter);
+    sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter);
   }
 
   /* Fill the automatic index with content */
@@ -1006,13 +1006,39 @@ static SQLITE_NOINLINE void constructBloomFilter(
 
   addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
   do{
+    const SrcItem *pItem;
+    const Table *pTab;
+    int sz;
     sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
     addrCont = sqlite3VdbeMakeLabel(pParse);
     iCur = pLevel->iTabCur;
     pLevel->regFilter = ++pParse->nMem;
+
+    /* The Bloom filter is a Blob held in a register.  Initialize it
+    ** to zero-filled blob of at least 80K bits, but maybe more if the
+    ** estimated size of the table is larger.  We could actually
+    ** measure the size of the table at run-time using OP_Count with
+    ** P3==1 and use that value to initialize the blob.  But that makes
+    ** testing complicated.  By basing the blob size on the value in the
+    ** sqlite_stat1 table, testing is much easier.
+    */
+    pItem = &pWInfo->pTabList->a[pLevel->iFrom];
+    assert( pItem!=0 );
+    pTab = pItem->pTab;
+    assert( pTab!=0 );
+    if( pTab->tabFlags & TF_HasStat1 ){
+      sz = sqlite3LogEstToInt(pItem->pTab->nRowLogEst);
+      if( sz<10000 ){
+        sz = 10000;
+      }else if( sz>10000000 ){
+        sz = 10000000;
+      }
+    }else{
+      sz = 10000;
+    }
+    sqlite3VdbeAddOp2(v, OP_Blob, sz, pLevel->regFilter);
+
     addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
-    sqlite3VdbeAddOp3(v, OP_Count, iCur, pLevel->regFilter, 1);
-    sqlite3VdbeAddOp2(v, OP_FilterInit, pLevel->regFilter, pLevel->regFilter);
     pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
     for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
       Expr *pExpr = pTerm->pExpr;
@@ -1034,13 +1060,14 @@ static SQLITE_NOINLINE void constructBloomFilter(
       int jj;
       for(jj=0; jj<n; jj++){
         int iCol = pIdx->aiColumn[jj];
+        assert( pIdx->pTable==pItem->pTab );
         sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj);
       }
       sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
       sqlite3ReleaseTempRange(pParse, r1, n);
     }
     sqlite3VdbeResolveLabel(v, addrCont);
-    sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+3);
+    sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
     VdbeCoverage(v);
     sqlite3VdbeJumpHere(v, addrTop);
     pLoop->wsFlags &= ~WHERE_BLOOMFILTER;

From 23d41e63f2e4560728e13cefabb4fd411c687672 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Mon, 6 Dec 2021 21:45:31 +0000
Subject: [PATCH 11/18] Add SQLITE_STMTSTATUS_FILTER_HIT and _MISS for tracking
 the effectiveness of Bloom filters.

FossilOrigin-Name: 24ba535d200fc8a99dd8e66c6d100b5f6ae442098bafb152008429398eefe3e7
---
 manifest        | 18 +++++++++---------
 manifest.uuid   |  2 +-
 src/shell.c.in  |  7 +++++++
 src/sqlite.h.in |  9 +++++++++
 src/vdbe.c      |  2 ++
 src/vdbeInt.h   |  2 +-
 6 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/manifest b/manifest
index b4f5f5334e..956bcffef9 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Omit\sthe\sOP_FilterInit\sopcode.\s\sUse\sOP_Blob\sto\sinitialize\seach\sBloom\sfilter\ninstead.\s\sSize\sthe\sBloom\sfilter\sbased\son\ssqlite_stat1\ssize\sestimates\srather\nthan\sa\srun-time\smeasurement\sfor\simproved\stestability.
-D 2021-12-06T20:16:53.814
+C Add\sSQLITE_STMTSTATUS_FILTER_HIT\sand\s_MISS\sfor\stracking\sthe\seffectiveness\nof\sBloom\sfilters.
+D 2021-12-06T21:45:31.659
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -552,8 +552,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c 4a1db4aadd802683db40ca2dbbb268187bd195f10cbdb7206dbd8ac988795571
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a7a3d9f54eb24821ec5f67f2e5589b68a5d42d46fc5849d7376886777d93a85a
-F src/shell.c.in 1458b700144c8326fda2514aaddeda49d6f01f1d1ccf7b9b696c53a3535a119c
-F src/sqlite.h.in 5cd209ac7dc4180f0e19292846f40440b8488015849ca0110c70b906b57d68f0
+F src/shell.c.in cda1eaf0292259b4b0721a5e03af9701fd482ebc37ce6a86ddc94cd9a38bb826
+F src/sqlite.h.in bb56040e3c498711c9f77727e477674395a50931ccba8095cfef5c8fb3c3e138
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
 F src/sqliteInt.h f4fbb14ea32d57b813aabf82f586d2ac042234dd89df1c03281f557907745b98
@@ -623,9 +623,9 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
 F src/util.c 6dfbd0bd1954e9531e1c511e5d20390d7dab9ffbf1e20a37c960d1aaf8582b46
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c 9cc221ebae54417b0b47b4ce6aa6eba7e919153f8ae6307f8d18a6749d453f9c
+F src/vdbe.c fd8542b7131f299659871535a41ea732764fb25e4d2931965c97fa36658c50d7
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
-F src/vdbeInt.h fd1103c7ecec8c84164038c8eacaa4a633cb3c10a2f725aae7bd865d4a4fcceb
+F src/vdbeInt.h 910985ac2783fe0938b314d811759d53fd25caf215810f62ca1ff068d6d60d7b
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
 F src/vdbeaux.c 21db442d159fd745a7693d157b5f998260b6af4ca60de559fa3b7b68c7405af2
 F src/vdbeblob.c 29c4118f7ee615cdee829e8401f6ead1b96b95d545b4de0042f6de39c962c652
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P edacf8034dc6bd892038c220c480ea512dbb4005db2a6b1f8e679e8a4929c6ed
-R 933b551d7a60e6c29e7de8b534d33928
+P 8a9036ee617a6ad93bfe827b0789773c49d3d45b085cb76fa4b9b20a41b79b97
+R ec73578237c4366b7d0d282bfae04830
 U drh
-Z ded7cd3093221f3f9f8f684eb1a7389d
+Z e55f2c8840b9a35831f295372358b466
diff --git a/manifest.uuid b/manifest.uuid
index 0b0fb52902..71816720c8 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-8a9036ee617a6ad93bfe827b0789773c49d3d45b085cb76fa4b9b20a41b79b97
\ No newline at end of file
+24ba535d200fc8a99dd8e66c6d100b5f6ae442098bafb152008429398eefe3e7
\ No newline at end of file
diff --git a/src/shell.c.in b/src/shell.c.in
index 543141c9e4..c46eaffd62 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -2790,6 +2790,7 @@ static int display_stats(
   }
 
   if( pArg->pStmt ){
+    int iHit, iMiss;
     iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
                                bReset);
     raw_printf(pArg->out, "Fullscan Steps:                      %d\n", iCur);
@@ -2797,6 +2798,12 @@ static int display_stats(
     raw_printf(pArg->out, "Sort Operations:                     %d\n", iCur);
     iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
     raw_printf(pArg->out, "Autoindex Inserts:                   %d\n", iCur);
+    iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset);
+    iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset);
+    if( iHit || iMiss ){
+      raw_printf(pArg->out, "Bloom filter bypass taken:           %d/%d\n",
+            iHit, iHit+iMiss);
+    }
     iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
     raw_printf(pArg->out, "Virtual Machine Steps:               %d\n", iCur);
     iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 98a028b0b0..4125122e17 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -8467,6 +8467,13 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
 ** The counter is incremented on the first [sqlite3_step()] call of each
 ** cycle.
 **
+** [[SQLITE_STMTSTATUS_FILTER HIT]] <dt>SQLITE_STMTSTATUS_FILTER_HIT</dt>
+** <dd>^This is the number of times that a join step was bypassed because
+** a Bloom filtered returned non-found.  The corresponding
+** SQLITE_STMTSTATUS_FILTER_MISS value is the number of times that the
+** Bloom filter returned a find, and thus the join step had to be processed
+** as normal.
+**
 ** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt>
 ** <dd>^This is the approximate number of bytes of heap memory
 ** used to store the prepared statement.  ^This value is not actually
@@ -8481,6 +8488,8 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
 #define SQLITE_STMTSTATUS_VM_STEP           4
 #define SQLITE_STMTSTATUS_REPREPARE         5
 #define SQLITE_STMTSTATUS_RUN               6
+#define SQLITE_STMTSTATUS_FILTER_MISS       7
+#define SQLITE_STMTSTATUS_FILTER_HIT        8
 #define SQLITE_STMTSTATUS_MEMUSED           99
 
 /*
diff --git a/src/vdbe.c b/src/vdbe.c
index c2281e70a1..0abe64a71f 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -8239,8 +8239,10 @@ case OP_Filter: {          /* jump */
   h %= pIn1->n;
   if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){
     VdbeBranchTaken(1, 2);
+    p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++;
     goto jump_to_p2;
   }else{
+    p->aCounter[SQLITE_STMTSTATUS_FILTER_MISS]++;
     VdbeBranchTaken(0, 2);
   }
   break;
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index c76cdbfdbc..38863f6d65 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -430,7 +430,7 @@ struct Vdbe {
   bft bIsReader:1;        /* True for statements that read */
   yDbMask btreeMask;      /* Bitmask of db->aDb[] entries referenced */
   yDbMask lockMask;       /* Subset of btreeMask that requires a lock */
-  u32 aCounter[7];        /* Counters used by sqlite3_stmt_status() */
+  u32 aCounter[9];        /* Counters used by sqlite3_stmt_status() */
   char *zSql;             /* Text of the SQL statement that generated this */
 #ifdef SQLITE_ENABLE_NORMALIZE
   char *zNormSql;         /* Normalization of the associated SQL statement */

From 3bd7cd736d142ec3068f56cac510afdbc85c925d Mon Sep 17 00:00:00 2001
From: drh <>
Date: Mon, 6 Dec 2021 23:07:59 +0000
Subject: [PATCH 12/18] Improved EXPLAIN QUERY PLAN output for Bloom filters.

FossilOrigin-Name: 00070e1fff6aec3d7c7b121f2b02bbca38a1664aca9afc3fb7e293f07fd1704f
---
 manifest        | 12 ++++++------
 manifest.uuid   |  2 +-
 src/wherecode.c | 41 +++++++++++++++++++++++++++++------------
 3 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/manifest b/manifest
index 956bcffef9..6a4298ce44 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sSQLITE_STMTSTATUS_FILTER_HIT\sand\s_MISS\sfor\stracking\sthe\seffectiveness\nof\sBloom\sfilters.
-D 2021-12-06T21:45:31.659
+C Improved\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\sBloom\sfilters.
+D 2021-12-06T23:07:59.482
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -640,7 +640,7 @@ F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
 F src/where.c 21bd1078837afb127827243d7ad549a4b47022ffaa43c5baa74dcac7f89809a7
 F src/whereInt.h 5c6601d6d0b7b8936482506d2d835981cc6efcd8e106a829893a27a14cfb10b8
-F src/wherecode.c fa667db48db1077b42731bfd97e9181b39409ffdc7051162ecae6895ca71ad2c
+F src/wherecode.c f82a322a8849a0290587d968f5c1c71b0d5018e078f43ea732a4cdbf837ed42b
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8a9036ee617a6ad93bfe827b0789773c49d3d45b085cb76fa4b9b20a41b79b97
-R ec73578237c4366b7d0d282bfae04830
+P 24ba535d200fc8a99dd8e66c6d100b5f6ae442098bafb152008429398eefe3e7
+R 7eae2a1d2a9d2b173d70c0467bdac4ba
 U drh
-Z e55f2c8840b9a35831f295372358b466
+Z 477e7fdac36f7f761dc02a6457a5f233
diff --git a/manifest.uuid b/manifest.uuid
index 71816720c8..a54777d56d 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-24ba535d200fc8a99dd8e66c6d100b5f6ae442098bafb152008429398eefe3e7
\ No newline at end of file
+00070e1fff6aec3d7c7b121f2b02bbca38a1664aca9afc3fb7e293f07fd1704f
\ No newline at end of file
diff --git a/src/wherecode.c b/src/wherecode.c
index dfe697f244..c04a73c2a8 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -176,19 +176,27 @@ int sqlite3WhereExplainOneScan(
         explainIndexRange(&str, pLoop);
       }
     }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
-      const char *zRangeOp;
+      char cRangeOp;
+#if 0  /* Better output, but breaks many tests */
+      const Table *pTab = pItem->pTab;
+      const char *zRowid = pTab->iPKey>=0 ? pTab->aCol[pTab->iPKey].zCnName:
+                              "rowid";
+#else
+      const char *zRowid = "rowid";
+#endif
+      sqlite3_str_appendf(&str, " USING INTEGER PRIMARY KEY (%s", zRowid);
       if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
-        zRangeOp = "=";
+        cRangeOp = '=';
       }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
-        zRangeOp = ">? AND rowid<";
+        sqlite3_str_appendf(&str, ">? AND %s", zRowid);
+        cRangeOp = '<';
       }else if( flags&WHERE_BTM_LIMIT ){
-        zRangeOp = ">";
+        cRangeOp = '>';
       }else{
         assert( flags&WHERE_TOP_LIMIT);
-        zRangeOp = "<";
+        cRangeOp = '<';
       }
-      sqlite3_str_appendf(&str, 
-          " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
+      sqlite3_str_appendf(&str, "%c?)", cRangeOp);
     }
 #ifndef SQLITE_OMIT_VIRTUALTABLE
     else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
@@ -243,12 +251,21 @@ int sqlite3WhereExplainBloomFilter(
 
     sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
     str.printfFlags = SQLITE_PRINTF_INTERNAL;
-    sqlite3_str_appendf(&str, "BLOOM FILTER ON %S(", pItem);
+    sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem);
     pLoop = pLevel->pWLoop;
-    for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){
-      const char *z = pItem->pTab->aCol[i].zCnName;
-      if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5);
-      sqlite3_str_appendf(&str, "%s=?", z);
+    if( pLoop->wsFlags & WHERE_IPK ){
+      const Table *pTab = pItem->pTab;
+      if( pTab->iPKey>=0 ){
+        sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName);
+      }else{
+        sqlite3_str_appendf(&str, "rowid=?");
+      }
+    }else{
+      for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){
+        const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i);
+        if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5);
+        sqlite3_str_appendf(&str, "%s=?", z);
+      }
     }
     sqlite3_str_append(&str, ")", 1);
     zMsg = sqlite3StrAccumFinish(&str);

From 761d64b73bc7ceb5ef60d956b6e1b78c1997082d Mon Sep 17 00:00:00 2001
From: drh <>
Date: Tue, 7 Dec 2021 22:37:50 +0000
Subject: [PATCH 13/18] Do not generate a Bloom filter if it cannot be used
 prior to the next seek, as that leads to a misleading EXPLAIN QUERY PLAN.

FossilOrigin-Name: 2739ed5192058fbcc816ecbc252be687efc606e038bfcd6cf71194a3f4f5684e
---
 manifest      | 12 ++++++------
 manifest.uuid |  2 +-
 src/where.c   |  9 ++++++---
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/manifest b/manifest
index 6a4298ce44..78850cff06 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improved\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\sBloom\sfilters.
-D 2021-12-06T23:07:59.482
+C Do\snot\sgenerate\sa\sBloom\sfilter\sif\sit\scannot\sbe\sused\sprior\sto\sthe\snext\sseek,\nas\sthat\sleads\sto\sa\smisleading\sEXPLAIN\sQUERY\sPLAN.
+D 2021-12-07T22:37:50.686
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -638,7 +638,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 21bd1078837afb127827243d7ad549a4b47022ffaa43c5baa74dcac7f89809a7
+F src/where.c 328e5c6f5a91f8c1e36417159cc93ea180700ae97726f934f69e68c8c60030b5
 F src/whereInt.h 5c6601d6d0b7b8936482506d2d835981cc6efcd8e106a829893a27a14cfb10b8
 F src/wherecode.c f82a322a8849a0290587d968f5c1c71b0d5018e078f43ea732a4cdbf837ed42b
 F src/whereexpr.c 19394cb463003e9cc9305730b1508b8817a22bb7247170d81234b691a7f05b89
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 24ba535d200fc8a99dd8e66c6d100b5f6ae442098bafb152008429398eefe3e7
-R 7eae2a1d2a9d2b173d70c0467bdac4ba
+P 00070e1fff6aec3d7c7b121f2b02bbca38a1664aca9afc3fb7e293f07fd1704f
+R 6ef1a6c68af918fb5dc855e869b3769c
 U drh
-Z 477e7fdac36f7f761dc02a6457a5f233
+Z 7a26146d090468562610338a13462117
diff --git a/manifest.uuid b/manifest.uuid
index a54777d56d..f1d3a5f29a 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-00070e1fff6aec3d7c7b121f2b02bbca38a1664aca9afc3fb7e293f07fd1704f
\ No newline at end of file
+2739ed5192058fbcc816ecbc252be687efc606e038bfcd6cf71194a3f4f5684e
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index e7246b1040..1eb9f36017 100644
--- a/src/where.c
+++ b/src/where.c
@@ -988,7 +988,8 @@ end_auto_index_create:
 static SQLITE_NOINLINE void constructBloomFilter(
   WhereInfo *pWInfo,    /* The WHERE clause */
   int iLevel,           /* Index in pWInfo->a[] that is pLevel */
-  WhereLevel *pLevel    /* Make a Bloom filter for this FROM term */
+  WhereLevel *pLevel,   /* Make a Bloom filter for this FROM term */
+  Bitmask notReady      /* Loops that are not ready */
 ){
   int addrOnce;                        /* Address of opening OP_Once */
   int addrTop;                         /* Address of OP_Rewind */
@@ -1076,7 +1077,9 @@ static SQLITE_NOINLINE void constructBloomFilter(
       iLevel++;
       pLevel = &pWInfo->a[iLevel];
       pLoop = pLevel->pWLoop;
-      if( pLoop && pLoop->wsFlags & WHERE_BLOOMFILTER ) break;
+      if( pLoop==0 ) continue;
+      if( pLoop->prereq & notReady ) continue;
+      if( pLoop->wsFlags & WHERE_BLOOMFILTER ) break;
     }
   }while( iLevel < pWInfo->nLevel );
   sqlite3VdbeJumpHere(v, addrOnce);
@@ -5594,7 +5597,7 @@ WhereInfo *sqlite3WhereBegin(
                   &pTabList->a[pLevel->iFrom], notReady, pLevel);
 #endif
       }else{
-        constructBloomFilter(pWInfo, ii, pLevel);
+        constructBloomFilter(pWInfo, ii, pLevel, notReady);
       }
       if( db->mallocFailed ) goto whereBeginError;
     }

From fb82caf0bed2cd006cda440888939b87641b00c9 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Wed, 8 Dec 2021 19:50:45 +0000
Subject: [PATCH 14/18] Improvements on the decision of whether or not to use a
 Bloom filter.

FossilOrigin-Name: 0fb2a4e08f518cb38ea3edc6a084d1e4874fd622ba3cf9101b49b3e7dc1a3f2b
---
 manifest       | 14 +++++++-------
 manifest.uuid  |  2 +-
 src/where.c    | 35 ++++++++++++++++++++++++-----------
 src/whereInt.h |  1 +
 4 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/manifest b/manifest
index ed505a6910..4c4a9d684d 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sthe\sWhereClause\sfix\sfrom\strunk
-D 2021-12-08T16:15:41.698
+C Improvements\son\sthe\sdecision\sof\swhether\sor\snot\sto\suse\sa\sBloom\sfilter.
+D 2021-12-08T19:50:45.145
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -638,8 +638,8 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 74382d720f2a9a06f738467929ecf0d79fcdd6b9779184bddbb97352895a1b23
-F src/whereInt.h d55d5ce5c9de361e16cf5cd23da054cdecc1ae7735682ee55c2f40e58e960d22
+F src/where.c 89958d4fc7c45e916882ebc97481d98597f516ce3d778ace3271aacf34e24e91
+F src/whereInt.h c2cb535e755b25a7e152bdb407cbb2f62bdb8747c44bf2d984139f5cbebb8704
 F src/wherecode.c e2207f011b7e5bdef5722da5e8d95eb30ad01051b3526757901ecb19a9e98ff3
 F src/whereexpr.c 791544603b254cf11f8e84e3b50b0863c57322e9f213b828680f658e232ebc57
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 2739ed5192058fbcc816ecbc252be687efc606e038bfcd6cf71194a3f4f5684e 6024682ca467fa4fe49608772b0bbfa2f8a419b32cebfa715941073c8b29da49
-R d3afb24f2c63fc5d59ade7856914b3ad
+P d3250256772e3348abe887c0ca3550a6647cce3804c9456a9d0112aea7ee1c46
+R a7fca9e9fc2528f3e921fdfbeaa2e361
 U drh
-Z 246d691185204c115a5a684ad14acaf7
+Z 815ca39cf2b788796cb5ebc9be23c44d
diff --git a/manifest.uuid b/manifest.uuid
index 1416a80b0a..a9d9e689b3 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-d3250256772e3348abe887c0ca3550a6647cce3804c9456a9d0112aea7ee1c46
\ No newline at end of file
+0fb2a4e08f518cb38ea3edc6a084d1e4874fd622ba3cf9101b49b3e7dc1a3f2b
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index bf10262a76..065d14fd64 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2028,9 +2028,9 @@ void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
     sqlite3_free(z);
   }
   if( p->wsFlags & WHERE_SKIPSCAN ){
-    sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip);
+    sqlite3DebugPrintf(" f %06x %d-%d", p->wsFlags, p->nLTerm,p->nSkip);
   }else{
-    sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm);
+    sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm);
   }
   sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
   if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){
@@ -2502,6 +2502,9 @@ static void whereLoopOutputAdjust(
       if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
     }
     if( j<0 ){
+      if( pLoop->maskSelf==pTerm->prereqAll ){
+        pLoop->wsFlags |= WHERE_CULLED;
+      }
       if( pTerm->truthProb<=0 ){
         /* If a truth probability is specified using the likelihood() hints,
         ** then use the probability provided by the application. */
@@ -2529,7 +2532,9 @@ static void whereLoopOutputAdjust(
       }
     }
   }
-  if( pLoop->nOut > nRow-iReduce )  pLoop->nOut = nRow - iReduce;
+  if( pLoop->nOut > nRow-iReduce ){
+    pLoop->nOut = nRow - iReduce;
+  }
 }
 
 /* 
@@ -4984,22 +4989,30 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
 ){
   int i;
   LogEst nSearch;
-  SrcItem *pItem;
 
   assert( pWInfo->nLevel>=2 );
   assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) );
   nSearch = pWInfo->a[0].pWLoop->nOut;
   for(i=1; i<pWInfo->nLevel; i++){
     WhereLoop *pLoop = pWInfo->a[i].pWLoop;
-    if( pLoop->nOut<0
+    const int reqFlags = (WHERE_CULLED|WHERE_COLUMN_EQ);
+    if( (pLoop->wsFlags & reqFlags)==reqFlags
      && (pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0
-     && (pLoop->wsFlags & WHERE_COLUMN_EQ)!=0
-     && nSearch > (pItem = &pWInfo->pTabList->a[pLoop->iTab])->pTab->nRowLogEst
-     && (pItem->fg.jointype & JT_LEFT)==0
     ){
-      pLoop->wsFlags |= WHERE_BLOOMFILTER;
-      pLoop->wsFlags &= ~WHERE_IDX_ONLY;
-      WHERETRACE(0xffff, ("-> use Bloom-filter on loop %c\n", pLoop->cId));
+      SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
+      Table *pTab = pItem->pTab;
+      pTab->tabFlags |= TF_StatsUsed;
+      if( nSearch > pTab->nRowLogEst
+       && (pItem->fg.jointype & JT_LEFT)==0
+      ){
+        pLoop->wsFlags |= WHERE_BLOOMFILTER;
+        pLoop->wsFlags &= ~WHERE_IDX_ONLY;
+        WHERETRACE(0xffff, (
+           "-> use Bloom-filter on loop %c because there are ~%.1e "
+           "lookups into %s which has only ~%.1e rows\n",
+           pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName,
+           (double)sqlite3LogEstToInt(pTab->nRowLogEst)));
+      }
     }
     nSearch += pLoop->nOut;
   }
diff --git a/src/whereInt.h b/src/whereInt.h
index 558a1e3eb9..d790653a71 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -608,5 +608,6 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
 #define WHERE_IN_SEEKSCAN  0x00100000  /* Seek-scan optimization for IN */
 #define WHERE_TRANSCONS    0x00200000  /* Uses a transitive constraint */
 #define WHERE_BLOOMFILTER  0x00400000  /* Consider using a Bloom-filter */
+#define WHERE_CULLED       0x00800000  /* nOut reduced by extra WHERE terms */
 
 #endif /* !defined(SQLITE_WHEREINT_H) */

From 7e910f6422553150a62332bdc2f3c21b16184abb Mon Sep 17 00:00:00 2001
From: drh <>
Date: Thu, 9 Dec 2021 01:28:15 +0000
Subject: [PATCH 15/18] Add SQLITE_TESTCTRL_LOGEST and enhance the LogEst
 utility program. Improvements to testability of bloom filters.

FossilOrigin-Name: 88b43d798cc5aa59855e92d3e658aee9f0a5def6ffbc5db77af048d75ecdf8cc
---
 manifest        | 28 ++++++++++++++--------------
 manifest.uuid   |  2 +-
 src/main.c      | 20 ++++++++++++++++++++
 src/sqlite.h.in |  3 ++-
 src/sqliteInt.h |  2 --
 src/util.c      |  2 --
 src/vdbe.c      |  6 ++----
 src/where.c     | 34 +++++++++++++++++++---------------
 src/whereInt.h  |  2 +-
 src/wherecode.c |  7 +++----
 tool/logest.c   |  5 ++++-
 11 files changed, 66 insertions(+), 45 deletions(-)

diff --git a/manifest b/manifest
index 4c4a9d684d..7cbde1e303 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improvements\son\sthe\sdecision\sof\swhether\sor\snot\sto\suse\sa\sBloom\sfilter.
-D 2021-12-08T19:50:45.145
+C Add\sSQLITE_TESTCTRL_LOGEST\sand\senhance\sthe\sLogEst\sutility\sprogram.\nImprovements\sto\stestability\sof\sbloom\sfilters.
+D 2021-12-09T01:28:15.168
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -515,7 +515,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
 F src/insert.c e0293a6f686e18cb2c9dd0619a731518e0109d7e1f1db1932974659e7843cfd1
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c e1dcff1c916bf6834e150b492eddda5d9792453182d2ad64294d2266b6e93c4c
-F src/main.c 1ea70751e6005ab6a9f784730fa0919efaa6639440a287deb73cb711e5aae57a
+F src/main.c 674a0fdfc2808e1d5a78b2eefe2ec3f93428cf82f0f6c013d577df1a1caa5940
 F src/malloc.c d9172a3946f11384f2fd6a799554ee26c6bb407c4bd0874a456ed485a2e362e4
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -553,10 +553,10 @@ F src/resolve.c 4a1db4aadd802683db40ca2dbbb268187bd195f10cbdb7206dbd8ac988795571
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a7a3d9f54eb24821ec5f67f2e5589b68a5d42d46fc5849d7376886777d93a85a
 F src/shell.c.in cda1eaf0292259b4b0721a5e03af9701fd482ebc37ce6a86ddc94cd9a38bb826
-F src/sqlite.h.in bb56040e3c498711c9f77727e477674395a50931ccba8095cfef5c8fb3c3e138
+F src/sqlite.h.in 50c8f27251b11f1c89b06abc6e4085fce15151bcbd355a44609ecb2ba5424841
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h f4fbb14ea32d57b813aabf82f586d2ac042234dd89df1c03281f557907745b98
+F src/sqliteInt.h 31b9673bc26b5b2a846bf26ce7124c869f64368f4eaac865d9350749ea314000
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -621,9 +621,9 @@ F src/trigger.c 2ef56f0b7b75349a5557d0604b475126329c2e1a02432e7d49c4c710613e8254
 F src/update.c d6f5c7b9e072660757ac7d58175aca11c07cb95ebbb297ae7f38853700f52328
 F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
-F src/util.c 6dfbd0bd1954e9531e1c511e5d20390d7dab9ffbf1e20a37c960d1aaf8582b46
+F src/util.c 569349b0bddcbfbc661856f446adb92e1b0a47b3cbef548da9fc5aa639d7964c
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c fd8542b7131f299659871535a41ea732764fb25e4d2931965c97fa36658c50d7
+F src/vdbe.c 855ee903521fcc5a799f673f5b05fc599dc50a31e6cb6a15e1e8a6858087595b
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
 F src/vdbeInt.h 910985ac2783fe0938b314d811759d53fd25caf215810f62ca1ff068d6d60d7b
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
@@ -638,9 +638,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 89958d4fc7c45e916882ebc97481d98597f516ce3d778ace3271aacf34e24e91
-F src/whereInt.h c2cb535e755b25a7e152bdb407cbb2f62bdb8747c44bf2d984139f5cbebb8704
-F src/wherecode.c e2207f011b7e5bdef5722da5e8d95eb30ad01051b3526757901ecb19a9e98ff3
+F src/where.c 4946af4e7d073fd35c97ffce4c4b4f3d84f6a0c9e5cb20342560cfaf072ef3a6
+F src/whereInt.h e83f7ba73db5b1b2685118fad67d178fbe04751a25419f0f6ff73e58b4807325
+F src/wherecode.c 560424f5845ec4ef1e36e8a6a066cc497aaf8163fef29fa18a0317e1e14ba9b7
 F src/whereexpr.c 791544603b254cf11f8e84e3b50b0863c57322e9f213b828680f658e232ebc57
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1856,7 +1856,7 @@ F tool/lemon.c 258881835bd5bccd0c74fb110fe54244ff18e8e7ef3d949cbdab7187f02132bb
 F tool/lempar.c 57478ea48420da05faa873c6d1616321caa5464644588c97fbe8e0ea04450748
 F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
 F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
-F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
+F tool/logest.c 83dbfda91615f1db5dce38215303d8bb456f437342d2c64262406dbdd1c931e2
 F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
 F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
 F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P d3250256772e3348abe887c0ca3550a6647cce3804c9456a9d0112aea7ee1c46
-R a7fca9e9fc2528f3e921fdfbeaa2e361
+P 0fb2a4e08f518cb38ea3edc6a084d1e4874fd622ba3cf9101b49b3e7dc1a3f2b
+R 13197c53db0503b249bb051590a0a6df
 U drh
-Z 815ca39cf2b788796cb5ebc9be23c44d
+Z 80d7c2199cca2c3c9ca5b22286927a11
diff --git a/manifest.uuid b/manifest.uuid
index a9d9e689b3..2d8c59a7eb 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-0fb2a4e08f518cb38ea3edc6a084d1e4874fd622ba3cf9101b49b3e7dc1a3f2b
\ No newline at end of file
+88b43d798cc5aa59855e92d3e658aee9f0a5def6ffbc5db77af048d75ecdf8cc
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
index 804719f176..1b7853f609 100644
--- a/src/main.c
+++ b/src/main.c
@@ -4347,6 +4347,26 @@ int sqlite3_test_control(int op, ...){
        break;
     }
 
+    /* sqlite3_test_control(SQLITE_TESTCTRL_LOGEST,
+    **      double fIn,     // Input value
+    **      int *pLogEst,   // sqlite3LogEstFromDouble(fIn)
+    **      u64 *pInt,      // sqlite3LogEstToInt(*pLogEst)
+    **      int *pLogEst2   // sqlite3LogEst(*pInt)
+    ** );
+    **
+    ** Test access for the LogEst conversion routines.
+    */
+    case SQLITE_TESTCTRL_LOGEST: {
+      double rIn = va_arg(ap, double);
+      LogEst rLogEst = sqlite3LogEstFromDouble(rIn);
+      u64 iInt = sqlite3LogEstToInt(rLogEst);
+      va_arg(ap, int*)[0] = rLogEst;
+      va_arg(ap, u64*)[0] = iInt;
+      va_arg(ap, int*)[0] = sqlite3LogEst(iInt);
+      break;
+    }
+ 
+
 #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
     /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue)
     **
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 4125122e17..f7cf4d2fe1 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -7944,7 +7944,8 @@ int sqlite3_test_control(int op, ...);
 #define SQLITE_TESTCTRL_SEEK_COUNT              30
 #define SQLITE_TESTCTRL_TRACEFLAGS              31
 #define SQLITE_TESTCTRL_TUNE                    32
-#define SQLITE_TESTCTRL_LAST                    32  /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_LOGEST                  33
+#define SQLITE_TESTCTRL_LAST                    33  /* Largest TESTCTRL */
 
 /*
 ** CAPI3REF: SQL Keyword Checking
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 90e60d30b0..0104bace6d 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -4780,9 +4780,7 @@ int sqlite3Utf8CharLen(const char *pData, int nByte);
 u32 sqlite3Utf8Read(const u8**);
 LogEst sqlite3LogEst(u64);
 LogEst sqlite3LogEstAdd(LogEst,LogEst);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
 LogEst sqlite3LogEstFromDouble(double);
-#endif
 u64 sqlite3LogEstToInt(LogEst);
 VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int);
 const char *sqlite3VListNumToName(VList*,int);
diff --git a/src/util.c b/src/util.c
index d93c298116..8ea951fa16 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1586,7 +1586,6 @@ LogEst sqlite3LogEst(u64 x){
   return a[x&7] + y - 10;
 }
 
-#ifndef SQLITE_OMIT_VIRTUALTABLE
 /*
 ** Convert a double into a LogEst
 ** In other words, compute an approximation for 10*log2(x).
@@ -1601,7 +1600,6 @@ LogEst sqlite3LogEstFromDouble(double x){
   e = (a>>52) - 1022;
   return e*10;
 }
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
 
 /*
 ** Convert a LogEst into an integer.
diff --git a/src/vdbe.c b/src/vdbe.c
index 0abe64a71f..ef60ed0123 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -8222,10 +8222,8 @@ case OP_Filter: {          /* jump */
 
   assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
   pIn1 = &aMem[pOp->p1];
-  if( (pIn1->flags & MEM_Blob)==0 || NEVER(pIn1->n<=0) ){
-    VdbeBranchTaken(0, 2);
-    break;
-  }
+  assert( (pIn1->flags & MEM_Blob)!=0 );
+  assert( pIn1->n >= 1 );
   h = filterHash(aMem, pOp);
 #ifdef SQLITE_DEBUG
   if( db->flags&SQLITE_VdbeTrace ){
diff --git a/src/where.c b/src/where.c
index 065d14fd64..859dac7cbb 100644
--- a/src/where.c
+++ b/src/where.c
@@ -1009,7 +1009,7 @@ static SQLITE_NOINLINE void constructBloomFilter(
   do{
     const SrcItem *pItem;
     const Table *pTab;
-    int sz;
+    u64 sz;
     sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
     addrCont = sqlite3VdbeMakeLabel(pParse);
     iCur = pLevel->iTabCur;
@@ -1027,17 +1027,13 @@ static SQLITE_NOINLINE void constructBloomFilter(
     assert( pItem!=0 );
     pTab = pItem->pTab;
     assert( pTab!=0 );
-    if( pTab->tabFlags & TF_HasStat1 ){
-      sz = sqlite3LogEstToInt(pItem->pTab->nRowLogEst);
-      if( sz<10000 ){
-        sz = 10000;
-      }else if( sz>10000000 ){
-        sz = 10000000;
-      }
-    }else{
+    sz = sqlite3LogEstToInt(pTab->nRowLogEst);
+    if( sz<10000 ){
       sz = 10000;
+    }else if( sz>10000000 ){
+      sz = 10000000;
     }
-    sqlite3VdbeAddOp2(v, OP_Blob, sz, pLevel->regFilter);
+    sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter);
 
     addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
     pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
@@ -2503,7 +2499,11 @@ static void whereLoopOutputAdjust(
     }
     if( j<0 ){
       if( pLoop->maskSelf==pTerm->prereqAll ){
-        pLoop->wsFlags |= WHERE_CULLED;
+        /* If there are extra terms in the WHERE clause not used by an index
+        ** that depend only on the table being scanned, and that will tend to
+        ** cause many rows to be omitted, then mark that table as
+        ** "self-culling". */
+        pLoop->wsFlags |= WHERE_SELFCULL;
       }
       if( pTerm->truthProb<=0 ){
         /* If a truth probability is specified using the likelihood() hints,
@@ -4974,10 +4974,13 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
 **
 **   (1)  The SEARCH happens more than N times where N is the number
 **        of rows in the table that is being considered for the Bloom
-**        filter.  (TO DO:  Make this condition more precise.)
-**   (2)  Most searches are expected to find zero rows
+**        filter.
+**   (2)  Some searches are expected to find zero rows.  (This is determined
+**        by the WHERE_SELFCULL flag on the term.)
 **   (3)  The table being searched is not the right table of a LEFT JOIN
-**   (4)  Bloom-filter processing is not disabled
+**   (4)  Bloom-filter processing is not disabled.  (Checked by the
+**        caller.)
+**   (5)  The size of the table being searched is known by ANALYZE.
 **
 ** This block of code merely checks to see if a Bloom filter would be
 ** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the
@@ -4995,7 +4998,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
   nSearch = pWInfo->a[0].pWLoop->nOut;
   for(i=1; i<pWInfo->nLevel; i++){
     WhereLoop *pLoop = pWInfo->a[i].pWLoop;
-    const int reqFlags = (WHERE_CULLED|WHERE_COLUMN_EQ);
+    const int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
     if( (pLoop->wsFlags & reqFlags)==reqFlags
      && (pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0
     ){
@@ -5004,6 +5007,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
       pTab->tabFlags |= TF_StatsUsed;
       if( nSearch > pTab->nRowLogEst
        && (pItem->fg.jointype & JT_LEFT)==0
+       && (pTab->tabFlags & TF_HasStat1)!=0
       ){
         pLoop->wsFlags |= WHERE_BLOOMFILTER;
         pLoop->wsFlags &= ~WHERE_IDX_ONLY;
diff --git a/src/whereInt.h b/src/whereInt.h
index d790653a71..8051b78a02 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -608,6 +608,6 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
 #define WHERE_IN_SEEKSCAN  0x00100000  /* Seek-scan optimization for IN */
 #define WHERE_TRANSCONS    0x00200000  /* Uses a transitive constraint */
 #define WHERE_BLOOMFILTER  0x00400000  /* Consider using a Bloom-filter */
-#define WHERE_CULLED       0x00800000  /* nOut reduced by extra WHERE terms */
+#define WHERE_SELFCULL     0x00800000  /* nOut reduced by extra WHERE terms */
 
 #endif /* !defined(SQLITE_WHEREINT_H) */
diff --git a/src/wherecode.c b/src/wherecode.c
index 42cfcc9eac..05d1f12ced 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1393,13 +1393,12 @@ static SQLITE_NOINLINE void filterPullDown(
     if( pLoop->prereq & notReady ) continue;
     if( pLoop->wsFlags & WHERE_IPK ){
       WhereTerm *pTerm = pLoop->aLTerm[0];
-      int r1, regRowid;
+      int regRowid;
       assert( pTerm!=0 );
       assert( pTerm->pExpr!=0 );
       testcase( pTerm->wtFlags & TERM_VIRTUAL );
-      r1 = sqlite3GetTempReg(pParse);
-      regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, r1);
-      if( regRowid!=r1 ) sqlite3ReleaseTempReg(pParse, r1);
+      regRowid = sqlite3GetTempReg(pParse);
+      regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid);
       sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter,
                            addrNxt, regRowid, 1);
       VdbeCoverage(pParse->pVdbe);
diff --git a/tool/logest.c b/tool/logest.c
index e936e02cbe..580a3a6f96 100644
--- a/tool/logest.c
+++ b/tool/logest.c
@@ -75,6 +75,7 @@ static sqlite3_uint64 logEstToInt(LogEst x){
   x /= 10;
   if( n>=5 ) n -= 2;
   else if( n>=1 ) n -= 1;
+  if( x>60 ) return ((sqlite3_uint64)0xffffffff)<<32 + 0xffffffff;
   if( x>=3 ) return (n+8)<<(x-3);
   return (n+8)>>(3-x);
 }
@@ -149,7 +150,7 @@ int main(int argc, char **argv){
     }else if( z[0]=='^' ){
       a[n++] = (LogEst)atoi(z+1);
     }else if( isInteger(z) ){
-      a[n++] = logEstFromInteger(atoi(z));
+      a[n++] = logEstFromInteger(atoll(z));
     }else if( isFloat(z) && z[0]!='-' ){
       a[n++] = logEstFromDouble(atof(z));
     }else{
@@ -161,6 +162,8 @@ int main(int argc, char **argv){
       printf("%5d (%f)\n", a[i], 1.0/(double)logEstToInt(-a[i]));
     }else if( a[i]<10 ){
       printf("%5d (%f)\n", a[i], logEstToInt(a[i]+100)/1024.0);
+    }else if( a[i]>100 ){
+      printf("%5d (%lld)\n", a[i], logEstToInt(a[i]));
     }else{
       sqlite3_uint64 x = logEstToInt(a[i]+100)*100/1024;
       printf("%5d (%lld.%02lld)\n", a[i], x/100, x%100);

From 5d88be8f488e2915563cd823bbf632d167b7ca51 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Thu, 9 Dec 2021 16:17:43 +0000
Subject: [PATCH 16/18] Add ".mode off" and ".mode count" to the CLI.

FossilOrigin-Name: b11f4d080aa9e6f694e2ec401e871f42bf25997e8e8bf77fa9b6014a50466e3c
---
 manifest       | 12 ++++++------
 manifest.uuid  |  2 +-
 src/shell.c.in | 18 +++++++++++++++++-
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/manifest b/manifest
index 7cbde1e303..9862becc43 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sSQLITE_TESTCTRL_LOGEST\sand\senhance\sthe\sLogEst\sutility\sprogram.\nImprovements\sto\stestability\sof\sbloom\sfilters.
-D 2021-12-09T01:28:15.168
+C Add\s".mode\soff"\sand\s".mode\scount"\sto\sthe\sCLI.
+D 2021-12-09T16:17:43.145
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -552,7 +552,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c 4a1db4aadd802683db40ca2dbbb268187bd195f10cbdb7206dbd8ac988795571
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a7a3d9f54eb24821ec5f67f2e5589b68a5d42d46fc5849d7376886777d93a85a
-F src/shell.c.in cda1eaf0292259b4b0721a5e03af9701fd482ebc37ce6a86ddc94cd9a38bb826
+F src/shell.c.in 239bee1085d94964f02582b0714dc3fc85cfc16e27e95813e4dbc24bb215a7e0
 F src/sqlite.h.in 50c8f27251b11f1c89b06abc6e4085fce15151bcbd355a44609ecb2ba5424841
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 0fb2a4e08f518cb38ea3edc6a084d1e4874fd622ba3cf9101b49b3e7dc1a3f2b
-R 13197c53db0503b249bb051590a0a6df
+P 88b43d798cc5aa59855e92d3e658aee9f0a5def6ffbc5db77af048d75ecdf8cc
+R 9caa791b08a3199a0c8bfc0ce172ce1a
 U drh
-Z 80d7c2199cca2c3c9ca5b22286927a11
+Z 89026152bfb77d836155ab364d1ebd27
diff --git a/manifest.uuid b/manifest.uuid
index 2d8c59a7eb..274a6197ab 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-88b43d798cc5aa59855e92d3e658aee9f0a5def6ffbc5db77af048d75ecdf8cc
\ No newline at end of file
+b11f4d080aa9e6f694e2ec401e871f42bf25997e8e8bf77fa9b6014a50466e3c
\ No newline at end of file
diff --git a/src/shell.c.in b/src/shell.c.in
index c46eaffd62..3262f98c12 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -1253,6 +1253,8 @@ struct ShellState {
 #define MODE_Markdown 14 /* Markdown formatting */
 #define MODE_Table   15  /* MySQL-style table formatting */
 #define MODE_Box     16  /* Unicode box-drawing characters */
+#define MODE_Count   17  /* Output only a count of the rows of output */
+#define MODE_Off     18  /* No query output shown */
 
 static const char *modeDescr[] = {
   "line",
@@ -1271,7 +1273,9 @@ static const char *modeDescr[] = {
   "json",
   "markdown",
   "table",
-  "box"
+  "box",
+  "count",
+  "off"
 };
 
 /*
@@ -2093,6 +2097,10 @@ static int shell_callback(
 
   if( azArg==0 ) return 0;
   switch( p->cMode ){
+    case MODE_Count:
+    case MODE_Off: {
+      break;
+    }
     case MODE_Line: {
       int w = 5;
       if( azArg==0 ) break;
@@ -3310,6 +3318,7 @@ static void exec_prepared_stmt(
   sqlite3_stmt *pStmt                              /* Statment to run */
 ){
   int rc;
+  sqlite3_uint64 nRow = 0;
 
   if( pArg->cMode==MODE_Column
    || pArg->cMode==MODE_Table
@@ -3342,6 +3351,7 @@ static void exec_prepared_stmt(
         azCols[i] = (char *)sqlite3_column_name(pStmt, i);
       }
       do{
+        nRow++;
         /* extract the data and data types */
         for(i=0; i<nCol; i++){
           aiTypes[i] = x = sqlite3_column_type(pStmt, i);
@@ -3369,6 +3379,8 @@ static void exec_prepared_stmt(
       sqlite3_free(pData);
       if( pArg->cMode==MODE_Json ){
         fputs("]\n", pArg->out);
+      }else if( pArg->cMode==MODE_Count ){
+        printf("%llu row%s\n", nRow, nRow!=1 ? "s" : "");
       }
     }
   }
@@ -8879,6 +8891,10 @@ static int do_meta_command(char *zLine, ShellState *p){
       p->mode = MODE_Table;
     }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){
       p->mode = MODE_Box;
+    }else if( c2=='c' && strncmp(azArg[1],"count",n2)==0 ){
+      p->mode = MODE_Count;
+    }else if( c2=='o' && strncmp(azArg[1],"off",n2)==0 ){
+      p->mode = MODE_Off;
     }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
       p->mode = MODE_Json;
     }else if( nArg==1 ){

From a11c5e22b5eb32cdf2061a6c19fd7bf4d2a36e05 Mon Sep 17 00:00:00 2001
From: drh <>
Date: Thu, 9 Dec 2021 18:44:03 +0000
Subject: [PATCH 17/18] Enable bloom filters for the right table of a LEFT
 JOIN.  Fix unreachable branches.

FossilOrigin-Name: d342ab722de1fc3f34219c9755253db9d88eb50a6fa5cc257207008b00e7fc03
---
 manifest        | 16 ++++++-------
 manifest.uuid   |  2 +-
 src/where.c     |  4 ++--
 src/wherecode.c | 63 +++++++++++++++++++++++--------------------------
 test/join5.test |  1 +
 5 files changed, 42 insertions(+), 44 deletions(-)

diff --git a/manifest b/manifest
index 9862becc43..a498dda5fc 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\s".mode\soff"\sand\s".mode\scount"\sto\sthe\sCLI.
-D 2021-12-09T16:17:43.145
+C Enable\sbloom\sfilters\sfor\sthe\sright\stable\sof\sa\sLEFT\sJOIN.\s\sFix\sunreachable\nbranches.
+D 2021-12-09T18:44:03.070
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -638,9 +638,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c 4946af4e7d073fd35c97ffce4c4b4f3d84f6a0c9e5cb20342560cfaf072ef3a6
+F src/where.c abd620b4823c0c72322ad5307805d0a350e808bd2424e3e00a1c3ecae32b8a09
 F src/whereInt.h e83f7ba73db5b1b2685118fad67d178fbe04751a25419f0f6ff73e58b4807325
-F src/wherecode.c 560424f5845ec4ef1e36e8a6a066cc497aaf8163fef29fa18a0317e1e14ba9b7
+F src/wherecode.c c2324c3204b7f51648cc71aa0c98bff3c72c96c2e0c2169809a921febef7e5e1
 F src/whereexpr.c 791544603b254cf11f8e84e3b50b0863c57322e9f213b828680f658e232ebc57
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1138,7 +1138,7 @@ F test/join.test 25da4f53523a4aa17c893134b47fba6aa4799bb33350517b157785878290e23
 F test/join2.test 9bdc615841b91c97a16d68bad9508aea11fa0c6b34e5689847bcc4dac70e4990
 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
-F test/join5.test f418fccdfefa41f1659663463aa517431ddcf3e30ccbb80e64173b7d615a03f4
+F test/join5.test d22395f7d4020a58cabbc8316f300a5cfef84aee9e8ba7ce79b33cc43a3e1e2e
 F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c
 F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
 F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 88b43d798cc5aa59855e92d3e658aee9f0a5def6ffbc5db77af048d75ecdf8cc
-R 9caa791b08a3199a0c8bfc0ce172ce1a
+P b11f4d080aa9e6f694e2ec401e871f42bf25997e8e8bf77fa9b6014a50466e3c
+R e684e836076af2e815def08dd3898cfa
 U drh
-Z 89026152bfb77d836155ab364d1ebd27
+Z 309930b0d62304a131a19ba2d8419e5a
diff --git a/manifest.uuid b/manifest.uuid
index 274a6197ab..c9a3436415 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-b11f4d080aa9e6f694e2ec401e871f42bf25997e8e8bf77fa9b6014a50466e3c
\ No newline at end of file
+d342ab722de1fc3f34219c9755253db9d88eb50a6fa5cc257207008b00e7fc03
\ No newline at end of file
diff --git a/src/where.c b/src/where.c
index 859dac7cbb..44423f4bda 100644
--- a/src/where.c
+++ b/src/where.c
@@ -5000,15 +5000,15 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
     WhereLoop *pLoop = pWInfo->a[i].pWLoop;
     const int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
     if( (pLoop->wsFlags & reqFlags)==reqFlags
-     && (pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0
+     && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0)
     ){
       SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
       Table *pTab = pItem->pTab;
       pTab->tabFlags |= TF_StatsUsed;
       if( nSearch > pTab->nRowLogEst
-       && (pItem->fg.jointype & JT_LEFT)==0
        && (pTab->tabFlags & TF_HasStat1)!=0
       ){
+        testcase( pItem->fg.jointype & JT_LEFT );
         pLoop->wsFlags |= WHERE_BLOOMFILTER;
         pLoop->wsFlags &= ~WHERE_IDX_ONLY;
         WHERETRACE(0xffff, (
diff --git a/src/wherecode.c b/src/wherecode.c
index 05d1f12ced..8cc76b1fdf 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -236,42 +236,37 @@ int sqlite3WhereExplainBloomFilter(
   const WhereLevel *pLevel           /* Bloom filter on this level */
 ){
   int ret = 0;
-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
-  if( sqlite3ParseToplevel(pParse)->explain==2 )
-#endif
-  {
-    SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom];
-    Vdbe *v = pParse->pVdbe;      /* VM being constructed */
-    sqlite3 *db = pParse->db;     /* Database handle */
-    char *zMsg;                   /* Text to add to EQP output */
-    int i;                        /* Loop counter */
-    WhereLoop *pLoop;             /* The where loop */
-    StrAccum str;                 /* EQP output string */
-    char zBuf[100];               /* Initial space for EQP output string */
+  SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom];
+  Vdbe *v = pParse->pVdbe;      /* VM being constructed */
+  sqlite3 *db = pParse->db;     /* Database handle */
+  char *zMsg;                   /* Text to add to EQP output */
+  int i;                        /* Loop counter */
+  WhereLoop *pLoop;             /* The where loop */
+  StrAccum str;                 /* EQP output string */
+  char zBuf[100];               /* Initial space for EQP output string */
 
-    sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
-    str.printfFlags = SQLITE_PRINTF_INTERNAL;
-    sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem);
-    pLoop = pLevel->pWLoop;
-    if( pLoop->wsFlags & WHERE_IPK ){
-      const Table *pTab = pItem->pTab;
-      if( pTab->iPKey>=0 ){
-        sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName);
-      }else{
-        sqlite3_str_appendf(&str, "rowid=?");
-      }
+  sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+  str.printfFlags = SQLITE_PRINTF_INTERNAL;
+  sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem);
+  pLoop = pLevel->pWLoop;
+  if( pLoop->wsFlags & WHERE_IPK ){
+    const Table *pTab = pItem->pTab;
+    if( pTab->iPKey>=0 ){
+      sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName);
     }else{
-      for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){
-        const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i);
-        if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5);
-        sqlite3_str_appendf(&str, "%s=?", z);
-      }
+      sqlite3_str_appendf(&str, "rowid=?");
+    }
+  }else{
+    for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){
+      const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i);
+      if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5);
+      sqlite3_str_appendf(&str, "%s=?", z);
     }
-    sqlite3_str_append(&str, ")", 1);
-    zMsg = sqlite3StrAccumFinish(&str);
-    ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
-                            pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
   }
+  sqlite3_str_append(&str, ")", 1);
+  zMsg = sqlite3StrAccumFinish(&str);
+  ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
+                          pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
   return ret;
 }
 #endif /* SQLITE_OMIT_EXPLAIN */
@@ -1390,7 +1385,9 @@ static SQLITE_NOINLINE void filterPullDown(
     WhereLevel *pLevel = &pWInfo->a[iLevel];
     WhereLoop *pLoop = pLevel->pWLoop;
     if( pLevel->regFilter==0 ) continue;
-    if( pLoop->prereq & notReady ) continue;
+    /*         ,--- Because constructBloomFilter() has will not have set
+    **  vvvvv--'    pLevel->regFilter if this were true. */
+    if( NEVER(pLoop->prereq & notReady) ) continue;
     if( pLoop->wsFlags & WHERE_IPK ){
       WhereTerm *pTerm = pLoop->aLTerm[0];
       int regRowid;
diff --git a/test/join5.test b/test/join5.test
index e2ff2f6c27..0ae4ca1127 100644
--- a/test/join5.test
+++ b/test/join5.test
@@ -303,6 +303,7 @@ do_eqp_test 7.4 {
 } {
   QUERY PLAN
   |--SCAN t3
+  |--BLOOM FILTER ON t4 (x=?)
   `--SEARCH t4 USING INDEX t4xz (x=?)
 } 
 

From 5a4ac1cc302388e8717f9873fc90e5b648a74eac Mon Sep 17 00:00:00 2001
From: drh <>
Date: Thu, 9 Dec 2021 19:42:52 +0000
Subject: [PATCH 18/18] Remove unused code and fix comments.  Final cleanup
 before merging.

FossilOrigin-Name: ce42039f5647b1f276acf5d9911528ecb47df1544a587def72c8cd6b2f664289
---
 manifest        | 22 +++++++++++-----------
 manifest.uuid   |  2 +-
 src/malloc.c    | 21 ---------------------
 src/sqlite.h.in | 15 +++++++++------
 src/sqliteInt.h |  4 ----
 src/vdbe.c      | 17 -----------------
 src/where.c     |  6 +++---
 src/wherecode.c |  2 +-
 8 files changed, 25 insertions(+), 64 deletions(-)

diff --git a/manifest b/manifest
index a498dda5fc..d3b9720825 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Enable\sbloom\sfilters\sfor\sthe\sright\stable\sof\sa\sLEFT\sJOIN.\s\sFix\sunreachable\nbranches.
-D 2021-12-09T18:44:03.070
+C Remove\sunused\scode\sand\sfix\scomments.\s\sFinal\scleanup\sbefore\smerging.
+D 2021-12-09T19:42:52.951
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -516,7 +516,7 @@ F src/insert.c e0293a6f686e18cb2c9dd0619a731518e0109d7e1f1db1932974659e7843cfd1
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c e1dcff1c916bf6834e150b492eddda5d9792453182d2ad64294d2266b6e93c4c
 F src/main.c 674a0fdfc2808e1d5a78b2eefe2ec3f93428cf82f0f6c013d577df1a1caa5940
-F src/malloc.c d9172a3946f11384f2fd6a799554ee26c6bb407c4bd0874a456ed485a2e362e4
+F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
 F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
@@ -553,10 +553,10 @@ F src/resolve.c 4a1db4aadd802683db40ca2dbbb268187bd195f10cbdb7206dbd8ac988795571
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a7a3d9f54eb24821ec5f67f2e5589b68a5d42d46fc5849d7376886777d93a85a
 F src/shell.c.in 239bee1085d94964f02582b0714dc3fc85cfc16e27e95813e4dbc24bb215a7e0
-F src/sqlite.h.in 50c8f27251b11f1c89b06abc6e4085fce15151bcbd355a44609ecb2ba5424841
+F src/sqlite.h.in 5999d6db0e65afbd686b76cddc385b310aa3815624edba43987913067f50e209
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h 31b9673bc26b5b2a846bf26ce7124c869f64368f4eaac865d9350749ea314000
+F src/sqliteInt.h b4391c3c2ae0a8020ce0f543fc2b529f9bcdf72ab7ba3c31d170e3228169162f
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -623,7 +623,7 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
 F src/util.c 569349b0bddcbfbc661856f446adb92e1b0a47b3cbef548da9fc5aa639d7964c
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
-F src/vdbe.c 855ee903521fcc5a799f673f5b05fc599dc50a31e6cb6a15e1e8a6858087595b
+F src/vdbe.c 5c05b1149a930851f36b29172002dfa56ce703e13f82d5bb4242a29dc144e350
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
 F src/vdbeInt.h 910985ac2783fe0938b314d811759d53fd25caf215810f62ca1ff068d6d60d7b
 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
@@ -638,9 +638,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c abd620b4823c0c72322ad5307805d0a350e808bd2424e3e00a1c3ecae32b8a09
+F src/where.c 71bbbac8748ddd470dd3c4213342dce2e39c2c0aa84f9b03d80bbc075fb7c502
 F src/whereInt.h e83f7ba73db5b1b2685118fad67d178fbe04751a25419f0f6ff73e58b4807325
-F src/wherecode.c c2324c3204b7f51648cc71aa0c98bff3c72c96c2e0c2169809a921febef7e5e1
+F src/wherecode.c 98ec56212ea7c0993f5eb808c515076d42c814f833c1641022927903cef0e811
 F src/whereexpr.c 791544603b254cf11f8e84e3b50b0863c57322e9f213b828680f658e232ebc57
 F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1934,7 +1934,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P b11f4d080aa9e6f694e2ec401e871f42bf25997e8e8bf77fa9b6014a50466e3c
-R e684e836076af2e815def08dd3898cfa
+P d342ab722de1fc3f34219c9755253db9d88eb50a6fa5cc257207008b00e7fc03
+R 5df525a734b3c13f01d7f10e288ab697
 U drh
-Z 309930b0d62304a131a19ba2d8419e5a
+Z ccda542cb10f0dae6599ca15d7e6a94e
diff --git a/manifest.uuid b/manifest.uuid
index c9a3436415..96b8bdf103 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-d342ab722de1fc3f34219c9755253db9d88eb50a6fa5cc257207008b00e7fc03
\ No newline at end of file
+ce42039f5647b1f276acf5d9911528ecb47df1544a587def72c8cd6b2f664289
\ No newline at end of file
diff --git a/src/malloc.c b/src/malloc.c
index 9a2eaeec15..932cecc210 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -211,27 +211,6 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
   return mx;
 }
 
-#if 0
-/*
-** Return an estimate of the amount of unallocated memory.
-**
-** This the hard heap limit minus the current memory usage.  It might
-** not be possible to allocate this much memory all at once.  This is
-** only an estimate.
-*/
-sqlite3_int64 sqlite3EstMemoryAvailable(void){
-  sqlite3_int64 n;
-  sqlite3_mutex_enter(mem0.mutex);
-  n = mem0.alarmThreshold;
-  if( n<=0 ) n = mem0.hardLimit;
-  sqlite3_mutex_leave(mem0.mutex);
-  if( n<=0 ) n = LARGEST_INT64;
-  n -= sqlite3_memory_used();
-  if( n<0 ) n = 0;
-  return n;
-}
-#endif
-
 /*
 ** Trigger the alarm 
 */
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index f7cf4d2fe1..63a6f37e9a 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -8468,12 +8468,15 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
 ** The counter is incremented on the first [sqlite3_step()] call of each
 ** cycle.
 **
-** [[SQLITE_STMTSTATUS_FILTER HIT]] <dt>SQLITE_STMTSTATUS_FILTER_HIT</dt>
-** <dd>^This is the number of times that a join step was bypassed because
-** a Bloom filtered returned non-found.  The corresponding
-** SQLITE_STMTSTATUS_FILTER_MISS value is the number of times that the
-** Bloom filter returned a find, and thus the join step had to be processed
-** as normal.
+** [[SQLITE_STMTSTATUS_FILTER_MISS]]
+** [[SQLITE_STMTSTATUS_FILTER HIT]] 
+** <dt>SQLITE_STMTSTATUS_FILTER_HIT<br>
+** SQLITE_STMTSTATUS_FILTER_MISS</dt>
+** <dd>^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join
+** step was bypassed because a Bloom filter returned not-found.  The
+** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of
+** times that the Bloom filter returned a find, and thus the join step
+** had to be processed as normal.
 **
 ** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt>
 ** <dd>^This is the approximate number of bytes of heap memory
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 0104bace6d..bd02e2626f 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3927,7 +3927,6 @@ struct Sqlite3Config {
   int iOnceResetThreshold;          /* When to reset OP_Once counters */
   u32 szSorterRef;                  /* Min size in bytes to use sorter-refs */
   unsigned int iPrngSeed;           /* Alternative fixed seed for the PRNG */
-  int iEstCountScale;               /* Multiple RowCountEst() by this amount */
   /* vvvv--- must be last ---vvv */
 #ifdef SQLITE_DEBUG
   sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */
@@ -4294,9 +4293,6 @@ void sqlite3MemSetDefault(void);
 void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
 #endif
 int sqlite3HeapNearlyFull(void);
-#if 0
-sqlite3_int64 sqlite3EstMemoryAvailable(void);
-#endif
 
 /*
 ** On systems with ample stack space and that support alloca(), make
diff --git a/src/vdbe.c b/src/vdbe.c
index ef60ed0123..27e030ec19 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -671,23 +671,6 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
   }
 }
 
-/*
-** The minimum size (in bytes) for a Bloom filter.
-**
-** No Bloom filter will be smaller than this many bytes.  But they
-** may be larger.
-*/
-#ifndef SQLITE_BLOOM_MIN
-# define SQLITE_BLOOM_MIN 10000
-#endif
-
-/*
-** The maximum size (in bytes) for a Bloom filter.
-*/
-#ifndef SQLITE_BLOOM_MAX
-# define SQLITE_BLOOM_MAX 1000000
-#endif
-
 /*
 ** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning
 ** with pOp->p3.  Return the hash.
diff --git a/src/where.c b/src/where.c
index 44423f4bda..be75101b26 100644
--- a/src/where.c
+++ b/src/where.c
@@ -4977,10 +4977,9 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
 **        filter.
 **   (2)  Some searches are expected to find zero rows.  (This is determined
 **        by the WHERE_SELFCULL flag on the term.)
-**   (3)  The table being searched is not the right table of a LEFT JOIN
-**   (4)  Bloom-filter processing is not disabled.  (Checked by the
+**   (3)  Bloom-filter processing is not disabled.  (Checked by the
 **        caller.)
-**   (5)  The size of the table being searched is known by ANALYZE.
+**   (4)  The size of the table being searched is known by ANALYZE.
 **
 ** This block of code merely checks to see if a Bloom filter would be
 ** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the
@@ -5000,6 +4999,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
     WhereLoop *pLoop = pWInfo->a[i].pWLoop;
     const int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
     if( (pLoop->wsFlags & reqFlags)==reqFlags
+     /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
      && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0)
     ){
       SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
diff --git a/src/wherecode.c b/src/wherecode.c
index 8cc76b1fdf..dd73bc4e83 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1371,7 +1371,7 @@ static void whereApplyPartialIndexConstraints(
 **
 ** We know that an inner loop uses a Bloom filter because it has the
 ** WhereLevel.regFilter set.  If an inner-loop Bloom filter is checked,
-** then clear the WhereLoeve.regFilter value to prevent the Bloom filter
+** then clear the WhereLevel.regFilter value to prevent the Bloom filter
 ** from being checked a second time when the inner loop is evaluated.
 */
 static SQLITE_NOINLINE void filterPullDown(