SQLite

Check-in [7f2c5c9ee3]
Login

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

Overview
Comment:Fix a problem handling expressions like "(a, b) IN (SELECT ... ORDER BY 1, 2)" when there is an index on "a" but not "b".
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | rowvalue
Files: files | file ages | folders
SHA1: 7f2c5c9ee3628c968306a5ab2e5a9a761f1b8055
User & Date: dan 2016-09-07 19:37:20.043
Context
2016-09-07
19:54
Add support for row-value comparisons, including IN operators, and row-value updates in the UPDATE statement. (check-in: ddb5f0558c user: drh tags: trunk)
19:37
Fix a problem handling expressions like "(a, b) IN (SELECT ... ORDER BY 1, 2)" when there is an index on "a" but not "b". (Closed-Leaf check-in: 7f2c5c9ee3 user: dan tags: rowvalue)
13:30
Simplify the affinity handling logic in codeAllEqualityTerms(). Logically the same, just a little easier to read and understand. (check-in: bbab9621f5 user: drh tags: rowvalue)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/wherecode.c.
441
442
443
444
445
446
447

448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465















466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
    for(i=iEq;i<pLoop->nLTerm; i++){
      if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
    }

    if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
      eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
    }else{

      sqlite3 *db = pParse->db;
      ExprList *pOrigRhs = pX->x.pSelect->pEList;
      ExprList *pOrigLhs = pX->pLeft->x.pList;
      ExprList *pRhs = 0;         /* New Select.pEList for RHS */
      ExprList *pLhs = 0;         /* New pX->pLeft vector */

      for(i=iEq;i<pLoop->nLTerm; i++){
        if( pLoop->aLTerm[i]->pExpr==pX ){
          int iField = pLoop->aLTerm[i]->iField - 1;
          Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
          Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);

          pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
          pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
        }
      }
      if( !db->mallocFailed ){
        Expr *pLeft = pX->pLeft;















        /* Take care here not to generate a TK_VECTOR containing only a
        ** single value. Since the parser never creates such a vector, some
        ** of the subroutines do not handle this case.  */
        if( pLhs->nExpr==1 ){
          pX->pLeft = pLhs->a[0].pExpr;
        }else{
          pLeft->x.pList = pLhs;
          aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
          testcase( aiMap==0 );
        }
        pX->x.pSelect->pEList = pRhs;
        eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
        testcase( aiMap!=0 && aiMap[0]!=0 );
        pX->x.pSelect->pEList = pOrigRhs;
        pLeft->x.pList = pOrigLhs;
        pX->pLeft = pLeft;
      }
      sqlite3ExprListDelete(pParse->db, pLhs);
      sqlite3ExprListDelete(pParse->db, pRhs);
    }








>

|
















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










|


|







441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
    for(i=iEq;i<pLoop->nLTerm; i++){
      if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
    }

    if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
      eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
    }else{
      Select *pSelect = pX->x.pSelect;
      sqlite3 *db = pParse->db;
      ExprList *pOrigRhs = pSelect->pEList;
      ExprList *pOrigLhs = pX->pLeft->x.pList;
      ExprList *pRhs = 0;         /* New Select.pEList for RHS */
      ExprList *pLhs = 0;         /* New pX->pLeft vector */

      for(i=iEq;i<pLoop->nLTerm; i++){
        if( pLoop->aLTerm[i]->pExpr==pX ){
          int iField = pLoop->aLTerm[i]->iField - 1;
          Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
          Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);

          pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
          pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
        }
      }
      if( !db->mallocFailed ){
        Expr *pLeft = pX->pLeft;

        if( pSelect->pOrderBy ){
          /* If the SELECT statement has an ORDER BY clause, zero the 
          ** iOrderByCol variables. These are set to non-zero when an 
          ** ORDER BY term exactly matches one of the terms of the 
          ** result-set. Since the result-set of the SELECT statement may
          ** have been modified or reordered, these variables are no longer 
          ** set correctly.  Since setting them is just an optimization, 
          ** it's easiest just to zero them here.  */
          ExprList *pOrderBy = pSelect->pOrderBy;
          for(i=0; i<pOrderBy->nExpr; i++){
            pOrderBy->a[i].u.x.iOrderByCol = 0;
          }
        }

        /* Take care here not to generate a TK_VECTOR containing only a
        ** single value. Since the parser never creates such a vector, some
        ** of the subroutines do not handle this case.  */
        if( pLhs->nExpr==1 ){
          pX->pLeft = pLhs->a[0].pExpr;
        }else{
          pLeft->x.pList = pLhs;
          aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
          testcase( aiMap==0 );
        }
        pSelect->pEList = pRhs;
        eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
        testcase( aiMap!=0 && aiMap[0]!=0 );
        pSelect->pEList = pOrigRhs;
        pLeft->x.pList = pOrigLhs;
        pX->pLeft = pLeft;
      }
      sqlite3ExprListDelete(pParse->db, pLhs);
      sqlite3ExprListDelete(pParse->db, pRhs);
    }

Changes to test/rowvalue9.test.
237
238
239
240
241
242
243
























































244
245
246
} {1}
do_execsql_test 5.2 {
  SELECT rowid FROM e2 WHERE rowid IN (SELECT +c FROM e1);
} {2}
do_execsql_test 5.3 {
  SELECT rowid FROM e2 WHERE rowid IN (SELECT 0+c FROM e1);
} {2}

























































finish_test








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



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
} {1}
do_execsql_test 5.2 {
  SELECT rowid FROM e2 WHERE rowid IN (SELECT +c FROM e1);
} {2}
do_execsql_test 5.3 {
  SELECT rowid FROM e2 WHERE rowid IN (SELECT 0+c FROM e1);
} {2}

#-------------------------------------------------------------------------
#
do_execsql_test 6.0 {
  CREATE TABLE f1(a, b);
  CREATE TABLE f2(c, d);
  CREATE TABLE f3(e, f);
}

do_execsql_test 6.1 {
  SELECT * FROM f3 WHERE (e, f) IN (
    SELECT a, b FROM f1 UNION ALL SELECT c, d FROM f2
  );
}
do_execsql_test 6.2 {
  CREATE INDEX f3e ON f3(e);
  SELECT * FROM f3 WHERE (e, f) IN (
    SELECT a, b FROM f1 UNION ALL SELECT c, d FROM f2
  );
}


#-------------------------------------------------------------------------
#
do_execsql_test 7.0 {
  CREATE TABLE g1(a, b); 
  INSERT INTO g1 VALUES
      (1, 1), (1, 2), (1, 3), (1, 'i'), (1, 'j'),
      (1, 6), (1, 7), (1, 8), (1, 9), (1, 10),
      (1, 4), (1, 5);

  CREATE TABLE g2(x, y);
  CREATE INDEX g2x ON g2(x);

  INSERT INTO g2 VALUES(1, 4);
  INSERT INTO g2 VALUES(1, 5);
}

do_execsql_test 7.1 {
  SELECT * FROM g2 WHERE (x, y) IN (
    SELECT a, b FROM g1 ORDER BY +a, +b LIMIT 10
  );
} { 1 4 1 5 }

do_execsql_test 7.2 {
  SELECT * FROM g2 WHERE (x, y) IN (
    SELECT a, b FROM g1 ORDER BY a, b LIMIT 10
  );
} { 1 4 1 5 }

do_execsql_test 7.3 {
  SELECT * FROM g2 WHERE (x, y) IN (
    SELECT a, b FROM g1 ORDER BY 1, 2 LIMIT 10
  );
} { 1 4 1 5 }


finish_test