SQLite

Check-in [ed14863d]
Login

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

Overview
Comment:Fix a problem with using the push-down optimization on compound SELECTs where component SELECTs use different collation sequences. dbsqlfuzz 11516f050100243e5a845f5a2b48a90ed2efaf2e. This problem appears to go all the way back to the beginning of the push-down optimization in check-in 6df18e949d367629 in SQLite 3.8.11.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ed14863dd72e35fa3a23320c3d5a8166515faea39a555c28a27b2d35e701eac4
User & Date: dan 2022-10-14 19:30:34
Original Comment: Fix a problem with using the push-down optimization on compound SELECTs where component SELECTs use different collation sequences. dbsqlfuzz 11516f050100243e5a845f5a2b48a90ed2efaf2e.
References
2022-10-26
21:14
Disable the push-down optimization for sub-queries that are INTERSECT, UNION or EXCEPT compounds. dbsqlfuzz a34f455c91ad75a0cf8cd9476841903f42930a7a. This corrects an issue that was introduce 12 days earlier by [ed14863dd72e35fa]. (check-in: 346a3b12 user: dan tags: trunk)
Context
2022-10-16
15:38
Add a top-level license and build-time version info header to generated sqlite3*.js. Correct a broken link in ext/wasm/index.html. (check-in: 0f1a06e8 user: stephan tags: trunk)
2022-10-15
15:39
Merge latest trunk changes. (check-in: 42255ead user: dan tags: recover-extension)
2022-10-14
19:56
Merge fixes from trunk. (check-in: 1cb65f36 user: drh tags: index-expr-opt)
19:30
Fix a problem with using the push-down optimization on compound SELECTs where component SELECTs use different collation sequences. dbsqlfuzz 11516f050100243e5a845f5a2b48a90ed2efaf2e. This problem appears to go all the way back to the beginning of the push-down optimization in check-in 6df18e949d367629 in SQLite 3.8.11. (check-in: ed14863d user: dan tags: trunk)
19:21
Add missing initializer from the extension loader. Fix for check-in [d6d449978245b4fa]. (check-in: 565d74c3 user: drh tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/select.c.

3746
3747
3748
3749
3750
3751
3752

3753
3754
3755
3756
3757
3758
3759
*/
typedef struct SubstContext {
  Parse *pParse;            /* The parsing context */
  int iTable;               /* Replace references to this table */
  int iNewTable;            /* New table number */
  int isOuterJoin;          /* Add TK_IF_NULL_ROW opcodes on each replacement */
  ExprList *pEList;         /* Replacement expressions */

} SubstContext;

/* Forward Declarations */
static void substExprList(SubstContext*, ExprList*);
static void substSelect(SubstContext*, Select*, int);

/*







>







3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
*/
typedef struct SubstContext {
  Parse *pParse;            /* The parsing context */
  int iTable;               /* Replace references to this table */
  int iNewTable;            /* New table number */
  int isOuterJoin;          /* Add TK_IF_NULL_ROW opcodes on each replacement */
  ExprList *pEList;         /* Replacement expressions */
  ExprList *pCList;         /* Collation sequences for replacement expr */
} SubstContext;

/* Forward Declarations */
static void substExprList(SubstContext*, ExprList*);
static void substSelect(SubstContext*, Select*, int);

/*
3787
3788
3789
3790
3791
3792
3793

3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
    if( pExpr->iColumn<0 ){
      pExpr->op = TK_NULL;
    }else
#endif
    {
      Expr *pNew;

      Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
      Expr ifNullRow;
      assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr );
      assert( pExpr->pRight==0 );
      if( sqlite3ExprIsVector(pCopy) ){
        sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
      }else{
        sqlite3 *db = pSubst->pParse->db;
        if( pSubst->isOuterJoin && pCopy->op!=TK_COLUMN ){
          memset(&ifNullRow, 0, sizeof(ifNullRow));







>
|

|







3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
    if( pExpr->iColumn<0 ){
      pExpr->op = TK_NULL;
    }else
#endif
    {
      Expr *pNew;
      int iColumn = pExpr->iColumn;
      Expr *pCopy = pSubst->pEList->a[iColumn].pExpr;
      Expr ifNullRow;
      assert( pSubst->pEList!=0 && iColumn<pSubst->pEList->nExpr );
      assert( pExpr->pRight==0 );
      if( sqlite3ExprIsVector(pCopy) ){
        sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
      }else{
        sqlite3 *db = pSubst->pParse->db;
        if( pSubst->isOuterJoin && pCopy->op!=TK_COLUMN ){
          memset(&ifNullRow, 0, sizeof(ifNullRow));
3827
3828
3829
3830
3831
3832
3833

3834
3835



3836
3837
3838

3839
3840
3841
3842
3843
3844
3845
          pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
          pExpr->op = TK_INTEGER;
          ExprSetProperty(pExpr, EP_IntValue);
        }

        /* Ensure that the expression now has an implicit collation sequence,
        ** just as it did when it was a column of a view or sub-query. */

        if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){
          CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr);



          pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, 
              (pColl ? pColl->zName : "BINARY")
          );

        }
        ExprClearProperty(pExpr, EP_Collate);
      }
    }
  }else{
    if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
      pExpr->iTable = pSubst->iNewTable;







>
|
|
>
>
>
|
|
|
>







3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
          pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
          pExpr->op = TK_INTEGER;
          ExprSetProperty(pExpr, EP_IntValue);
        }

        /* Ensure that the expression now has an implicit collation sequence,
        ** just as it did when it was a column of a view or sub-query. */
        {
          CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
          CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
                pSubst->pCList->a[iColumn].pExpr
          );
          if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
            pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
                (pColl ? pColl->zName : "BINARY")
            );
          }
        }
        ExprClearProperty(pExpr, EP_Collate);
      }
    }
  }else{
    if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
      pExpr->iTable = pSubst->iNewTable;
4023
4024
4025
4026
4027
4028
4029












4030
4031
4032
4033
4034
4035
4036
  memset(&w, 0, sizeof(w));
  w.u.aiCol = aCsrMap;
  w.xExprCallback = renumberCursorsCb;
  w.xSelectCallback = sqlite3SelectWalkNoop;
  sqlite3WalkSelect(&w, p);
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */













#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following







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







4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
  memset(&w, 0, sizeof(w));
  w.u.aiCol = aCsrMap;
  w.xExprCallback = renumberCursorsCb;
  w.xSelectCallback = sqlite3SelectWalkNoop;
  sqlite3WalkSelect(&w, p);
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */

/*
** If pSel is not part of a compound SELECT, return a pointer to its
** expression list. Otherwise, return a pointer to the expression list
** of the leftmost SELECT in the compound.
*/
static ExprList *findLeftmostExprlist(Select *pSel){
  while( pSel->pPrior ){
    pSel = pSel->pPrior;
  }
  return pSel->pEList;
}

#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
4576
4577
4578
4579
4580
4581
4582

4583
4584
4585
4586
4587
4588
4589
    if( db->mallocFailed==0 ){
      SubstContext x;
      x.pParse = pParse;
      x.iTable = iParent;
      x.iNewTable = iNewParent;
      x.isOuterJoin = isOuterJoin;
      x.pEList = pSub->pEList;

      substSelect(&x, pParent, 0);
    }
  
    /* The flattened query is a compound if either the inner or the
    ** outer query is a compound. */
    pParent->selFlags |= pSub->selFlags & SF_Compound;
    assert( (pSub->selFlags & SF_Distinct)==0 ); /* restriction (17b) */







>







4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
    if( db->mallocFailed==0 ){
      SubstContext x;
      x.pParse = pParse;
      x.iTable = iParent;
      x.iNewTable = iNewParent;
      x.isOuterJoin = isOuterJoin;
      x.pEList = pSub->pEList;
      x.pCList = findLeftmostExprlist(pSub);
      substSelect(&x, pParent, 0);
    }
  
    /* The flattened query is a compound if either the inner or the
    ** outer query is a compound. */
    pParent->selFlags |= pSub->selFlags & SF_Compound;
    assert( (pSub->selFlags & SF_Distinct)==0 ); /* restriction (17b) */
5058
5059
5060
5061
5062
5063
5064

5065
5066
5067
5068
5069
5070
5071
      pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
      unsetJoinExpr(pNew, -1, 1);
      x.pParse = pParse;
      x.iTable = pSrc->iCursor;
      x.iNewTable = pSrc->iCursor;
      x.isOuterJoin = 0;
      x.pEList = pSubq->pEList;

      pNew = substExpr(&x, pNew);
#ifndef SQLITE_OMIT_WINDOWFUNC
      if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
        /* Restriction 6c has prevented push-down in this case */
        sqlite3ExprDelete(pParse->db, pNew);
        nChng--;
        break;







>







5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
      pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
      unsetJoinExpr(pNew, -1, 1);
      x.pParse = pParse;
      x.iTable = pSrc->iCursor;
      x.iNewTable = pSrc->iCursor;
      x.isOuterJoin = 0;
      x.pEList = pSubq->pEList;
      x.pCList = findLeftmostExprlist(pSubq);
      pNew = substExpr(&x, pNew);
#ifndef SQLITE_OMIT_WINDOWFUNC
      if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
        /* Restriction 6c has prevented push-down in this case */
        sqlite3ExprDelete(pParse->db, pNew);
        nChng--;
        break;

Changes to test/collate5.test.

15
16
17
18
19
20
21


22
23
24
25
26
27
28

29
30
31
32
33
34
35
# GROUP BY clauses that use user-defined collation sequences.
#
# $Id: collate5.test,v 1.7 2008/09/16 11:58:20 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl




#
# Tests are organised as follows:
# collate5-1.* - DISTINCT
# collate5-2.* - Compound SELECT
# collate5-3.* - ORDER BY on compound SELECT
# collate5-4.* - GROUP BY


# Create the collation sequence 'TEXT', purely for asthetic reasons. The
# test cases in this script could just as easily use BINARY.
db collate TEXT [list string compare]

# Mimic the SQLite 2 collation type NUMERIC.
db collate numeric numeric_collate







>
>







>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# GROUP BY clauses that use user-defined collation sequences.
#
# $Id: collate5.test,v 1.7 2008/09/16 11:58:20 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

set testprefix collate5


#
# Tests are organised as follows:
# collate5-1.* - DISTINCT
# collate5-2.* - Compound SELECT
# collate5-3.* - ORDER BY on compound SELECT
# collate5-4.* - GROUP BY
# collate5-5.* - Collation sequence cases

# Create the collation sequence 'TEXT', purely for asthetic reasons. The
# test cases in this script could just as easily use BINARY.
db collate TEXT [list string compare]

# Mimic the SQLite 2 collation type NUMERIC.
db collate numeric numeric_collate
284
285
286
287
288
289
290
291

































292
  }
} {/[aA] 1(.0)? 2 [bB] 2 1 [bB] 3 1/}
do_test collate5-4.3 {
  execsql {
    DROP TABLE collate5t1;
  }
} {}


































finish_test








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

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  }
} {/[aA] 1(.0)? 2 [bB] 2 1 [bB] 3 1/}
do_test collate5-4.3 {
  execsql {
    DROP TABLE collate5t1;
  }
} {}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 5.0 {
  CREATE TABLE t1(a, b COLLATE nocase);
  CREATE TABLE t2(c, d);
  INSERT INTO t2 VALUES(1, 'bbb');
}
do_execsql_test 5.1 {
  SELECT * FROM (
      SELECT a, b FROM t1 UNION ALL SELECT c, d FROM t2
  ) WHERE b='BbB';
} {1 bbb}

reset_db
do_execsql_test 5.2 {
  CREATE TABLE t1(a,b,c COLLATE NOCASE);
  INSERT INTO t1 VALUES(NULL,'C','c');
  CREATE VIEW v2 AS
    SELECT a,b,c FROM t1 INTERSECT SELECT a,b,b FROM t1
    WHERE 'eT"3qRkL+oJMJjQ9z0'>=b
    ORDER BY a,b,c;
}

do_execsql_test 5.3 {
  SELECT * FROM v2;
} { {} C c }

do_execsql_test 5.4 {
  SELECT * FROM v2 WHERE c='c';
} { {} C c }


finish_test