/ Check-in [028475cb]
Login

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

Overview
Comment:Proposed fix for an infinite loop bug in the WITH clause semantic analysis logic.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | infinite-with-loop-bug
Files: files | file ages | folders
SHA1: 028475cb17a4b50baf0e9aba9bd3403d0a5d03b0
User & Date: drh 2015-11-07 17:48:21
Context
2015-11-07
17:51
Add test cases for WITH clauses. Closed-Leaf check-in: e7e65c75 user: dan tags: infinite-with-loop-bug
17:48
Proposed fix for an infinite loop bug in the WITH clause semantic analysis logic. check-in: 028475cb user: drh tags: infinite-with-loop-bug
15:19
Enhance TreeView to show WITH clauses. Add an assert to detect the infinite loop behavior when certain kinds of errors occur on a nested WITH clause. check-in: 2040d88e user: drh tags: infinite-with-loop-bug
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/select.c.

  3999   3999   ** onto the top of the stack. If argument bFree is true, then this
  4000   4000   ** WITH clause will never be popped from the stack. In this case it
  4001   4001   ** should be freed along with the Parse object. In other cases, when
  4002   4002   ** bFree==0, the With object will be freed along with the SELECT 
  4003   4003   ** statement with which it is associated.
  4004   4004   */
  4005   4005   void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
  4006         -  assert( bFree==0 || pParse->pWith==0 );
         4006  +  assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
  4007   4007     if( pWith ){
  4008   4008       assert( pParse->pWith!=pWith );
  4009   4009       pWith->pOuter = pParse->pWith;
  4010   4010       pParse->pWith = pWith;
  4011         -    pParse->bFreeWith = bFree;
         4011  +    if( bFree ) pParse->pWithToFree = pWith;
  4012   4012     }
  4013   4013   }
  4014   4014   
  4015   4015   /*
  4016   4016   ** This function checks if argument pFrom refers to a CTE declared by 
  4017   4017   ** a WITH clause on the stack currently maintained by the parser. And,
  4018   4018   ** if currently processing a CTE expression, if it is a recursive
................................................................................
  4097   4097       }
  4098   4098       assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
  4099   4099   
  4100   4100       pCte->zCteErr = "circular reference: %s";
  4101   4101       pSavedWith = pParse->pWith;
  4102   4102       pParse->pWith = pWith;
  4103   4103       sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
         4104  +    pParse->pWith = pWith;
  4104   4105   
  4105   4106       for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
  4106   4107       pEList = pLeft->pEList;
  4107   4108       if( pCte->pCols ){
  4108   4109         if( pEList && pEList->nExpr!=pCte->pCols->nExpr ){
  4109   4110           sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns",
  4110   4111               pCte->zName, pEList->nExpr, pCte->pCols->nExpr

Changes to src/sqliteInt.h.

  2750   2750     ** using offsetof(Parse,nVar) so the nVar field must be the first field
  2751   2751     ** in the recursive region.
  2752   2752     ************************************************************************/
  2753   2753   
  2754   2754     int nVar;                 /* Number of '?' variables seen in the SQL so far */
  2755   2755     int nzVar;                /* Number of available slots in azVar[] */
  2756   2756     u8 iPkSortOrder;          /* ASC or DESC for INTEGER PRIMARY KEY */
  2757         -  u8 bFreeWith;             /* True if pWith should be freed with parser */
  2758   2757     u8 explain;               /* True if the EXPLAIN flag is found on the query */
  2759   2758   #ifndef SQLITE_OMIT_VIRTUALTABLE
  2760   2759     u8 declareVtab;           /* True if inside sqlite3_declare_vtab() */
  2761   2760     int nVtabLock;            /* Number of virtual tables to lock */
  2762   2761   #endif
  2763   2762     int nAlias;               /* Number of aliased result set columns */
  2764   2763     int nHeight;              /* Expression tree height of current sub-select */
................................................................................
  2777   2776   #ifndef SQLITE_OMIT_VIRTUALTABLE
  2778   2777     Token sArg;               /* Complete text of a module argument */
  2779   2778     Table **apVtabLock;       /* Pointer to virtual tables needing locking */
  2780   2779   #endif
  2781   2780     Table *pZombieTab;        /* List of Table objects to delete after code gen */
  2782   2781     TriggerPrg *pTriggerPrg;  /* Linked list of coded triggers */
  2783   2782     With *pWith;              /* Current WITH clause, or NULL */
         2783  +  With *pWithToFree;        /* Free this WITH object at the end of the parse */
  2784   2784   };
  2785   2785   
  2786   2786   /*
  2787   2787   ** Return true if currently inside an sqlite3_declare_vtab() call.
  2788   2788   */
  2789   2789   #ifdef SQLITE_OMIT_VIRTUALTABLE
  2790   2790     #define IN_DECLARE_VTAB 0

Changes to src/tokenize.c.

   506    506       /* If the pParse->declareVtab flag is set, do not delete any table 
   507    507       ** structure built up in pParse->pNewTable. The calling code (see vtab.c)
   508    508       ** will take responsibility for freeing the Table structure.
   509    509       */
   510    510       sqlite3DeleteTable(db, pParse->pNewTable);
   511    511     }
   512    512   
   513         -  if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith);
          513  +  sqlite3WithDelete(db, pParse->pWithToFree);
   514    514     sqlite3DeleteTrigger(db, pParse->pNewTrigger);
   515    515     for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
   516    516     sqlite3DbFree(db, pParse->azVar);
   517    517     while( pParse->pAinc ){
   518    518       AutoincInfo *p = pParse->pAinc;
   519    519       pParse->pAinc = p->pNext;
   520    520       sqlite3DbFree(db, p);

Added test/with3.test.

            1  +# 2015-11-07
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing the WITH clause.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set ::testprefix with3
           18  +
           19  +ifcapable {!cte} {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +# Test problems found by Kostya Serebryany using 
           25  +# LibFuzzer.  (http://llvm.org/docs/LibFuzzer.html)
           26  +#
           27  +do_catchsql_test 1.0 {
           28  +  WITH i(x) AS (
           29  +    WITH j AS (SELECT 10)
           30  +    SELECT 5 FROM t0 UNION SELECT 8 FROM m
           31  +  )
           32  +  SELECT * FROM i;
           33  +} {1 {no such table: m}}
           34  +
           35  +# Additional test cases that came out of the work to
           36  +# fix for Kostya's problem.
           37  +#
           38  +do_execsql_test 2.0 {
           39  + WITH
           40  +  x1 AS (SELECT 10),
           41  +  x2 AS (SELECT 11),
           42  +  x3 AS (
           43  +    SELECT * FROM x1 UNION ALL SELECT * FROM x2
           44  +  ),
           45  +  x4 AS (
           46  +    WITH
           47  +    x1 AS (SELECT 12),
           48  +    x2 AS (SELECT 13)
           49  +    SELECT * FROM x3
           50  +  )
           51  +  SELECT * FROM x4;
           52  +
           53  +} {10 11}
           54  +
           55  +do_execsql_test 2.1 {
           56  +  CREATE TABLE t1(x);
           57  +  WITH
           58  +    x1(a) AS (values(100))
           59  +  INSERT INTO t1(x)
           60  +    SELECT * FROM (WITH x2(y) AS (SELECT * FROM x1) SELECT y+a FROM x1, x2);
           61  +  SELECT * FROM t1;
           62  +} {200}
           63  +
           64  +finish_test