]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for common table expressions (WITH clauses).
authordan <dan@noemail.net>
Fri, 17 Jan 2014 15:15:10 +0000 (15:15 +0000)
committerdan <dan@noemail.net>
Fri, 17 Jan 2014 15:15:10 +0000 (15:15 +0000)
FossilOrigin-Name: 0171e3bb4f663a9414b0e8b64c87b5d0683855b5

1  2 
manifest
manifest.uuid
src/select.c
src/where.c

diff --cc manifest
index 1aa2634f3ede52f6d5591fe07c59c362ac7a121b,586f354eacd15a7763f096eb2b18f99b92e6377e..61c516e9ddf454445b4879674525e540d20c8bce
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Always\suse\savailable\sindices\sto\soptimize\sLIKE\soperators\seven\sif\sthe\spattern\nof\sthe\sLIKE\soperator\shas\sa\sCOLLATE\smodifier.\s\sThis\sfixes\san\sineffiency\sthat\nwas\sintroduced\sinto\s3.7.15\sby\scheck-in\s[8542e6180d4]\son\s2012-12-08.
- D 2014-01-16T15:31:41.806
 -C Fix\ssome\sproblems\sto\sdo\swith\sWITH\sclauses\sand\sname\sresolution.
 -D 2014-01-17T14:59:27.898
++C Add\ssupport\sfor\scommon\stable\sexpressions\s(WITH\sclauses).
++D 2014-01-17T15:15:10.270
  F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
  F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
  F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@@ -219,12 -219,12 +219,12 @@@ F src/printf.c 85d07756e45d7496d19439dc
  F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
  F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6
  F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
- F src/select.c 996d8b88603edbd478aaa70b75d535a3ddea933d
 -F src/select.c c77955f93121adc8b4b43a98add62fbaa2b48132
++F src/select.c 231079b8b07ea6f3cf4663e9c0ef2315abe2236c
  F src/shell.c 9f3bc02a658b8f61d2cbe60cfc482f660c1c6c48
- F src/sqlite.h.in d94a8b89522f526ba711182ee161e06f8669bcc9
+ F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
  F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
  F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
- F src/sqliteInt.h 0c65967bb807dee3c9eef2bbd17f880eeb28ea30
+ F src/sqliteInt.h 9600eeb486c274fbdb815d040e4a7f262b7317e1
  F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
  F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
  F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@@ -292,8 -292,8 +292,8 @@@ F src/vdbetrace.c 6f52bc0c51e144b7efdcf
  F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
  F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4
  F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
- F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74
- F src/where.c 81cec50fe73633144b0730de477e141c53485862
+ F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
 -F src/where.c 369b0259fabfb22644d197736ae622f762cbaba8
++F src/where.c 5e11de480a94e6ff8f9922e3a04a31b56d5f33b5
  F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358
  F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
  F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@@ -1148,7 -1151,7 +1151,7 @@@ F tool/vdbe-compress.tcl 0cf56e9263a152
  F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
  F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
  F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
- P f61a70589ac7e05008a362bd9d5b7bde5d07a758
- R c3393c71f3ebd7292d430bc7115b905d
- U drh
- Z 57df34b30b12b159eb99c0c8be54bec6
 -P f68c6c4d36481526a9348244adc571ea282dc9eb
 -R d73fd639096ae3d6dc6dd6a7018ffc11
++P 16bd54783a3f5531c55564ddefdada657c078eb0 6a549187ed8b5ed50daefa676ff666ae2ed43346
++R 3149ee3ed72aa52234b32eaa7e696309
+ U dan
 -Z 6339477b00dd8c67441747d0ac03cfea
++Z e17f48449fd6868168f4a2f61958f3af
diff --cc manifest.uuid
index b7340407494db75cfa001407c8bf7050fba13fb1,71dd74abb9b1ec799ad9ec01e609fd174d41fc04..5d482e8fa7912be2fee912e26cecf2e5676166d8
@@@ -1,1 -1,1 +1,1 @@@
- 16bd54783a3f5531c55564ddefdada657c078eb0
 -6a549187ed8b5ed50daefa676ff666ae2ed43346
++0171e3bb4f663a9414b0e8b64c87b5d0683855b5
diff --cc src/select.c
index 58458974091e0704e73b35746f1478ef57a038d5,d9625cba43cfa2d1d764039a911044d9e1495da4..b3a6daf170dca261122f52d343039a9493d05fcc
@@@ -3394,6 -3500,175 +3500,183 @@@ static int convertCompoundSelectToSubqu
    return WRC_Continue;
  }
  
+ #ifndef SQLITE_OMIT_CTE
+ /*
+ ** Argument pWith (which may be NULL) points to a linked list of nested 
+ ** WITH contexts, from inner to outermost. If the table identified by 
+ ** FROM clause element pItem is really a common-table-expression (CTE) 
+ ** then return a pointer to the CTE definition for that table. Otherwise
+ ** return NULL.
+ */
+ static struct Cte *searchWith(With *pWith, struct SrcList_item *pItem){
+   const char *zName;
+   if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){
+     With *p;
+     for(p=pWith; p; p=p->pOuter){
+       int i;
+       for(i=0; i<p->nCte; i++){
+         if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
+           return &p->a[i];
+         }
+       }
+     }
+   }
+   return 0;
+ }
+ /* The code generator maintains a stack of active WITH clauses
+ ** with the inner-most WITH clause being at the top of the stack.
+ **
+ ** This routine pushes the WITH clause passed as the second argument
+ ** onto the top of the stack. If argument bFree is true, then this
+ ** WITH clause will never be popped from the stack. In this case it
+ ** should be freed along with the Parse object. In other cases, when
+ ** bFree==0, the With object will be freed along with the SELECT 
+ ** statement with which it is associated.
+ */
+ void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
+   assert( bFree==0 || pParse->pWith==0 );
+   if( pWith ){
+     pWith->pOuter = pParse->pWith;
+     pParse->pWith = pWith;
+     pParse->bFreeWith = bFree;
+   }
+ }
+ /*
+ ** This function checks if argument pFrom refers to a CTE declared by 
+ ** a WITH clause on the stack currently maintained by the parser. And,
+ ** if currently processing a CTE expression, if it is a recursive
+ ** reference to the current CTE.
+ **
+ ** If pFrom falls into either of the two categories above, pFrom->pTab
+ ** and other fields are populated accordingly. The caller should check
+ ** (pFrom->pTab!=0) to determine whether or not a successful match
+ ** was found.
+ **
+ ** Whether or not a match is found, SQLITE_OK is returned if no error
+ ** occurs. If an error does occur, an error message is stored in the
+ ** parser and some error code other than SQLITE_OK returned.
+ */
+ static int withExpand(
+   Walker *pWalker, 
+   struct SrcList_item *pFrom
+ ){
+   Table *pTab;
+   Parse *pParse = pWalker->pParse;
+   sqlite3 *db = pParse->db;
+   struct Cte *pCte;
+   assert( pFrom->pTab==0 );
+   pCte = searchWith(pParse->pWith, pFrom);
+   if( pCte ){
+     ExprList *pEList;
+     Select *pSel;
+     Select *pLeft;                /* Left-most SELECT statement */
+     int bMayRecursive;            /* True if compound joined by UNION [ALL] */
+     /* If pCte->zErr is non-NULL at this point, then this is an illegal
+     ** recursive reference to CTE pCte. Leave an error in pParse and return
+     ** early. If pCte->zErr is NULL, then this is not a recursive reference.
+     ** In this case, proceed.  */
+     if( pCte->zErr ){
+       sqlite3ErrorMsg(pParse, pCte->zErr, pCte->zName);
+       return WRC_Abort;
+     }
+     pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
+     if( pTab==0 ) return WRC_Abort;
+     pTab->nRef = 1;
+     pTab->zName = sqlite3DbStrDup(db, pCte->zName);
+     pTab->iPKey = -1;
+     pTab->nRowEst = 1048576;
+     pTab->tabFlags |= TF_Ephemeral;
+     pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
+     if( db->mallocFailed ) return SQLITE_NOMEM;
+     assert( pFrom->pSelect );
+     /* Check if this is a recursive CTE. */
+     pSel = pFrom->pSelect;
+     bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
+     if( bMayRecursive ){
+       int i;
+       SrcList *pSrc = pFrom->pSelect->pSrc;
+       for(i=0; i<pSrc->nSrc; i++){
+         struct SrcList_item *pItem = &pSrc->a[i];
+         if( pItem->zDatabase==0 
+          && pItem->zName!=0 
+          && 0==sqlite3StrICmp(pItem->zName, pCte->zName)
+           ){
+           pItem->pTab = pTab;
+           pItem->isRecursive = 1;
+           pTab->nRef++;
+           pSel->selFlags |= SF_Recursive;
+         }
+       }
+     }
+     /* Only one recursive reference is permitted. */ 
+     if( pTab->nRef>2 ){
+       sqlite3ErrorMsg(
+           pParse, "multiple references to recursive table: %s", pCte->zName
+       );
+       return WRC_Abort;
+     }
+     assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
+     pCte->zErr = "circular reference: %s";
+     sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
+     for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
+     pEList = pLeft->pEList;
+     if( pCte->pCols ){
+       if( pEList->nExpr!=pCte->pCols->nExpr ){
+         sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns",
+             pCte->zName, pEList->nExpr, pCte->pCols->nExpr
+         );
+         return WRC_Abort;
+       }
+       pEList = pCte->pCols;
+     }
+     selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
+     if( bMayRecursive ){
+       if( pSel->selFlags & SF_Recursive ){
+         pCte->zErr = "multiple recursive references: %s";
+       }else{
+         pCte->zErr = "recursive reference in a subquery: %s";
+       }
+       sqlite3WalkSelect(pWalker, pSel);
+     }
+     pCte->zErr = 0;
+   }
+   return SQLITE_OK;
+ }
+ #endif
+ #ifndef SQLITE_OMIT_CTE
++/*
++** If the SELECT passed as the second argument has an associated WITH 
++** clause, pop it from the stack stored as part of the Parse object.
++**
++** This function is used as the xSelectCallback2() callback by
++** sqlite3SelectExpand() when walking a SELECT tree to resolve table
++** names and other FROM clause elements. 
++*/
+ static void selectPopWith(Walker *pWalker, Select *p){
+   Parse *pParse = pWalker->pParse;
+   if( p->pWith ){
+     assert( pParse->pWith==p->pWith );
+     pParse->pWith = p->pWith->pOuter;
+   }
+   return WRC_Continue;
+ }
+ #else
+ #define selectPopWith 0
+ #endif
  /*
  ** This routine is a Walker callback for "expanding" a SELECT statement.
  ** "Expanding" means to do the following:
diff --cc src/where.c
Simple merge