aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/where.c122
-rw-r--r--src/whereInt.h1
2 files changed, 118 insertions, 5 deletions
diff --git a/src/where.c b/src/where.c
index 371bf5230..22ed0c9b5 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
@@ -4842,6 +4898,49 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
}
/*
+** 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
** information needed to terminate the loop. Later, the calling routine
@@ -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 ff6ac4c39..fbe84787f 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) */