mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-14 00:22:38 +03:00
Improve the error messages used to report illegal recursive cte references.
FossilOrigin-Name: 54eee9fe99290e59469bd3e1a66bb749887d37ee
This commit is contained in:
18
manifest
18
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Allow\sonly\sa\ssingle\srecursive\sreference\sin\sa\srecursive\sCTE.\sAlso\srequire\sthat\sthis\sreference\sis\snot\spart\sof\sa\ssub-query.
|
C Improve\sthe\serror\smessages\sused\sto\sreport\sillegal\srecursive\scte\sreferences.
|
||||||
D 2014-01-16T18:34:33.041
|
D 2014-01-16T21:02:02.206
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@@ -169,7 +169,7 @@ F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
|||||||
F src/btree.c c15e1722696b66c4029c487acfb830b0985bf142
|
F src/btree.c c15e1722696b66c4029c487acfb830b0985bf142
|
||||||
F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9
|
F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9
|
||||||
F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
|
F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
|
||||||
F src/build.c 5a2daa6649640711ca60114046903be932348e52
|
F src/build.c a6b9ba918f9194e955d198402076fe3203ff2567
|
||||||
F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98
|
F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98
|
||||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||||
F src/ctime.c 77779efbe78dd678d84bfb4fc2e87b6b6ad8dccd
|
F src/ctime.c 77779efbe78dd678d84bfb4fc2e87b6b6ad8dccd
|
||||||
@@ -219,12 +219,12 @@ F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269
|
|||||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||||
F src/resolve.c ae278d8ce037883323f677e78c241f64289f12ec
|
F src/resolve.c ae278d8ce037883323f677e78c241f64289f12ec
|
||||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||||
F src/select.c 2d2da29fd4c877646109f7d2fba26b5cc454cd37
|
F src/select.c 1b058f7ef37e2667fd33921531480a616a04bca2
|
||||||
F src/shell.c 9f3bc02a658b8f61d2cbe60cfc482f660c1c6c48
|
F src/shell.c 9f3bc02a658b8f61d2cbe60cfc482f660c1c6c48
|
||||||
F src/sqlite.h.in d94a8b89522f526ba711182ee161e06f8669bcc9
|
F src/sqlite.h.in d94a8b89522f526ba711182ee161e06f8669bcc9
|
||||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||||
F src/sqliteInt.h b30bd95081be525b32551f180cf43ccfaff9f5bc
|
F src/sqliteInt.h d49c0bea5282f15c1eb1eb9d705770f70d19c1e2
|
||||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||||
@@ -1091,7 +1091,7 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
|||||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||||
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
||||||
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
||||||
F test/with1.test 8ac68051fce8f2fe4a7bb6cb7ba62c0e941b1c84
|
F test/with1.test f1602892682cd9bfa5b3b0a5d04b2283b61e7982
|
||||||
F test/withM.test 52448ce23e1c2ecba79d10e130ee49ce9f9a2a7a
|
F test/withM.test 52448ce23e1c2ecba79d10e130ee49ce9f9a2a7a
|
||||||
F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8
|
F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8
|
||||||
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||||
@@ -1150,7 +1150,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||||
P 6bfa387e82de47ca1f40225fe28d873e29d6f481
|
P a296b73360d34c9364eceb2cc09a9a92adc4abb8
|
||||||
R d0f7d170eccd6b8c29b55960683e8b9a
|
R 20dd6a1609afa0cf73fe04c795b9e1e4
|
||||||
U dan
|
U dan
|
||||||
Z 617b83d8e92edcc7afd63e19b605afe5
|
Z 6bd9745179436f1c3b8a75548e64bd7b
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
a296b73360d34c9364eceb2cc09a9a92adc4abb8
|
54eee9fe99290e59469bd3e1a66bb749887d37ee
|
||||||
@@ -4245,6 +4245,7 @@ With *sqlite3WithAdd(
|
|||||||
pNew->a[pNew->nCte].pSelect = pQuery;
|
pNew->a[pNew->nCte].pSelect = pQuery;
|
||||||
pNew->a[pNew->nCte].pCols = pArglist;
|
pNew->a[pNew->nCte].pCols = pArglist;
|
||||||
pNew->a[pNew->nCte].zName = zName;
|
pNew->a[pNew->nCte].zName = zName;
|
||||||
|
pNew->a[pNew->nCte].zErr = 0;
|
||||||
pNew->nCte++;
|
pNew->nCte++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
66
src/select.c
66
src/select.c
@@ -3533,44 +3533,6 @@ void sqlite3WithPush(Parse *pParse, With *pWith){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** If argument pCte is not NULL, check if it is already a part of the
|
|
||||||
** stack of CTEs stored by the parser. If so, this indicates an illegal
|
|
||||||
** recursive reference in a CTE, set of mutually recursive CTEs. Store
|
|
||||||
** an error in the parser and return SQLITE_ERROR if this is the case.
|
|
||||||
**
|
|
||||||
** Otherwise, if pCte is not already part of the stack of CTEs stored
|
|
||||||
** in the parser, push it onto the stop of that stack.
|
|
||||||
*/
|
|
||||||
static int ctePush(Parse *pParse, struct Cte *pCte){
|
|
||||||
if( pCte ){
|
|
||||||
struct Cte *p;
|
|
||||||
for(p=pParse->pCte; p; p=p->pOuterCte){
|
|
||||||
if( p==pCte ){
|
|
||||||
sqlite3ErrorMsg(
|
|
||||||
pParse, "illegal recursive defininition in cte: %s", pCte->zName
|
|
||||||
);
|
|
||||||
return SQLITE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pCte->pOuterCte = pParse->pCte;
|
|
||||||
pParse->pCte = pCte;
|
|
||||||
}
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
** If argument pCte is not NULL, it must be a pointer to the CTE currently
|
|
||||||
** on top of the stack of CTEs stored in the parser. Remove it from that
|
|
||||||
** stack.
|
|
||||||
*/
|
|
||||||
static void ctePop(Parse *pParse, struct Cte *pCte){
|
|
||||||
if( pCte ){
|
|
||||||
assert( pParse->pCte==pCte );
|
|
||||||
pParse->pCte = pCte->pOuterCte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This function checks if argument pFrom refers to a CTE declared by
|
** This function checks if argument pFrom refers to a CTE declared by
|
||||||
** a WITH clause on the stack currently maintained by the parser. And,
|
** a WITH clause on the stack currently maintained by the parser. And,
|
||||||
@@ -3602,6 +3564,16 @@ static int withExpand(
|
|||||||
ExprList *pEList;
|
ExprList *pEList;
|
||||||
Select *pSel;
|
Select *pSel;
|
||||||
Select *pLeft; /* Left-most SELECT statement */
|
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));
|
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
||||||
if( pTab==0 ) return WRC_Abort;
|
if( pTab==0 ) return WRC_Abort;
|
||||||
@@ -3616,7 +3588,8 @@ static int withExpand(
|
|||||||
|
|
||||||
/* Check if this is a recursive CTE. */
|
/* Check if this is a recursive CTE. */
|
||||||
pSel = pFrom->pSelect;
|
pSel = pFrom->pSelect;
|
||||||
if( pSel->op==TK_ALL || pSel->op==TK_UNION ){
|
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
|
||||||
|
if( bMayRecursive ){
|
||||||
int i;
|
int i;
|
||||||
SrcList *pSrc = pFrom->pSelect->pSrc;
|
SrcList *pSrc = pFrom->pSelect->pSrc;
|
||||||
for(i=0; i<pSrc->nSrc; i++){
|
for(i=0; i<pSrc->nSrc; i++){
|
||||||
@@ -3642,8 +3615,8 @@ static int withExpand(
|
|||||||
}
|
}
|
||||||
assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
|
assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
|
||||||
|
|
||||||
if( ctePush(pParse, pCte) ) return WRC_Abort;
|
pCte->zErr = "circular reference to cte: %s";
|
||||||
sqlite3WalkSelect(pWalker, pTab->nRef==2 ? pSel->pPrior : pSel);
|
sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
|
||||||
|
|
||||||
for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
|
for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
|
||||||
pEList = pLeft->pEList;
|
pEList = pLeft->pEList;
|
||||||
@@ -3658,8 +3631,15 @@ static int withExpand(
|
|||||||
}
|
}
|
||||||
selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
|
selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
|
||||||
|
|
||||||
if( pSel->selFlags & SF_Recursive ) sqlite3WalkSelect(pWalker, pSel);
|
if( bMayRecursive ){
|
||||||
ctePop(pParse, pCte);
|
if( pSel->selFlags & SF_Recursive ){
|
||||||
|
pCte->zErr = "multiple recursive references in cte: %s";
|
||||||
|
}else{
|
||||||
|
pCte->zErr = "recursive reference may not appear in sub-query: %s";
|
||||||
|
}
|
||||||
|
sqlite3WalkSelect(pWalker, pSel);
|
||||||
|
}
|
||||||
|
pCte->zErr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
|
|||||||
@@ -2371,7 +2371,6 @@ struct Parse {
|
|||||||
Table *pZombieTab; /* List of Table objects to delete after code gen */
|
Table *pZombieTab; /* List of Table objects to delete after code gen */
|
||||||
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
|
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
|
||||||
With *pWith; /* Current WITH clause, or NULL */
|
With *pWith; /* Current WITH clause, or NULL */
|
||||||
struct Cte *pCte; /* Current CTE, or NULL */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2650,7 +2649,7 @@ struct With {
|
|||||||
char *zName; /* Name of this CTE */
|
char *zName; /* Name of this CTE */
|
||||||
ExprList *pCols; /* List of explicit column names, or NULL */
|
ExprList *pCols; /* List of explicit column names, or NULL */
|
||||||
Select *pSelect; /* The definition of this CTE */
|
Select *pSelect; /* The definition of this CTE */
|
||||||
struct Cte *pOuterCte; /* Next WITH clause in outer context */
|
const char *zErr; /* Error message for circular references */
|
||||||
} a[1];
|
} a[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -76,10 +76,10 @@ do_execsql_test 2.5 {
|
|||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
do_catchsql_test 3.1 {
|
do_catchsql_test 3.1 {
|
||||||
WITH tmp2(x) AS ( SELECT * FROM tmp1),
|
WITH tmp2(x) AS ( SELECT * FROM tmp1 ),
|
||||||
tmp1(a) AS ( SELECT * FROM tmp2 )
|
tmp1(a) AS ( SELECT * FROM tmp2 )
|
||||||
SELECT * FROM tmp1;
|
SELECT * FROM tmp1;
|
||||||
} {1 {illegal recursive defininition in cte: tmp1}}
|
} {1 {circular reference to cte: tmp1}}
|
||||||
|
|
||||||
do_catchsql_test 3.2 {
|
do_catchsql_test 3.2 {
|
||||||
CREATE TABLE t2(x INTEGER);
|
CREATE TABLE t2(x INTEGER);
|
||||||
@@ -268,6 +268,64 @@ do_execsql_test 6.4 {
|
|||||||
} {10}
|
} {10}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
do_execsql_test 7.1 {
|
||||||
|
CREATE TABLE tree(i, p);
|
||||||
|
INSERT INTO tree VALUES(1, NULL);
|
||||||
|
INSERT INTO tree VALUES(2, 1);
|
||||||
|
INSERT INTO tree VALUES(3, 1);
|
||||||
|
INSERT INTO tree VALUES(4, 2);
|
||||||
|
INSERT INTO tree VALUES(5, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 7.2 {
|
||||||
|
WITH t(id, path) AS (
|
||||||
|
SELECT i, '' FROM tree WHERE p IS NULL
|
||||||
|
UNION ALL
|
||||||
|
SELECT i, path || '/' || i FROM tree, t WHERE p = id
|
||||||
|
)
|
||||||
|
SELECT path FROM t;
|
||||||
|
} {{} /2 /3 /2/4 /2/4/5}
|
||||||
|
|
||||||
|
do_execsql_test 7.3 {
|
||||||
|
WITH t(id) AS (
|
||||||
|
VALUES(2)
|
||||||
|
UNION ALL
|
||||||
|
SELECT i FROM tree, t WHERE p = id
|
||||||
|
)
|
||||||
|
SELECT id FROM t;
|
||||||
|
} {2 4 5}
|
||||||
|
|
||||||
|
do_catchsql_test 7.4 {
|
||||||
|
WITH t(id) AS (
|
||||||
|
VALUES(2)
|
||||||
|
UNION ALL
|
||||||
|
SELECT i FROM tree WHERE p IN (SELECT id FROM t)
|
||||||
|
)
|
||||||
|
SELECT id FROM t;
|
||||||
|
} {1 {recursive reference may not appear in sub-query: t}}
|
||||||
|
|
||||||
|
do_catchsql_test 7.5 {
|
||||||
|
WITH t(id) AS (
|
||||||
|
VALUES(2)
|
||||||
|
UNION ALL
|
||||||
|
SELECT i FROM tree, t WHERE p = id AND p IN (SELECT id FROM t)
|
||||||
|
)
|
||||||
|
SELECT id FROM t;
|
||||||
|
} {1 {multiple recursive references in cte: t}}
|
||||||
|
|
||||||
|
do_catchsql_test 7.6 {
|
||||||
|
WITH t(id) AS (
|
||||||
|
SELECT i FROM tree WHERE 2 IN (SELECT id FROM t)
|
||||||
|
UNION ALL
|
||||||
|
SELECT i FROM tree, t WHERE p = id
|
||||||
|
)
|
||||||
|
SELECT id FROM t;
|
||||||
|
} {1 {circular reference to cte: t}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user