aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2015-03-06 19:47:38 +0000
committerdrh <drh@noemail.net>2015-03-06 19:47:38 +0000
commit8f1a7ed33f89a56ed58aa99516014dbf0295df64 (patch)
tree433cb44c6a0bcf93d748e2896e64d4b7bba1048a /src
parentf07cf6e2a54866d913ca8a9b4eaae3d3aecb2601 (diff)
downloadsqlite-8f1a7ed33f89a56ed58aa99516014dbf0295df64.tar.gz
sqlite-8f1a7ed33f89a56ed58aa99516014dbf0295df64.zip
Fix the LIKE optimization even when comparing mixed-case BLOBs.
FossilOrigin-Name: a58aafdb4e1422b6a8ffc07a67984928bbedf919
Diffstat (limited to 'src')
-rw-r--r--src/where.c62
-rw-r--r--src/whereInt.h4
2 files changed, 53 insertions, 13 deletions
diff --git a/src/where.c b/src/where.c
index 82c7e699f..24c384347 100644
--- a/src/where.c
+++ b/src/where.c
@@ -1264,9 +1264,25 @@ static void exprAnalyze(
int idxNew1;
int idxNew2;
Token sCollSeqName; /* Name of collating sequence */
+ const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC;
+ pTerm->wtFlags |= TERM_LIKE;
pLeft = pExpr->x.pList->a[1].pExpr;
pStr2 = sqlite3ExprDup(db, pStr1, 0);
+
+ /* Convert the lower bound to upper-case and the upper bound to
+ ** lower-case (upper-case is less than lower-case in ASCII) so that
+ ** the range constraints also work for BLOBs
+ */
+ if( noCase && !pParse->db->mallocFailed ){
+ int i;
+ char c;
+ for(i=0; (c = pStr1->u.zToken[i])!=0; i++){
+ pStr1->u.zToken[i] = sqlite3Toupper(c);
+ pStr2->u.zToken[i] = sqlite3Tolower(c);
+ }
+ }
+
if( !db->mallocFailed ){
u8 c, *pC; /* Last character before the first wildcard */
pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1];
@@ -1286,12 +1302,11 @@ static void exprAnalyze(
sCollSeqName.z = noCase ? "NOCASE" : "BINARY";
sCollSeqName.n = 6;
pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
- pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
+ pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
pStr1, 0);
transferJoinMarkings(pNewExpr1, pExpr);
- idxNew1 = whereClauseInsert(pWC, pNewExpr1,
- TERM_LIKEOPT|TERM_VIRTUAL|TERM_DYNAMIC);
+ idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags);
testcase( idxNew1==0 );
exprAnalyze(pSrc, pWC, idxNew1);
pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
@@ -1299,8 +1314,7 @@ static void exprAnalyze(
sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
pStr2, 0);
transferJoinMarkings(pNewExpr2, pExpr);
- idxNew2 = whereClauseInsert(pWC, pNewExpr2,
- TERM_LIKEOPT|TERM_VIRTUAL|TERM_DYNAMIC);
+ idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags);
testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm];
@@ -2475,20 +2489,37 @@ static int whereInScanEst(
** but joins might run a little slower. The trick is to disable as much
** as we can without disabling too much. If we disabled in (1), we'd get
** the wrong answer. See ticket #813.
+**
+** If all the children of a term are disabled, then that term is also
+** automatically disabled. In this way, terms get disabled if derived
+** virtual terms are tested first. For example:
+**
+** x GLOB 'abc*' AND x>='abc' AND x<'acd'
+** \___________/ \______/ \_____/
+** parent child1 child2
+**
+** Only the parent term was in the original WHERE clause. The child1
+** and child2 terms were added by the LIKE optimization. If both of
+** the virtual child terms are valid, then testing of the parent can be
+** skipped.
*/
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
- if( pTerm
+ int nLoop = 0;
+ while( pTerm
&& (pTerm->wtFlags & TERM_CODED)==0
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
&& (pLevel->notReady & pTerm->prereqAll)==0
){
- pTerm->wtFlags |= TERM_CODED;
- if( pTerm->iParent>=0 ){
- WhereTerm *pOther = &pTerm->pWC->a[pTerm->iParent];
- if( (--pOther->nChild)==0 ){
- disableTerm(pLevel, pOther);
- }
+ if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){
+ pTerm->wtFlags |= TERM_LIKECOND;
+ }else{
+ pTerm->wtFlags |= TERM_CODED;
}
+ if( pTerm->iParent<0 ) break;
+ pTerm = &pTerm->pWC->a[pTerm->iParent];
+ pTerm->nChild--;
+ if( pTerm->nChild!=0 ) break;
+ nLoop++;
}
}
@@ -3807,6 +3838,7 @@ static Bitmask codeOneLoopStart(
*/
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
Expr *pE;
+ int skipLikeAddr = 0;
testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
@@ -3821,7 +3853,13 @@ static Bitmask codeOneLoopStart(
if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
continue;
}
+ if( pTerm->wtFlags & TERM_LIKECOND ){
+ assert( pLevel->iLikeRepCntr>0 );
+ skipLikeAddr = sqlite3VdbeAddOp1(v, OP_IfZero, pLevel->iLikeRepCntr);
+ VdbeCoverage(v);
+ }
sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL);
+ if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr);
pTerm->wtFlags |= TERM_CODED;
}
diff --git a/src/whereInt.h b/src/whereInt.h
index 6a42af47a..04cc2029d 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -277,7 +277,9 @@ struct WhereTerm {
#else
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
#endif
-#define TERM_LIKEOPT 0x100 /* Used by the LIKE optimization */
+#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */
+#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */
+#define TERM_LIKE 0x400 /* The original LIKE operator */
/*
** An instance of the WhereScan object is used as an iterator for locating