SQLite

Check-in [1ad41840c5]
Login

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

Overview
Comment:Add restriction (9) to the push-down optimization: If the subquery is a compound then all arms of the compound must have the same affinity. dbsqlfuzz 3a548de406a50e896c1bf7142692d35d339d697f.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 1ad41840c5e0fa702ba2c0df77a3ea126bd695b910b5d1271fa3129c38c58f5f
User & Date: drh 2022-11-25 15:52:00.241
References
2023-02-22
20:42
Remove the push-down optimization restriction that was added by check-in [1ad41840c5e0fa70] because it is no longer needed after the enhancements to compound query processing in check-in [27655c9353620aa5] This resolves the performance regression reported by forum post bcc4375032. (check-in: aa6bd6dff7 user: drh tags: trunk)
Context
2022-11-25
16:10
Improve the query planner such that it is able to make use of indexed expressions within an aggregate query with GROUP BY. This implements enhancement request [99378177930f87bd]. (check-in: b9190d3da7 user: drh tags: trunk)
15:52
Add restriction (9) to the push-down optimization: If the subquery is a compound then all arms of the compound must have the same affinity. dbsqlfuzz 3a548de406a50e896c1bf7142692d35d339d697f. (check-in: 1ad41840c5 user: drh tags: trunk)
2022-11-24
17:58
Fix a test case in fts3expr4.test to account for different locales. (check-in: a2b6883ac2 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/select.c.
4045
4046
4047
4048
4049
4050
4051




























4052
4053
4054
4055
4056
4057
4058
*/
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







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







4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
*/
static ExprList *findLeftmostExprlist(Select *pSel){
  while( pSel->pPrior ){
    pSel = pSel->pPrior;
  }
  return pSel->pEList;
}

/*
** Return true if any of the result-set columns in the compound query
** have incompatible affinities on one or more arms of the compound.
*/
static int compoundHasDifferentAffinities(Select *p){
  int ii;
  ExprList *pList;
  assert( p!=0 );
  assert( p->pEList!=0 );
  assert( p->pPrior!=0 );
  pList = p->pEList;
  for(ii=0; ii<pList->nExpr; ii++){
    char aff;
    Select *pSub1;
    assert( pList->a[ii].pExpr!=0 );
    aff = sqlite3ExprAffinity(pList->a[ii].pExpr);
    for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){
      assert( pSub1->pEList!=0 );
      assert( pSub1->pEList->nExpr>ii );
      assert( pSub1->pEList->a[ii].pExpr!=0 );
      if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
        return 1;
      }
    }
  }
  return 0;
}

#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
4149
4150
4151
4152
4153
4154
4155
4156

4157
4158
4159
4160
4161
4162
4163
**              (17d2) DISTINCT
**        (17e) the subquery may not contain window functions, and
**        (17f) the subquery must not be the RHS of a LEFT JOIN.
**        (17g) either the subquery is the first element of the outer
**              query or there are no RIGHT or FULL JOINs in any arm
**              of the subquery.  (This is a duplicate of condition (27b).)
**        (17h) The corresponding result set expressions in all arms of the
**              compound must have the same affinity.

**
**        The parent and sub-query may contain WHERE clauses. Subject to
**        rules (11), (13) and (14), they may also contain ORDER BY,
**        LIMIT and OFFSET clauses.  The subquery cannot use any compound
**        operator other than UNION ALL because all the other compound
**        operators have an implied DISTINCT which is disallowed by
**        restriction (4).







|
>







4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
**              (17d2) DISTINCT
**        (17e) the subquery may not contain window functions, and
**        (17f) the subquery must not be the RHS of a LEFT JOIN.
**        (17g) either the subquery is the first element of the outer
**              query or there are no RIGHT or FULL JOINs in any arm
**              of the subquery.  (This is a duplicate of condition (27b).)
**        (17h) The corresponding result set expressions in all arms of the
**              compound must have the same affinity. (See restriction (9)
**              on the push-down optimization.)
**
**        The parent and sub-query may contain WHERE clauses. Subject to
**        rules (11), (13) and (14), they may also contain ORDER BY,
**        LIMIT and OFFSET clauses.  The subquery cannot use any compound
**        operator other than UNION ALL because all the other compound
**        operators have an implied DISTINCT which is disallowed by
**        restriction (4).
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
      }
    }

    /* Restriction (23) */
    if( (p->selFlags & SF_Recursive) ) return 0;

    /* Restriction (17h) */
    for(ii=0; ii<pSub->pEList->nExpr; ii++){
      char aff;
      assert( pSub->pEList->a[ii].pExpr!=0 );
      aff = sqlite3ExprAffinity(pSub->pEList->a[ii].pExpr);
      for(pSub1=pSub->pPrior; pSub1; pSub1=pSub1->pPrior){
        assert( pSub1->pEList!=0 );
        assert( pSub1->pEList->nExpr>ii );
        assert( pSub1->pEList->a[ii].pExpr!=0 );
        if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
          return 0;
        }
      }
    }

    if( pSrc->nSrc>1 ){
      if( pParse->nSelect>500 ) return 0;
      if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0;
      aCsrMap = sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int));
      if( aCsrMap ) aCsrMap[0] = pParse->nTab;
    }







<
<
<
<
<
<
<
<
<
|
<
<
<







4397
4398
4399
4400
4401
4402
4403









4404



4405
4406
4407
4408
4409
4410
4411
      }
    }

    /* Restriction (23) */
    if( (p->selFlags & SF_Recursive) ) return 0;

    /* Restriction (17h) */









    if( compoundHasDifferentAffinities(pSub) ) return 0;




    if( pSrc->nSrc>1 ){
      if( pParse->nSelect>500 ) return 0;
      if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0;
      aCsrMap = sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int));
      if( aCsrMap ) aCsrMap[0] = pParse->nTab;
    }
5031
5032
5033
5034
5035
5036
5037





5038
5039
5040
5041
5042
5043
5044
**
**   (8) The subquery may not be a compound that uses UNION, INTERSECT,
**       or EXCEPT.  (We could, perhaps, relax this restriction to allow
**       this case if none of the comparisons operators between left and
**       right arms of the compound use a collation other than BINARY.
**       But it is a lot of work to check that case for an obscure and
**       minor optimization, so we omit it for now.)





**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
static int pushDownWhereTerms(
  Parse *pParse,        /* Parse context (for malloc() and error reporting) */
  Select *pSubq,        /* The subquery whose WHERE clause is to be augmented */







>
>
>
>
>







5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
**
**   (8) The subquery may not be a compound that uses UNION, INTERSECT,
**       or EXCEPT.  (We could, perhaps, relax this restriction to allow
**       this case if none of the comparisons operators between left and
**       right arms of the compound use a collation other than BINARY.
**       But it is a lot of work to check that case for an obscure and
**       minor optimization, so we omit it for now.)
**
**   (9) If the subquery is a compound, then all arms of the compound must
**       have the same affinity.  (This is the same as restriction (17h)
**       for query flattening.)
**       
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
static int pushDownWhereTerms(
  Parse *pParse,        /* Parse context (for malloc() and error reporting) */
  Select *pSubq,        /* The subquery whose WHERE clause is to be augmented */
5056
5057
5058
5059
5060
5061
5062



5063
5064
5065
5066
5067
5068
5069
    Select *pSel;
    for(pSel=pSubq; pSel; pSel=pSel->pPrior){
      u8 op = pSel->op;
      assert( op==TK_ALL || op==TK_SELECT 
           || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
      if( op!=TK_ALL && op!=TK_SELECT ) return 0;  /* restriction (8) */
      if( pSel->pWin ) return 0;    /* restriction (6b) */



    }
  }else{
    if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
  }
#endif

#ifdef SQLITE_DEBUG







>
>
>







5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
    Select *pSel;
    for(pSel=pSubq; pSel; pSel=pSel->pPrior){
      u8 op = pSel->op;
      assert( op==TK_ALL || op==TK_SELECT 
           || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
      if( op!=TK_ALL && op!=TK_SELECT ) return 0;  /* restriction (8) */
      if( pSel->pWin ) return 0;    /* restriction (6b) */
    }
    if( compoundHasDifferentAffinities(pSubq) ){
      return 0;  /* restriction (9) */
    }
  }else{
    if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
  }
#endif

#ifdef SQLITE_DEBUG
Changes to test/pushdown.test.
82
83
84
85
86
87
88



89






















90
91
    SELECT * FROM u1 WHERE 123=(
      SELECT x FROM u2 WHERE x=a AND f('two')
    ) AND f('three')=123
  }
  set L
} {three}



























  
finish_test







>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    SELECT * FROM u1 WHERE 123=(
      SELECT x FROM u2 WHERE x=a AND f('two')
    ) AND f('three')=123
  }
  set L
} {three}

# 2022-11-25 dbsqlfuzz crash-3a548de406a50e896c1bf7142692d35d339d697f
# Disable the push-down optimization for compound subqueries if any
# arm of the compound has an incompatible affinity.
#
reset_db
do_execsql_test 3.1 {
  CREATE TABLE t0(c0 INT);
  INSERT INTO t0 VALUES(0);
  CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
  INSERT INTO t1_a VALUES(1,'one'); --,(4,'four');
  CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
  INSERT INTO t1_b VALUES(2,'two'); --,(5,'five');
  CREATE VIEW v0 AS SELECT CAST(t0.c0 AS INTEGER) AS c0 FROM t0;
  CREATE VIEW t1 AS SELECT a, b FROM t1_a UNION ALL SELECT c, 0 FROM t1_b;
  SELECT t1.a, quote(t1.b), t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1;
} {
  1 'one' 0
  2 '0'   0
}
do_execsql_test 3.2 {
  SELECT a, quote(b), cd FROM (
    SELECT t1.a, t1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1
  ) WHERE a=2 AND b='0' AND cd=0;
} {
  2 '0'   0
}
  
finish_test