The LIKE optimization must be applied twice, once for strings and a second
time for BLOBs. Ticket [05f43be8fdda9f]. This check-in is a proof-of-concept of how that might be done. FossilOrigin-Name: 5757e803cb5759b476bbc6453c58340089611420
This commit is contained in:
+14
-2
@@ -1015,7 +1015,7 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */
|
||||
** Synopsis: r[P2]='P4'
|
||||
**
|
||||
** P4 points to a nul terminated UTF-8 string. This opcode is transformed
|
||||
** into a String before it is executed for the first time. During
|
||||
** into a String opcode before it is executed for the first time. During
|
||||
** this transformation, the length of string P4 is computed and stored
|
||||
** as the P1 parameter.
|
||||
*/
|
||||
@@ -1047,10 +1047,15 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */
|
||||
/* Fall through to the next case, OP_String */
|
||||
}
|
||||
|
||||
/* Opcode: String P1 P2 * P4 *
|
||||
/* Opcode: String P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P2]='P4' (len=P1)
|
||||
**
|
||||
** The string value P4 of length P1 (bytes) is stored in register P2.
|
||||
**
|
||||
** If P5!=0 and the content of register P3 is greater than zero, then
|
||||
** the datatype of the register P2 is convert to BLOB. The content is
|
||||
** the same string text, it is merely interpreted as a BLOB as if it
|
||||
** had been CAST.
|
||||
*/
|
||||
case OP_String: { /* out2-prerelease */
|
||||
assert( pOp->p4.z!=0 );
|
||||
@@ -1059,6 +1064,13 @@ case OP_String: { /* out2-prerelease */
|
||||
pOut->n = pOp->p1;
|
||||
pOut->enc = encoding;
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
if( pOp->p5 ){
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
assert( pIn3->flags & MEM_Int );
|
||||
if( pIn3->u.i ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+43
-6
@@ -202,7 +202,7 @@ static void whereClauseClear(WhereClause *pWC){
|
||||
** calling this routine. Such pointers may be reinitialized by referencing
|
||||
** the pWC->a[] array.
|
||||
*/
|
||||
static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
|
||||
static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
|
||||
WhereTerm *pTerm;
|
||||
int idx;
|
||||
testcase( wtFlags & TERM_VIRTUAL );
|
||||
@@ -627,7 +627,11 @@ static void exprAnalyzeAll(
|
||||
** so and false if not.
|
||||
**
|
||||
** In order for the operator to be optimizible, the RHS must be a string
|
||||
** literal that does not begin with a wildcard.
|
||||
** literal that does not begin with a wildcard. The LHS must be a column
|
||||
** that may only be NULL, a string, or a BLOB, never a number. (This means
|
||||
** that virtual tables cannot participate in the LIKE optimization.) If the
|
||||
** collating sequence for the column on the LHS must be appropriate for
|
||||
** the operator.
|
||||
*/
|
||||
static int isLikeOrGlob(
|
||||
Parse *pParse, /* Parsing and code generating context */
|
||||
@@ -656,7 +660,7 @@ static int isLikeOrGlob(
|
||||
pLeft = pList->a[1].pExpr;
|
||||
if( pLeft->op!=TK_COLUMN
|
||||
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|
||||
|| IsVirtual(pLeft->pTab)
|
||||
|| IsVirtual(pLeft->pTab) /* Value might be numeric */
|
||||
){
|
||||
/* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must
|
||||
** be the name of an indexed column with TEXT affinity. */
|
||||
@@ -1286,7 +1290,8 @@ static void exprAnalyze(
|
||||
sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
|
||||
pStr1, 0);
|
||||
transferJoinMarkings(pNewExpr1, pExpr);
|
||||
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
idxNew1 = whereClauseInsert(pWC, pNewExpr1,
|
||||
TERM_LIKEOPT|TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew1==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew1);
|
||||
pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
|
||||
@@ -1294,7 +1299,8 @@ static void exprAnalyze(
|
||||
sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
|
||||
pStr2, 0);
|
||||
transferJoinMarkings(pNewExpr2, pExpr);
|
||||
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
idxNew2 = whereClauseInsert(pWC, pNewExpr2,
|
||||
TERM_LIKEOPT|TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew2==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew2);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
@@ -2966,7 +2972,21 @@ static void addScanStatus(
|
||||
# define addScanStatus(a, b, c, d) ((void)d)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Look at the last instruction coded. If that instruction is OP_String8
|
||||
** and if pLoop->iLikeRepCntr is non-zero, then change the P3 to be
|
||||
** pLoop->iLikeRepCntr and set P5.
|
||||
**
|
||||
** This is part of the LIKE optimization. FIXME: Explain in more detail
|
||||
*/
|
||||
static void whereLikeOptimizationStringFixup(Vdbe *v, WhereLevel *pLevel){
|
||||
VdbeOp *pOp;
|
||||
pOp = sqlite3VdbeGetOp(v, -1);
|
||||
if( pLevel->iLikeRepCntr && ALWAYS(pOp->opcode==OP_String8) ){
|
||||
pOp->p3 = pLevel->iLikeRepCntr;
|
||||
pOp->p5 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for the start of the iLevel-th loop in the WHERE clause
|
||||
@@ -3300,6 +3320,14 @@ static Bitmask codeOneLoopStart(
|
||||
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
|
||||
pRangeEnd = pLoop->aLTerm[j++];
|
||||
nExtraReg = 1;
|
||||
if( pRangeStart
|
||||
&& (pRangeStart->wtFlags & TERM_LIKEOPT)!=0
|
||||
&& (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0
|
||||
){
|
||||
pLevel->iLikeRepCntr = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLikeRepCntr);
|
||||
pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v);
|
||||
}
|
||||
if( pRangeStart==0
|
||||
&& (j = pIdx->aiColumn[nEq])>=0
|
||||
&& pIdx->pTable->aCol[j].notNull==0
|
||||
@@ -3342,6 +3370,7 @@ static Bitmask codeOneLoopStart(
|
||||
if( pRangeStart ){
|
||||
Expr *pRight = pRangeStart->pExpr->pRight;
|
||||
sqlite3ExprCode(pParse, pRight, regBase+nEq);
|
||||
whereLikeOptimizationStringFixup(v, pLevel);
|
||||
if( (pRangeStart->wtFlags & TERM_VNULL)==0
|
||||
&& sqlite3ExprCanBeNull(pRight)
|
||||
){
|
||||
@@ -3387,6 +3416,7 @@ static Bitmask codeOneLoopStart(
|
||||
Expr *pRight = pRangeEnd->pExpr->pRight;
|
||||
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
|
||||
sqlite3ExprCode(pParse, pRight, regBase+nEq);
|
||||
whereLikeOptimizationStringFixup(v, pLevel);
|
||||
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
|
||||
&& sqlite3ExprCanBeNull(pRight)
|
||||
){
|
||||
@@ -6595,6 +6625,13 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
sqlite3VdbeJumpHere(v, pLevel->addrSkip);
|
||||
sqlite3VdbeJumpHere(v, pLevel->addrSkip-2);
|
||||
}
|
||||
if( pLevel->addrLikeRep ){
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLikeRepCntr);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, pLevel->iLikeRepCntr, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrLikeRep);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
if( pLevel->iLeftJoin ){
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|
||||
|
||||
+4
-1
@@ -69,6 +69,8 @@ struct WhereLevel {
|
||||
int addrCont; /* Jump here to continue with the next loop cycle */
|
||||
int addrFirst; /* First instruction of interior of the loop */
|
||||
int addrBody; /* Beginning of the body of this loop */
|
||||
int iLikeRepCntr; /* LIKE range processing counter register */
|
||||
int addrLikeRep; /* LIKE range processing address */
|
||||
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 ends the loop */
|
||||
@@ -253,7 +255,7 @@ struct WhereTerm {
|
||||
} u;
|
||||
LogEst truthProb; /* Probability of truth for this expression */
|
||||
u16 eOperator; /* A WO_xx value describing <op> */
|
||||
u8 wtFlags; /* TERM_xxx bit flags. See below */
|
||||
u16 wtFlags; /* TERM_xxx bit flags. See below */
|
||||
u8 nChild; /* Number of children that must disable us */
|
||||
WhereClause *pWC; /* The clause this term is part of */
|
||||
Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
|
||||
@@ -275,6 +277,7 @@ struct WhereTerm {
|
||||
#else
|
||||
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
|
||||
#endif
|
||||
#define TERM_LIKEOPT 0x100 /* Used by the LIKE optimization */
|
||||
|
||||
/*
|
||||
** An instance of the WhereScan object is used as an iterator for locating
|
||||
|
||||
Reference in New Issue
Block a user