From: dan Date: Fri, 17 Jan 2014 15:15:10 +0000 (+0000) Subject: Add support for common table expressions (WITH clauses). X-Git-Tag: version-3.8.3~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=718569443d81360b140e8be62e7c352f9ca6576c;p=thirdparty%2Fsqlite.git Add support for common table expressions (WITH clauses). FossilOrigin-Name: 0171e3bb4f663a9414b0e8b64c87b5d0683855b5 --- 718569443d81360b140e8be62e7c352f9ca6576c diff --cc manifest index 1aa2634f3e,586f354eac..61c516e9dd --- a/manifest +++ 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 b734040749,71dd74abb9..5d482e8fa7 --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 16bd54783a3f5531c55564ddefdada657c078eb0 -6a549187ed8b5ed50daefa676ff666ae2ed43346 ++0171e3bb4f663a9414b0e8b64c87b5d0683855b5 diff --cc src/select.c index 5845897409,d9625cba43..b3a6daf170 --- a/src/select.c +++ b/src/select.c @@@ -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; inCte; 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; inSrc; 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: