mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
Allow multiple recursive terms in the compound SELECT of a recursive CTE.
This facilitates writing a query to find find the connected components of an undirected graph. FossilOrigin-Name: 5481fa8c79c34f434e99ab633ff3d0942a309a74fb0cf38e3d3617b51d5d21dd
This commit is contained in:
94
src/select.c
94
src/select.c
@@ -2340,6 +2340,7 @@ static void generateWithRecursiveQuery(
|
||||
int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
|
||||
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
|
||||
Select *pSetup = p->pPrior; /* The setup query */
|
||||
Select *pFirstRec; /* Left-most recursive term */
|
||||
int addrTop; /* Top of the loop */
|
||||
int addrCont, addrBreak; /* CONTINUE and BREAK addresses */
|
||||
int iCurrent = 0; /* The Current table */
|
||||
@@ -2415,7 +2416,25 @@ static void generateWithRecursiveQuery(
|
||||
/* Detach the ORDER BY clause from the compound SELECT */
|
||||
p->pOrderBy = 0;
|
||||
|
||||
/* Figure out how many elements of the compound SELECT are part of the
|
||||
** recursive query. Make sure no recursive elements use aggregate
|
||||
** functions. Mark the recursive elements as UNION ALL even if they
|
||||
** are really UNION because the distinctness will be enforced by the
|
||||
** iDistinct table. pFirstRec is left pointing to the left-most
|
||||
** recursive term of the CTE.
|
||||
*/
|
||||
pFirstRec = p;
|
||||
for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){
|
||||
if( pFirstRec->selFlags & SF_Aggregate ){
|
||||
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
||||
goto end_of_recursive_query;
|
||||
}
|
||||
pFirstRec->op = TK_ALL;
|
||||
if( (pFirstRec->pPrior->selFlags & SF_Recursive)==0 ) break;
|
||||
}
|
||||
|
||||
/* Store the results of the setup-query in Queue. */
|
||||
pSetup = pFirstRec->pPrior;
|
||||
pSetup->pNext = 0;
|
||||
ExplainQueryPlan((pParse, 1, "SETUP"));
|
||||
rc = sqlite3Select(pParse, pSetup, &destQueue);
|
||||
@@ -2448,15 +2467,11 @@ static void generateWithRecursiveQuery(
|
||||
/* Execute the recursive SELECT taking the single row in Current as
|
||||
** the value for the recursive-table. Store the results in the Queue.
|
||||
*/
|
||||
if( p->selFlags & SF_Aggregate ){
|
||||
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
||||
}else{
|
||||
p->pPrior = 0;
|
||||
ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
|
||||
sqlite3Select(pParse, p, &destQueue);
|
||||
assert( p->pPrior==0 );
|
||||
p->pPrior = pSetup;
|
||||
}
|
||||
pFirstRec->pPrior = 0;
|
||||
ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
|
||||
sqlite3Select(pParse, p, &destQueue);
|
||||
assert( pFirstRec->pPrior==0 );
|
||||
pFirstRec->pPrior = pSetup;
|
||||
|
||||
/* Keep running the loop until the Queue is empty */
|
||||
sqlite3VdbeGoto(v, addrTop);
|
||||
@@ -2525,6 +2540,16 @@ static int multiSelectValues(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the SELECT statement which is known to be the recursive
|
||||
** part of a recursive CTE still has its anchor terms attached. If the
|
||||
** anchor terms have already been removed, then return false.
|
||||
*/
|
||||
static int hasAnchor(Select *p){
|
||||
while( p && (p->selFlags & SF_Recursive)!=0 ){ p = p->pPrior; }
|
||||
return p!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to process a compound query form from
|
||||
** two or more separate queries using UNION, UNION ALL, EXCEPT, or
|
||||
@@ -2610,7 +2635,7 @@ static int multiSelect(
|
||||
assert( p->pEList->nExpr==pPrior->pEList->nExpr );
|
||||
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
if( p->selFlags & SF_Recursive ){
|
||||
if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){
|
||||
generateWithRecursiveQuery(pParse, p, &dest);
|
||||
}else
|
||||
#endif
|
||||
@@ -2701,6 +2726,7 @@ static int multiSelect(
|
||||
findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
||||
assert( p->pEList );
|
||||
}
|
||||
|
||||
|
||||
/* Code the SELECT statements to our left
|
||||
*/
|
||||
@@ -4794,8 +4820,10 @@ static int withExpand(
|
||||
ExprList *pEList;
|
||||
Select *pSel;
|
||||
Select *pLeft; /* Left-most SELECT statement */
|
||||
Select *pRecTerm; /* Left-most recursive term */
|
||||
int bMayRecursive; /* True if compound joined by UNION [ALL] */
|
||||
With *pSavedWith; /* Initial value of pParse->pWith */
|
||||
int iRecTab = -1; /* Cursor for recursive table */
|
||||
|
||||
/* If pCte->zCteErr is non-NULL at this point, then this is an illegal
|
||||
** recursive reference to CTE pCte. Leave an error in pParse and return
|
||||
@@ -4820,44 +4848,48 @@ static int withExpand(
|
||||
assert( pFrom->pSelect );
|
||||
|
||||
/* Check if this is a recursive CTE. */
|
||||
pSel = pFrom->pSelect;
|
||||
pRecTerm = pSel = pFrom->pSelect;
|
||||
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
|
||||
if( bMayRecursive ){
|
||||
while( bMayRecursive && pRecTerm->op==pSel->op ){
|
||||
int i;
|
||||
SrcList *pSrc = pFrom->pSelect->pSrc;
|
||||
SrcList *pSrc = pRecTerm->pSrc;
|
||||
assert( pRecTerm->pPrior!=0 );
|
||||
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->fg.isRecursive = 1;
|
||||
if( pRecTerm->selFlags & SF_Recursive ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"multiple references to recursive table: %s", pCte->zName
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pTab->nTabRef++;
|
||||
pSel->selFlags |= SF_Recursive;
|
||||
pRecTerm->selFlags |= SF_Recursive;
|
||||
if( iRecTab<0 ) iRecTab = pParse->nTab++;
|
||||
pItem->iCursor = iRecTab;
|
||||
}
|
||||
}
|
||||
if( (pRecTerm->selFlags & SF_Recursive)==0 ) break;
|
||||
pRecTerm = pRecTerm->pPrior;
|
||||
}
|
||||
|
||||
/* Only one recursive reference is permitted. */
|
||||
if( pTab->nTabRef>2 ){
|
||||
sqlite3ErrorMsg(
|
||||
pParse, "multiple references to recursive table: %s", pCte->zName
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
assert( pTab->nTabRef==1 ||
|
||||
((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 ));
|
||||
|
||||
pCte->zCteErr = "circular reference: %s";
|
||||
pSavedWith = pParse->pWith;
|
||||
pParse->pWith = pWith;
|
||||
if( bMayRecursive ){
|
||||
Select *pPrior = pSel->pPrior;
|
||||
assert( pPrior->pWith==0 );
|
||||
pPrior->pWith = pSel->pWith;
|
||||
sqlite3WalkSelect(pWalker, pPrior);
|
||||
pPrior->pWith = 0;
|
||||
if( pSel->selFlags & SF_Recursive ){
|
||||
assert( pRecTerm!=0 );
|
||||
assert( (pRecTerm->selFlags & SF_Recursive)==0 );
|
||||
assert( pRecTerm->pNext!=0 );
|
||||
assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 );
|
||||
assert( pRecTerm->pWith==0 );
|
||||
pRecTerm->pWith = pSel->pWith;
|
||||
sqlite3WalkSelect(pWalker, pRecTerm);
|
||||
pRecTerm->pWith = 0;
|
||||
}else{
|
||||
sqlite3WalkSelect(pWalker, pSel);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user