1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

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:
drh
2015-03-06 16:45:16 +00:00
parent 8dd7a6a925
commit f07cf6e2a5
5 changed files with 73 additions and 18 deletions

View File

@@ -1,5 +1,5 @@
C Clearification\sof\ssome\sdocumentation\stext.\s\sAdded\srequirements\smarks. C The\sLIKE\soptimization\smust\sbe\sapplied\stwice,\sonce\sfor\sstrings\sand\sa\ssecond\ntime\sfor\sBLOBs.\s\sTicket\s[05f43be8fdda9f].\s\sThis\scheck-in\sis\sa\sproof-of-concept\nof\show\sthat\smight\sbe\sdone.
D 2015-03-06T04:37:26.939 D 2015-03-06T16:45:16.543
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 2f643d6968dfc0b82d2e546a0525a39079f9e928 F Makefile.in 2f643d6968dfc0b82d2e546a0525a39079f9e928
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -293,7 +293,7 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e
F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec
F src/vdbe.c 991e9b2c38cdc987ed214249b3c72ea73a06fb2e F src/vdbe.c 6bee3b85a2f013a8fdc496996089d3b6bedfb525
F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3 F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
F src/vdbeInt.h bb56fd199d8af1a2c1b9639ee2f70724b4338e3a F src/vdbeInt.h bb56fd199d8af1a2c1b9639ee2f70724b4338e3a
F src/vdbeapi.c dac0d0d8009a8aa549cd77d9c29da44c0344f0c4 F src/vdbeapi.c dac0d0d8009a8aa549cd77d9c29da44c0344f0c4
@@ -307,8 +307,8 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c 39303f2c9db02a4e422cd8eb2c8760420c6a51fe F src/wal.c 39303f2c9db02a4e422cd8eb2c8760420c6a51fe
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c c1b3706929fe918966227f3b91ff433a825037fd F src/where.c b7e82341d2570ac8a051e133cfc44c7eec79a30e
F src/whereInt.h d3633e9b592103241b74b0ec76185f3e5b8b62e0 F src/whereInt.h 0ba6257f2a44acd6262f259d5147cd01c52cc45b
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
@@ -1240,7 +1240,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 31d5e9b42e5c96207187dcde1cbbb1e79f26fca2 P 8c1e85aab9e0d90726057e25e2ea0663341c070f
R e3bd9c89d1416be2fa13657fae649477 R db06eec232964b57030b876196612d14
T *branch * like-opt-fix
T *sym-like-opt-fix *
T -sym-trunk *
U drh U drh
Z 0cf6c34744d872d2f1bdb9d1e644babc Z 9b71822fa0ca3d1b398ae4d623871060

View File

@@ -1 +1 @@
8c1e85aab9e0d90726057e25e2ea0663341c070f 5757e803cb5759b476bbc6453c58340089611420

View File

@@ -1015,7 +1015,7 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */
** Synopsis: r[P2]='P4' ** Synopsis: r[P2]='P4'
** **
** P4 points to a nul terminated UTF-8 string. This opcode is transformed ** 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 ** this transformation, the length of string P4 is computed and stored
** as the P1 parameter. ** 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 */ /* 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) ** Synopsis: r[P2]='P4' (len=P1)
** **
** The string value P4 of length P1 (bytes) is stored in register P2. ** 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 */ case OP_String: { /* out2-prerelease */
assert( pOp->p4.z!=0 ); assert( pOp->p4.z!=0 );
@@ -1059,6 +1064,13 @@ case OP_String: { /* out2-prerelease */
pOut->n = pOp->p1; pOut->n = pOp->p1;
pOut->enc = encoding; pOut->enc = encoding;
UPDATE_MAX_BLOBSIZE(pOut); 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; break;
} }

View File

@@ -202,7 +202,7 @@ static void whereClauseClear(WhereClause *pWC){
** calling this routine. Such pointers may be reinitialized by referencing ** calling this routine. Such pointers may be reinitialized by referencing
** the pWC->a[] array. ** the pWC->a[] array.
*/ */
static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
WhereTerm *pTerm; WhereTerm *pTerm;
int idx; int idx;
testcase( wtFlags & TERM_VIRTUAL ); testcase( wtFlags & TERM_VIRTUAL );
@@ -627,7 +627,11 @@ static void exprAnalyzeAll(
** so and false if not. ** so and false if not.
** **
** In order for the operator to be optimizible, the RHS must be a string ** 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( static int isLikeOrGlob(
Parse *pParse, /* Parsing and code generating context */ Parse *pParse, /* Parsing and code generating context */
@@ -656,7 +660,7 @@ static int isLikeOrGlob(
pLeft = pList->a[1].pExpr; pLeft = pList->a[1].pExpr;
if( pLeft->op!=TK_COLUMN if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT || 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 /* 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. */ ** be the name of an indexed column with TEXT affinity. */
@@ -1286,7 +1290,8 @@ static void exprAnalyze(
sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName), sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
pStr1, 0); pStr1, 0);
transferJoinMarkings(pNewExpr1, pExpr); transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); idxNew1 = whereClauseInsert(pWC, pNewExpr1,
TERM_LIKEOPT|TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew1==0 ); testcase( idxNew1==0 );
exprAnalyze(pSrc, pWC, idxNew1); exprAnalyze(pSrc, pWC, idxNew1);
pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
@@ -1294,7 +1299,8 @@ static void exprAnalyze(
sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName), sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
pStr2, 0); pStr2, 0);
transferJoinMarkings(pNewExpr2, pExpr); transferJoinMarkings(pNewExpr2, pExpr);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); idxNew2 = whereClauseInsert(pWC, pNewExpr2,
TERM_LIKEOPT|TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew2==0 ); testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew2); exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm]; pTerm = &pWC->a[idxTerm];
@@ -2966,7 +2972,21 @@ static void addScanStatus(
# define addScanStatus(a, b, c, d) ((void)d) # define addScanStatus(a, b, c, d) ((void)d)
#endif #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 ** 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 ){ if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
pRangeEnd = pLoop->aLTerm[j++]; pRangeEnd = pLoop->aLTerm[j++];
nExtraReg = 1; 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 if( pRangeStart==0
&& (j = pIdx->aiColumn[nEq])>=0 && (j = pIdx->aiColumn[nEq])>=0
&& pIdx->pTable->aCol[j].notNull==0 && pIdx->pTable->aCol[j].notNull==0
@@ -3342,6 +3370,7 @@ static Bitmask codeOneLoopStart(
if( pRangeStart ){ if( pRangeStart ){
Expr *pRight = pRangeStart->pExpr->pRight; Expr *pRight = pRangeStart->pExpr->pRight;
sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3ExprCode(pParse, pRight, regBase+nEq);
whereLikeOptimizationStringFixup(v, pLevel);
if( (pRangeStart->wtFlags & TERM_VNULL)==0 if( (pRangeStart->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight) && sqlite3ExprCanBeNull(pRight)
){ ){
@@ -3387,6 +3416,7 @@ static Bitmask codeOneLoopStart(
Expr *pRight = pRangeEnd->pExpr->pRight; Expr *pRight = pRangeEnd->pExpr->pRight;
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3ExprCode(pParse, pRight, regBase+nEq);
whereLikeOptimizationStringFixup(v, pLevel);
if( (pRangeEnd->wtFlags & TERM_VNULL)==0 if( (pRangeEnd->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight) && sqlite3ExprCanBeNull(pRight)
){ ){
@@ -6595,6 +6625,13 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3VdbeJumpHere(v, pLevel->addrSkip); sqlite3VdbeJumpHere(v, pLevel->addrSkip);
sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); 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 ){ if( pLevel->iLeftJoin ){
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0

View File

@@ -69,6 +69,8 @@ struct WhereLevel {
int addrCont; /* Jump here to continue with the next loop cycle */ int addrCont; /* Jump here to continue with the next loop cycle */
int addrFirst; /* First instruction of interior of the loop */ int addrFirst; /* First instruction of interior of the loop */
int addrBody; /* Beginning of the body of this 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 iFrom; /* Which entry in the FROM clause */
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ 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 */ int p1, p2; /* Operands of the opcode used to ends the loop */
@@ -253,7 +255,7 @@ struct WhereTerm {
} u; } u;
LogEst truthProb; /* Probability of truth for this expression */ LogEst truthProb; /* Probability of truth for this expression */
u16 eOperator; /* A WO_xx value describing <op> */ 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 */ u8 nChild; /* Number of children that must disable us */
WhereClause *pWC; /* The clause this term is part of */ WhereClause *pWC; /* The clause this term is part of */
Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
@@ -275,6 +277,7 @@ struct WhereTerm {
#else #else
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */ # define TERM_VNULL 0x00 /* Disabled if not using stat3 */
#endif #endif
#define TERM_LIKEOPT 0x100 /* Used by the LIKE optimization */
/* /*
** An instance of the WhereScan object is used as an iterator for locating ** An instance of the WhereScan object is used as an iterator for locating