113 lines
3.8 KiB
Diff
113 lines
3.8 KiB
Diff
Subject: [PATCH] Avoid infinite recursion in the ALTER TABLE code when a view
|
|
contains an unused CTE that references, directly or indirectly, the view itself.
|
|
|
|
diff --git a/src/alter.c b/src/alter.c
|
|
index 707472a..132c821 100644
|
|
--- a/src/alter.c
|
|
+++ b/src/alter.c
|
|
@@ -796,6 +796,7 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
|
|
** descend into sub-select statements.
|
|
*/
|
|
static int renameColumnSelectCb(Walker *pWalker, Select *p){
|
|
+ if( p->selFlags & SF_View ) return WRC_Prune;
|
|
UNUSED_PARAMETER(pWalker);
|
|
UNUSED_PARAMETER(p);
|
|
return WRC_Continue;
|
|
@@ -1258,8 +1259,9 @@ static void renameColumnFunc(
|
|
if( sParse.pNewTable ){
|
|
Select *pSelect = sParse.pNewTable->pSelect;
|
|
if( pSelect ){
|
|
+ pSelect->selFlags &= ~SF_View;
|
|
sParse.rc = SQLITE_OK;
|
|
- sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
|
|
+ sqlite3SelectPrep(&sParse, pSelect, 0);
|
|
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
|
|
if( rc==SQLITE_OK ){
|
|
sqlite3WalkSelect(&sWalker, pSelect);
|
|
@@ -1368,6 +1370,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
|
|
int i;
|
|
RenameCtx *p = pWalker->u.pRename;
|
|
SrcList *pSrc = pSelect->pSrc;
|
|
+ if( pSelect->selFlags & SF_View ) return WRC_Prune;
|
|
for(i=0; i<pSrc->nSrc; i++){
|
|
struct SrcList_item *pItem = &pSrc->a[i];
|
|
if( pItem->pTab==p->pTab ){
|
|
@@ -1442,10 +1445,13 @@ static void renameTableFunc(
|
|
|
|
if( pTab->pSelect ){
|
|
if( isLegacy==0 ){
|
|
+ Select *pSelect = pTab->pSelect;
|
|
NameContext sNC;
|
|
memset(&sNC, 0, sizeof(sNC));
|
|
sNC.pParse = &sParse;
|
|
|
|
+ assert( pSelect->selFlags & SF_View );
|
|
+ pSelect->selFlags &= ~SF_View;
|
|
sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
|
|
if( sParse.nErr ) rc = sParse.rc;
|
|
sqlite3WalkSelect(&sWalker, pTab->pSelect);
|
|
diff --git a/src/build.c b/src/build.c
|
|
index f273394..7d73893 100644
|
|
--- a/src/build.c
|
|
+++ b/src/build.c
|
|
@@ -2266,6 +2266,7 @@ void sqlite3CreateView(
|
|
** allocated rather than point to the input string - which means that
|
|
** they will persist after the current sqlite3_exec() call returns.
|
|
*/
|
|
+ pSelect->selFlags |= SF_View;
|
|
if( IN_RENAME_OBJECT ){
|
|
p->pSelect = pSelect;
|
|
pSelect = 0;
|
|
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
|
|
index e5ba8a0..1cf6937 100644
|
|
--- a/src/sqliteInt.h
|
|
+++ b/src/sqliteInt.h
|
|
@@ -2875,6 +2875,7 @@ struct Select {
|
|
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
|
|
#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
|
|
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
|
|
+#define SF_View 0x0200000 /* SELECT statement is a view */
|
|
|
|
/*
|
|
** The results of a SELECT can be distributed in several ways, as defined
|
|
diff --git a/test/altertab2.test b/test/altertab2.test
|
|
index 2102e02..f2fa5ee 100644
|
|
--- a/test/altertab2.test
|
|
+++ b/test/altertab2.test
|
|
@@ -106,4 +106,35 @@ do_catchsql_test 3.2 {
|
|
ALTER TABLE v0 RENAME TO t3 ;
|
|
} {1 {error in view v2: view v2 is circularly defined}}
|
|
|
|
+#------------------------------------------------------------------------
|
|
+#
|
|
+reset_db
|
|
+do_execsql_test 4.1 {
|
|
+ CREATE TABLE t1(a);
|
|
+ CREATE VIEW v2(b) AS SELECT * FROM v2;
|
|
+}
|
|
+
|
|
+do_catchsql_test 4.2 {
|
|
+ ALTER TABLE t1 RENAME TO t4;
|
|
+} {1 {error in view v2: view v2 is circularly defined}}
|
|
+
|
|
+do_execsql_test 4.3 {
|
|
+ DROP VIEW v2;
|
|
+ CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) SELECT * FROM t3;
|
|
+}
|
|
+
|
|
+breakpoint
|
|
+do_catchsql_test 4.4 {
|
|
+ ALTER TABLE t1 RENAME TO t4;
|
|
+} {1 {error in view v2: view v2 is circularly defined}}
|
|
+
|
|
+do_execsql_test 4.5 {
|
|
+ DROP VIEW v2;
|
|
+ CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) VALUES(1);
|
|
+}
|
|
+
|
|
+do_catchsql_test 4.6 {
|
|
+ ALTER TABLE t1 RENAME TO t4;
|
|
+} {0 {}}
|
|
+
|
|
finish_test
|