SQLite

Check-in [8d631a6b9e]
Login

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

Overview
Comment:Honor the MATERIALIZED keyword on a common table expression by not flattening the CTE into an outer query.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8d631a6b9eb06b54b10eda595a4c588ca38f909b4afa653b24d1f720b55d72f7
User & Date: drh 2022-04-22 16:15:48.995
Context
2022-04-22
17:36
Improve EXPLAIN QUERY PLAN output and comments on bytecode listings by distinguishing between "subquery" and "join" and using consistent names across EQP and bytecode. (check-in: a2d3ee9242 user: drh tags: trunk)
16:15
Honor the MATERIALIZED keyword on a common table expression by not flattening the CTE into an outer query. (check-in: 8d631a6b9e user: drh tags: trunk)
15:18
Add test cases to check the handling of SQLITE_BUSY in rbu. (check-in: 6fccc733c6 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/expr.c.
1523
1524
1525
1526
1527
1528
1529

1530
1531
1532
1533
1534
1535
1536
    if( pRet ){
      int i;
      pRet->nCte = p->nCte;
      for(i=0; i<p->nCte; i++){
        pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
        pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
        pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName);

      }
    }
  }
  return pRet;
}
#else
# define sqlite3WithDup(x,y) 0







>







1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
    if( pRet ){
      int i;
      pRet->nCte = p->nCte;
      for(i=0; i<p->nCte; i++){
        pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
        pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
        pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName);
        pRet->a[i].eM10d = p->a[i].eM10d;
      }
    }
  }
  return pRet;
}
#else
# define sqlite3WithDup(x,y) 0
Changes to src/select.c.
4149
4150
4151
4152
4153
4154
4155


4156
4157
4158
4159
4160
4161
4162
**
**  (26)  The subquery may not be the right operand of a RIGHT JOIN.
**        See also (3) for restrictions on LEFT JOIN.
**
**  (27)  The subquery may not contain a FULL or RIGHT JOIN unless it
**        is the first element of the parent query.
**


**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom].  isAgg is true if the outer query
** uses aggregates.
**
** If flattening is not attempted, this routine is a no-op and returns 0.
** If flattening is attempted this routine returns 1.







>
>







4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
**
**  (26)  The subquery may not be the right operand of a RIGHT JOIN.
**        See also (3) for restrictions on LEFT JOIN.
**
**  (27)  The subquery may not contain a FULL or RIGHT JOIN unless it
**        is the first element of the parent query.
**
**  (28)  The subquery is not a MATERIALIZED CTE.
**
**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom].  isAgg is true if the outer query
** uses aggregates.
**
** If flattening is not attempted, this routine is a no-op and returns 0.
** If flattening is attempted this routine returns 1.
4271
4272
4273
4274
4275
4276
4277



4278
4279
4280
4281
4282
4283
4284
    isOuterJoin = -1;
  }
#endif

  assert( pSubSrc->nSrc>0 );  /* True by restriction (7) */
  if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
    return 0;   /* Restriction (27) */



  }

  /* Restriction (17): If the sub-query is a compound SELECT, then it must
  ** use only the UNION ALL operator. And none of the simple select queries
  ** that make up the compound SELECT are allowed to be aggregate or distinct
  ** queries.
  */







>
>
>







4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
    isOuterJoin = -1;
  }
#endif

  assert( pSubSrc->nSrc>0 );  /* True by restriction (7) */
  if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
    return 0;   /* Restriction (27) */
  }
  if( pSubitem->fg.isCte && pSubitem->u2.pCteUse->eM10d==M10d_Yes ){
    return 0;       /* (28) */
  }

  /* Restriction (17): If the sub-query is a compound SELECT, then it must
  ** use only the UNION ALL operator. And none of the simple select queries
  ** that make up the compound SELECT are allowed to be aggregate or distinct
  ** queries.
  */
Changes to src/treeview.c.
111
112
113
114
115
116
117




118
119
120
121
122
123
124
        int j;
        for(j=0; j<pCte->pCols->nExpr; j++){
          sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zEName);
          cSep = ',';
        }
        sqlite3_str_appendf(&x, ")");
      }




      if( pCte->pUse ){
        sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse,
                 pCte->pUse->nUse);
      }
      sqlite3StrAccumFinish(&x);
      sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
      sqlite3TreeViewSelect(pView, pCte->pSelect, 0);







>
>
>
>







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
        int j;
        for(j=0; j<pCte->pCols->nExpr; j++){
          sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zEName);
          cSep = ',';
        }
        sqlite3_str_appendf(&x, ")");
      }
      if( pCte->eM10d!=M10d_Any ){
        sqlite3_str_appendf(&x, " %sMATERIALIZED", 
           pCte->eM10d==M10d_No ? "NOT " : "");
      }
      if( pCte->pUse ){
        sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse,
                 pCte->pUse->nUse);
      }
      sqlite3StrAccumFinish(&x);
      sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
      sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
Changes to test/with1.test.
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
             SELECT * FROM Table0  NATURAL JOIN  Table0));
} {{}}

# 2019-01-17
# Make sure crazy nexted CTE joins terminate with an error quickly.
#
do_catchsql_test 22.1 {
  WITH RECURSIVE c AS (
     WITH RECURSIVE c AS (
        WITH RECURSIVE c AS (
           WITH RECURSIVE c AS (
               WITH  c AS (VALUES(0))
               SELECT 1 FROM c LEFT JOIN c ON ltrim(1)
           )
           SELECT 1 FROM c,c,c,c,c,c,c,c,c
        )
        SELECT  2 FROM c,c,c,c,c,c,c,c,c
     )







|
|
|
|







1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
             SELECT * FROM Table0  NATURAL JOIN  Table0));
} {{}}

# 2019-01-17
# Make sure crazy nexted CTE joins terminate with an error quickly.
#
do_catchsql_test 22.1 {
  WITH RECURSIVE c AS NOT MATERIALIZED (
     WITH RECURSIVE c AS NOT MATERIALIZED (
        WITH RECURSIVE c AS NOT MATERIALIZED (
           WITH RECURSIVE c AS NOT MATERIALIZED (
               WITH  c AS (VALUES(0))
               SELECT 1 FROM c LEFT JOIN c ON ltrim(1)
           )
           SELECT 1 FROM c,c,c,c,c,c,c,c,c
        )
        SELECT  2 FROM c,c,c,c,c,c,c,c,c
     )
Changes to test/with6.test.
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
  SELECT y FROM t2 ORDER BY y;
} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
do_eqp_test 211 {
  SELECT y FROM t2 ORDER BY y;
} {
  QUERY PLAN
  |--MATERIALIZE c1
  |  |--MATERIALIZE c
  |  |  `--SCAN 3 CONSTANT ROWS
  |  `--SCAN c
  |--MATERIALIZE c2


  |  `--SCAN c
  |--SCAN c1
  |--SCAN c2
  |--SCAN t1
  `--USE TEMP B-TREE FOR ORDER BY
}
do_execsql_test 220 {
  DROP VIEW t2;
  CREATE VIEW t2(y) AS
    WITH c(z) AS MATERIALIZED (VALUES(4),(5),(6))
    SELECT c1.z+c2.z*100+t1.x*10000
      FROM t1,
           (SELECT z FROM c LIMIT 5) AS c1,
           (SELECT z FROM c LIMIT 5) AS c2;
  SELECT y FROM t2 ORDER BY y;
} {40404 40405 40406 40504 40505 40506 40604 40605 40606}




































































finish_test







|



>
>

















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

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
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
  SELECT y FROM t2 ORDER BY y;
} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
do_eqp_test 211 {
  SELECT y FROM t2 ORDER BY y;
} {
  QUERY PLAN
  |--MATERIALIZE c1
  |  |--CO-ROUTINE c
  |  |  `--SCAN 3 CONSTANT ROWS
  |  `--SCAN c
  |--MATERIALIZE c2
  |  |--CO-ROUTINE c
  |  |  `--SCAN 3 CONSTANT ROWS
  |  `--SCAN c
  |--SCAN c1
  |--SCAN c2
  |--SCAN t1
  `--USE TEMP B-TREE FOR ORDER BY
}
do_execsql_test 220 {
  DROP VIEW t2;
  CREATE VIEW t2(y) AS
    WITH c(z) AS MATERIALIZED (VALUES(4),(5),(6))
    SELECT c1.z+c2.z*100+t1.x*10000
      FROM t1,
           (SELECT z FROM c LIMIT 5) AS c1,
           (SELECT z FROM c LIMIT 5) AS c2;
  SELECT y FROM t2 ORDER BY y;
} {40404 40405 40406 40504 40505 40506 40604 40605 40606}

# 2022-04-22: Do not allow flattening of a MATERIALIZED CTE into
# an outer query.
#
reset_db
db null -
do_execsql_test 300 {
  CREATE TABLE t2(a INT,b INT,d INT); INSERT INTO t2 VALUES(4,5,6),(7,8,9);
  CREATE TABLE t3(a INT,b INT,e INT); INSERT INTO t3 VALUES(3,3,3),(8,8,8);
} {}
do_execsql_test 310 {
  WITH t23 AS MATERIALIZED (SELECT * FROM t2 FULL JOIN t3 USING(b))
  SELECT * FROM t23;
} {
  4 5 6 - -
  7 8 9 8 8
  - 3 - 3 3
}
do_eqp_test 311 {
  WITH t23 AS MATERIALIZED (SELECT * FROM t2 FULL JOIN t3 USING(b))
  SELECT * FROM t23;
} {
  QUERY PLAN
  |--MATERIALIZE t23
  |  |--SCAN t2
  |  |--SCAN t3 LEFT-JOIN
  |  `--RIGHT-JOIN t3
  |     `--SCAN t3
  `--SCAN t23
}
do_execsql_test 320 {
  WITH t23 AS NOT MATERIALIZED (SELECT * FROM t2 FULL JOIN t3 USING(b))
  SELECT * FROM t23;
} {
  4 5 6 - -
  7 8 9 8 8
  - 3 - 3 3
}
do_eqp_test 321 {
  WITH t23 AS NOT MATERIALIZED (SELECT * FROM t2 FULL JOIN t3 USING(b))
  SELECT * FROM t23;
} {
  QUERY PLAN
  |--SCAN t2
  |--SCAN t3 LEFT-JOIN
  `--RIGHT-JOIN t3
     `--SCAN t3
}
do_execsql_test 330 {
  WITH t23 AS (SELECT * FROM t2 FULL JOIN t3 USING(b))
  SELECT * FROM t23;
} {
  4 5 6 - -
  7 8 9 8 8
  - 3 - 3 3
}
do_eqp_test 331 {
  WITH t23 AS (SELECT * FROM t2 FULL JOIN t3 USING(b))
  SELECT * FROM t23;
} {
  QUERY PLAN
  |--SCAN t2
  |--SCAN t3 LEFT-JOIN
  `--RIGHT-JOIN t3
     `--SCAN t3
}


finish_test