/ Check-in [158290c0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhance the LIKE optimization so that it works for arbitrary expressions on the LHS as long as the pattern on the RHS does not begin with a digit or a minus sign.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 158290c0abafde67ee3f2363f0b6646887841df3
User & Date: drh 2017-02-10 21:37:57
References
2019-06-14
12:28
Refactor the LIKE optimization decision logic so that it uses sqlite3AtoF() on both boundary keys to determine if the optimization can be used when the LHS is something that might not have TEXT affinity. Ticket [ce8717f0885af975]. See also [c94369cae9b561b1], [b043a54c3de54b28], [fd76310a5e843e07], and [158290c0abafde67]. check-in: b4a9e09e user: drh tags: trunk
2017-06-23
19:21 New ticket [b9f01010] Violation of the Query Planner Stability Guarantee. artifact: 81c613f2 user: drh
2017-02-17
02:04
Fix a test case that was made to fail by the LIKE optimization enhancement in check-in [158290c0ab] but which went unnoticed because test builds were running with ICU enabled and ICU disables the LIKE optimization. check-in: 218b2bbb user: drh tags: trunk
Context
2017-02-10
21:40
Bump the version number up to 3.18.0. check-in: 7520c238 user: drh tags: trunk
21:37
Enhance the LIKE optimization so that it works for arbitrary expressions on the LHS as long as the pattern on the RHS does not begin with a digit or a minus sign. check-in: 158290c0 user: drh tags: trunk
19:38
Add the "," flag to printf(). check-in: 064445b1 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/whereexpr.c.

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
...
234
235
236
237
238
239
240

















241
242
243
244
245
246
247
    return 0;
  }
#ifdef SQLITE_EBCDIC
  if( *pnoCase ) return 0;
#endif
  pList = pExpr->x.pList;
  pLeft = pList->a[1].pExpr;
  if( pLeft->op!=TK_COLUMN 
   || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT 
   || 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. */
    return 0;
  }
  assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */

  pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr);
  op = pRight->op;
  if( op==TK_VARIABLE ){
    Vdbe *pReprepare = pParse->pReprepare;
    int iCol = pRight->iColumn;
    pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
................................................................................
    }
    sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
    assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
  }else if( op==TK_STRING ){
    z = pRight->u.zToken;
  }
  if( z ){

















    cnt = 0;
    while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
      cnt++;
    }
    if( cnt!=0 && 255!=(u8)z[cnt-1] ){
      Expr *pPrefix;
      *pisComplete = c==wc[0] && z[cnt+1]==0;







<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







209
210
211
212
213
214
215









216
217
218
219
220
221
222
...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    return 0;
  }
#ifdef SQLITE_EBCDIC
  if( *pnoCase ) return 0;
#endif
  pList = pExpr->x.pList;
  pLeft = pList->a[1].pExpr;










  pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr);
  op = pRight->op;
  if( op==TK_VARIABLE ){
    Vdbe *pReprepare = pParse->pReprepare;
    int iCol = pRight->iColumn;
    pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
................................................................................
    }
    sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
    assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
  }else if( op==TK_STRING ){
    z = pRight->u.zToken;
  }
  if( z ){

    /* If the RHS begins with a digit or a minus sign, then the LHS must
    ** be an ordinary column (not a virtual table column) with TEXT affinity.
    ** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
    ** even though "lhs LIKE rhs" is true.  But if the RHS does not start
    ** with a digit or '-', then "lhs LIKE rhs" will always be false if
    ** the LHS is numeric and so the optimization still works.
    */
    if( sqlite3Isdigit(z[0]) || z[0]=='-' ){
      if( pLeft->op!=TK_COLUMN 
       || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT 
       || IsVirtual(pLeft->pTab)  /* Value might be numeric */
      ){
        sqlite3ValueFree(pVal);
        return 0;
      }
    }
    cnt = 0;
    while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
      cnt++;
    }
    if( cnt!=0 && 255!=(u8)z[cnt-1] ){
      Expr *pPrefix;
      *pisComplete = c==wc[0] && z[cnt+1]==0;

Changes to test/vtab1.test.

1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
unset -nocomplain echo_module_begin_fail

do_execsql_test 18.1.0 {
  CREATE TABLE t6(a, b TEXT);
  CREATE INDEX i6 ON t6(b, a);
  INSERT INTO t6 VALUES(1, 'Peter');
  INSERT INTO t6 VALUES(2, 'Andrew');
  INSERT INTO t6 VALUES(3, 'James');
  INSERT INTO t6 VALUES(4, 'John');
  INSERT INTO t6 VALUES(5, 'Phillip');
  INSERT INTO t6 VALUES(6, 'Bartholomew');
  CREATE VIRTUAL TABLE e6 USING echo(t6);
}

foreach {tn sql res filter} {
  1.1 "SELECT a FROM e6 WHERE b>'James'" {4 1 5}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b > ?} James}

  1.2 "SELECT a FROM e6 WHERE b>='J' AND b<'K'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b >= ? AND b < ?} J K}

  1.3 "SELECT a FROM e6 WHERE b LIKE 'J%'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} J%}

  1.4 "SELECT a FROM e6 WHERE b LIKE 'j%'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} j%}
} {
  set echo_module {}
  do_execsql_test 18.$tn.1 $sql $res
  do_test         18.$tn.2 { lrange $::echo_module 2 end } $filter
}

do_execsql_test 18.2.0 {  PRAGMA case_sensitive_like = ON }
foreach {tn sql res filter} {
  2.1 "SELECT a FROM e6 WHERE b LIKE 'J%'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} J%}

  2.2 "SELECT a FROM e6 WHERE b LIKE 'j%'" {}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} j%}
} {
  set echo_module {}
  do_execsql_test 18.$tn.1 $sql $res
  do_test         18.$tn.2 { lrange $::echo_module 2 end } $filter
}
do_execsql_test 18.2.x {  PRAGMA case_sensitive_like = OFF }








|
|






|
|

|
|

|
|

|
|








|
|

|
|







1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
unset -nocomplain echo_module_begin_fail

do_execsql_test 18.1.0 {
  CREATE TABLE t6(a, b TEXT);
  CREATE INDEX i6 ON t6(b, a);
  INSERT INTO t6 VALUES(1, 'Peter');
  INSERT INTO t6 VALUES(2, 'Andrew');
  INSERT INTO t6 VALUES(3, '8James');
  INSERT INTO t6 VALUES(4, '8John');
  INSERT INTO t6 VALUES(5, 'Phillip');
  INSERT INTO t6 VALUES(6, 'Bartholomew');
  CREATE VIRTUAL TABLE e6 USING echo(t6);
}

foreach {tn sql res filter} {
  1.1 "SELECT a FROM e6 WHERE b>'8James'" {4 2 6 1 5}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b > ?} 8James}

  1.2 "SELECT a FROM e6 WHERE b>='8' AND b<'9'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b >= ? AND b < ?} 8 9}

  1.3 "SELECT a FROM e6 WHERE b LIKE '8J%'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8J%}

  1.4 "SELECT a FROM e6 WHERE b LIKE '8j%'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8j%}
} {
  set echo_module {}
  do_execsql_test 18.$tn.1 $sql $res
  do_test         18.$tn.2 { lrange $::echo_module 2 end } $filter
}

do_execsql_test 18.2.0 {  PRAGMA case_sensitive_like = ON }
foreach {tn sql res filter} {
  2.1 "SELECT a FROM e6 WHERE b LIKE '8J%'" {3 4}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8J%}

  2.2 "SELECT a FROM e6 WHERE b LIKE '8j%'" {}
    {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8j%}
} {
  set echo_module {}
  do_execsql_test 18.$tn.1 $sql $res
  do_test         18.$tn.2 { lrange $::echo_module 2 end } $filter
}
do_execsql_test 18.2.x {  PRAGMA case_sensitive_like = OFF }

Changes to test/vtabH.test.

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
do_execsql_test 1.0 {
  CREATE TABLE t6(a, b TEXT);
  CREATE INDEX i6 ON t6(b, a);
  CREATE VIRTUAL TABLE e6 USING echo(t6);
}

foreach {tn sql expect} {
  1 "SELECT * FROM e6 WHERE b LIKE 'abc'" {
    xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b like ?}
    xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} abc
  }

  2 "SELECT * FROM e6 WHERE b GLOB 'abc'" {
    xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b glob ?}
    xFilter {SELECT rowid, a, b FROM 't6' WHERE b glob ?} abc
  }
} {
  do_test 1.$tn {
    set echo_module {}
    execsql $sql
    set ::echo_module
  } [list {*}$expect]







|

|


|

|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
do_execsql_test 1.0 {
  CREATE TABLE t6(a, b TEXT);
  CREATE INDEX i6 ON t6(b, a);
  CREATE VIRTUAL TABLE e6 USING echo(t6);
}

foreach {tn sql expect} {
  1 "SELECT * FROM e6 WHERE b LIKE '8abc'" {
    xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b like ?}
    xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8abc
  }

  2 "SELECT * FROM e6 WHERE b GLOB '8abc'" {
    xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b glob ?}
    xFilter {SELECT rowid, a, b FROM 't6' WHERE b glob ?} 8abc
  }
} {
  do_test 1.$tn {
    set echo_module {}
    execsql $sql
    set ::echo_module
  } [list {*}$expect]