From e6ff280c839fdfc7418fd5add12e8f32702a17fb Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 5 Oct 2026 09:32:24 +0000 Subject: [PATCH] Limit the depth of recursion for CTEs using SQLITE_LIMIT_EXPR_DEPTH. [bugs:/info/2026-10-04T04:58:58Z|Bug 2026-10-04T04:58:58Z]. FossilOrigin-Name: f54730871303e655a70f3167ce1a39dcd4e53fcf017d225f429e2bb67ad96604 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 36 ++++++++++++++++++++++++------------ test/with2.test | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index 586bfc4cab..4ed38fb19e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sexpression\stree\sdepth\scalculations\sfor\sCOLLATE.\n[bugs:/info/2026-10-04T04:50:56Z|Bug\s2026-10-04T04:50:56Z] -D 2026-10-04T21:54:17.561 +C Limit\sthe\sdepth\sof\srecursion\sfor\sCTEs\susing\sSQLITE_LIMIT_EXPR_DEPTH.\n[bugs:/info/2026-10-04T04:58:58Z|Bug\s2026-10-04T04:58:58Z]. +D 2026-10-05T09:32:24.919 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -800,7 +800,7 @@ F src/printf.c 4bd228ed9e9346d558362be562757ee5eef6f9bf483b7f02751ca42cf95174d3 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 0e0990f60c93db38c735d85e5d1741130e72dd0670aec204b5a39118b53c8044 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 5859a4a19581d91fb2c3a818c18ad45de6906434aeaf0c4b74ca5f58295cb432 +F src/select.c 6aa1d385f58b7e5b813a1ea4008dfe36378568d13e1cdfac8fb21f135f13e589 F src/shell.c.in 8504577eb02e87b58f9d632bc4458efda074476428774692207ddacc0fe9e1df F src/sqlite.h.in 884996e07cfb7681efc76661b79d56a93f9edaea7f6fc4a3c82636dc84783486 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 @@ -2162,7 +2162,7 @@ F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3c F test/windowfault.test 15094c1529424e62f798bc679e3fe9dfab6e8ba2f7dfe8c923b6248c31660a7c F test/windowpushd.test c420e2265f0e09a0e798d0513a660d71b51602088d81b3dbd038918ee1339dcc F test/with1.test c416097db4ead936b98a76ff3069f76a6009c5aaec5ff2b2c5ca210bc595ad57 -F test/with2.test 181674a6cc86a601ca2ac052741cdfad5b529e07e870435d2f6cdb92d589ff17 +F test/with2.test 70cdc0b211a2c5ffccc93f5c50cf092367bf5a1792576d23a479915a80fb5506 F test/with3.test e30369ea27aa27eb1bda4c5e510c8a9f782c8afd2ab99d1a02b8a7f25a5d3e65 F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205 F test/with5.test 262a604e001938428da3d06ca90df9a478eff34ada737e2c023a99954418e8cf @@ -2278,8 +2278,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P d4330c79a477bab7e86aba860813f4f83e9af5ef8067698fcdf41efd6a0e8516 -R 960558da009947c307ca304fdbbe4e0f +P 252c0b7bf5a727d29605672bb58e06c758173c276671df3e388a68294998b4d3 +R 290d962d1474ee55ec1b78b7d7977cb5 U drh -Z 6378420ac511b5740e99c19b2a3da65d +Z 4a8d751b209e545320f7b152e9bdedd1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e3648f7934..9a0c32db84 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -252c0b7bf5a727d29605672bb58e06c758173c276671df3e388a68294998b4d3 +f54730871303e655a70f3167ce1a39dcd4e53fcf017d225f429e2bb67ad96604 diff --git a/src/select.c b/src/select.c index be04fec950..9932689e29 100644 --- a/src/select.c +++ b/src/select.c @@ -2490,6 +2490,20 @@ void sqlite3SubqueryColumnTypes( pTab->szTabRow = 1; /* Any non-zero value works */ } +/* +** Check the current subquery nesting depth. Return true and set an +** error if it has gone too deep. +*/ +static int checkSubqueryNestingDepth(Parse *pParse){ +#if SQLITE_MAX_EXPR_DEPTH>0 + if( pParse->nNestSel >= pParse->db->aLimit[SQLITE_LIMIT_EXPR_DEPTH] ){ + sqlite3ErrorMsg(pParse, "VIEWs and/or subqueries nested too deep"); + return 1; + } +#endif + return 0; +} + /* ** Given a SELECT statement, generate a Table structure that describes ** the result set of that SELECT. @@ -2500,12 +2514,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, char aff){ u64 savedFlags; pParse->nNestSel++; -#if SQLITE_MAX_EXPR_DEPTH>0 - if( pParse->nNestSel >= db->aLimit[SQLITE_LIMIT_EXPR_DEPTH] ){ - sqlite3ErrorMsg(pParse, "VIEWs and/or subqueries nested too deep"); - return 0; - } -#endif + if( checkSubqueryNestingDepth(pParse) ) return 0; savedFlags = db->flags; db->flags &= ~(u64)SQLITE_FullColNames; db->flags |= SQLITE_ShortColNames; @@ -5752,14 +5761,13 @@ With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ } /* -** This function checks if argument pFrom refers to a CTE declared by +** This function checks to see if argument pFrom refers to a CTE declared by ** a WITH clause on the stack currently maintained by the parser (on the -** pParse->pWith linked list). And if currently processing a CTE -** CTE expression, through routine checks to see if the reference is -** a recursive reference to the CTE. +** pParse->pWith linked list). And if currently processing a CTE expression, +** it also checks to see if the reference is a recursive reference to the CTE. ** -** If pFrom matches a CTE according to either of these two above, pFrom->pSTab -** and other fields are populated accordingly. +** If pFrom matches a CTE, pFrom->pSTab and other fields are populated +** accordingly. ** ** Return 0 if no match is found. ** Return 1 if a match is found. @@ -5888,6 +5896,8 @@ static int resolveFromTermToCte( pRecTerm = pRecTerm->pPrior; } + pParse->nNestSel++; + if( checkSubqueryNestingDepth(pParse) ) return 2; pCte->zCteErr = "circular reference: %s"; pSavedWith = pParse->pWith; pParse->pWith = pWith; @@ -5912,6 +5922,8 @@ static int resolveFromTermToCte( } } pParse->pWith = pWith; + pParse->nNestSel--; + assert( pParse->nNestSel>=0 ); for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); pEList = pLeft->pEList; diff --git a/test/with2.test b/test/with2.test index 68790fe860..a6125bdaa3 100644 --- a/test/with2.test +++ b/test/with2.test @@ -635,4 +635,37 @@ do_execsql_test 13.1 { SELECT * FROM t1, t3; } {111 222} +# Bug 2026-10-04T04:58:58Z +reset_db +sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH 12 +do_catchsql_test 14.1 { + WITH c0 AS (SELECT 1 AS x), + c1 AS (SELECT x FROM c0), + c2 AS (SELECT x FROM c1), + c3 AS (SELECT x FROM c2), + c4 AS (SELECT x FROM c3), + c5 AS (SELECT x FROM c4), + c6 AS (SELECT x FROM c5), + c7 AS (SELECT x FROM c6), + c8 AS (SELECT x FROM c7), + c9 AS (SELECT x FROM c8), + c10 AS (SELECT x FROM c9) + SELECT x FROM c10; +} {0 1} +sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH 11 +do_catchsql_test 14.2 { + WITH c00 AS (SELECT 1 AS x), + c1 AS (SELECT x FROM c00), + c2 AS (SELECT x FROM c1), + c3 AS (SELECT x FROM c2), + c4 AS (SELECT x FROM c3), + c5 AS (SELECT x FROM c4), + c6 AS (SELECT x FROM c5), + c7 AS (SELECT x FROM c6), + c8 AS (SELECT x FROM c7), + c9 AS (SELECT x FROM c8), + c10 AS (SELECT x FROM c9) + SELECT x FROM c10; +} {1 {VIEWs and/or subqueries nested too deep}} + finish_test