From: dan Date: Fri, 18 Dec 2020 16:13:39 +0000 (+0000) Subject: When flattening UNION ALL subqueries into a join query, ensure that separate cursor... X-Git-Tag: version-3.35.0~174^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=964fa26e0c81e999950ce1f9b95e0547e93fdf5d;p=thirdparty%2Fsqlite.git When flattening UNION ALL subqueries into a join query, ensure that separate cursor numbers are used for each segment of the newly flattened query. FossilOrigin-Name: c510377b0b052e400f2ee4f20220b61cdf74ee44b9bb9e6490787c88dd4c55aa --- diff --git a/manifest b/manifest index 8381f8dd8d..be6d22c043 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\spart\sof\sthe\sheader\scomment\sfor\sflattenSubquery(). -D 2020-12-17T17:17:12.113 +C When\sflattening\sUNION\sALL\ssubqueries\sinto\sa\sjoin\squery,\sensure\sthat\sseparate\scursor\snumbers\sare\sused\sfor\seach\ssegment\sof\sthe\snewly\sflattened\squery. +D 2020-12-18T16:13:39.603 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -540,7 +540,7 @@ F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 1948a92ca9eab776632816b97e57c61d933474a78aad4f4ef835c916a83dbb1c F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c a9c7607d8fe60e17df2b8839abad2e333b306e40e0b20e53747b3dcf600832ec +F src/select.c 7282b1a0fc81ce3c61c988b228b63b45a5521254033370d1ad6bf3366b5c2788 F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce0090 F src/sqlite.h.in 5b7593bb0f3658e682a9fcd1cd8fcedf244ec45ca93d645055a53172f55eb783 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1627,8 +1627,8 @@ F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776 F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a -F test/unionall.test d2a6956fa3f01e0566b2cb81b2eea6e10be67c54ca1b1623685c6dbff8bd465b -F test/unionallfault.test 8dcc3f680ace498d8d3110ddcfdaa6a3d8aa1843bc7c266b990f13815ee6d7fe +F test/unionall.test 6676ff915eced719e7010149d758ba85665c0aef6a782e693b6fb5e83fb4e4e1 +F test/unionallfault.test 652bfbb630e6c43135965dc1e8f0a9a791da83aec885d626a632fe1909c56f73 F test/unionvtab.test e1704ab1b4c1bb3ffc9da4681f8e85a0b909fd80b937984fc94b27415ac8e5a4 F test/unionvtabfault.test e8759f3d14fb938ce9657e2342db34aeac0fb9bc1692b0d1ebb0069630151d06 F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 @@ -1893,7 +1893,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5d6dc29d5f81738b07e4fee652fb2343fc409c2545f2f4667e8ee82d1a75f721 -R 856c8700228bcdbf610d5cdb34881db9 +P dc0937ce9d5569e3409b2b036a9f15b896125f4eb2eec30e3f0bbe4a92bcd0ad +R d7f9897a9bbc548d53ddd8cd562a8b6a U dan -Z d0c22076b1f40da6b64269c66f6c133c +Z aa626afff11dd6191a903497d2bb6cf2 diff --git a/manifest.uuid b/manifest.uuid index 3ab5a1e229..dcf4a244f3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dc0937ce9d5569e3409b2b036a9f15b896125f4eb2eec30e3f0bbe4a92bcd0ad \ No newline at end of file +c510377b0b052e400f2ee4f20220b61cdf74ee44b9bb9e6490787c88dd4c55aa \ No newline at end of file diff --git a/src/select.c b/src/select.c index d6cd724ae0..bc5f17ecb2 100644 --- a/src/select.c +++ b/src/select.c @@ -3649,6 +3649,86 @@ static void recomputeColumnsUsed( } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ +#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) +/* +** Assign new cursor numbers to each of the items in pSrc. For each +** new cursor number assigned, set an entry in the aCsrMap[] array +** to map the old cursor number to the new: +** +** aCsrMap[iOld] = iNew; +** +** The array is guaranteed by the caller to be large enough for all +** existing cursor numbers in pSrc. +** +** If pSrc contains any sub-selects, call this routine recursively +** on the FROM clause of each such sub-select, with iExcept set to -1. +*/ +static void srclistRenumberCursors( + Parse *pParse, /* Parse context */ + int *aCsrMap, /* Array to store cursor mappings in */ + SrcList *pSrc, /* FROM clause to renumber */ + int iExcept /* FROM clause item to skip */ +){ + int i; + struct SrcList_item *pItem; + for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){ + if( i!=iExcept ){ + int iNew = pParse->nTab++; + aCsrMap[pItem->iCursor] = iNew; + pItem->iCursor = iNew; + if( pItem->pSelect ){ + srclistRenumberCursors(pParse, aCsrMap, pItem->pSelect->pSrc, -1); + } + } + } +} + +/* +** Expression walker callback used by renumberCursors() to update +** Expr objects to match newly assigned cursor numbers. +*/ +static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ + int *aCsrMap = pWalker->u.aiCol; + if( pExpr->op==TK_COLUMN && aCsrMap[pExpr->iTable] ){ + pExpr->iTable = aCsrMap[pExpr->iTable]; + } + return WRC_Continue; +} + +/* +** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc) +** of the SELECT statement passed as the second argument, and to each +** cursor in the FROM clause of any FROM clause sub-selects, recursively. +** Except, do not assign a new cursor number to the iExcept'th element in +** the FROM clause of (*p). Update all expressions and other references +** to refer to the new cursor numbers. +** +** Argument aCsrMap is an array that may be used for temporary working +** space. Two guarantees are made by the caller: +** +** * the array is larger than the largest cursor number used within the +** select statement passed as an argument, and +** +** * the array entries for all cursor numbers that do *not* appear in +** FROM clauses of the select statement as described above are +** initialized to zero. +*/ +static void renumberCursors( + Parse *pParse, /* Parse context */ + Select *p, /* Select to renumber cursors within */ + int iExcept, /* FROM clause item to skip */ + int *aCsrMap /* Working space */ +){ + Walker w; + srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept); + memset(&w, 0, sizeof(w)); + w.u.aiCol = aCsrMap; + w.xExprCallback = renumberCursorsCb; + w.xSelectCallback = sqlite3SelectWalkNoop; + sqlite3WalkSelect(&w, p); +} +#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** This routine attempts to flatten subqueries as a performance optimization. @@ -3824,6 +3904,7 @@ static int flattenSubquery( struct SrcList_item *pSubitem; /* The subquery */ sqlite3 *db = pParse->db; Walker w; /* Walker to persist agginfo data */ + int *aCsrMap = 0; /* Check to see if flattening is permitted. Return 0 if not. */ @@ -3950,6 +4031,10 @@ static int flattenSubquery( /* Restriction (23) */ if( (p->selFlags & SF_Recursive) ) return 0; + + if( pSrc->nSrc>1 ){ + aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int)); + } } /***** If we reach this point, flattening is permitted. *****/ @@ -4024,6 +4109,9 @@ static int flattenSubquery( if( pNew==0 ){ p->pPrior = pPrior; }else{ + if( aCsrMap && db->mallocFailed==0 ){ + renumberCursors(pParse, pNew, iFrom, aCsrMap); + } pNew->pPrior = pPrior; if( pPrior ) pPrior->pNext = pNew; pNew->pNext = p; @@ -4032,10 +4120,11 @@ static int flattenSubquery( " creates %u as peer\n",pNew->selId)); } assert( pSubitem->pSelect==0 ); - if( db->mallocFailed ){ - pSubitem->pSelect = pSub1; - return 1; - } + } + sqlite3DbFree(db, aCsrMap); + if( db->mallocFailed ){ + pSubitem->pSelect = pSub1; + return 1; } /* Defer deleting the Table object associated with the diff --git a/test/unionall.test b/test/unionall.test index 43b24a455f..0098cf15a4 100644 --- a/test/unionall.test +++ b/test/unionall.test @@ -165,6 +165,37 @@ do_execsql_test 2.2.8 { 3 three 3 iii 3 THREE 3 iii } +do_execsql_test 2.2.9a { + SELECT * FROM t1, t2 ORDER BY +k +} { + 1 one 2 ii 1 ONE 2 ii 2 two 2 ii + 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii + + 1 one 3 iii 1 ONE 3 iii 2 two 3 iii + 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii + + 1 one 4 iv 1 ONE 4 iv 2 two 4 iv + 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv + + 1 one 5 v 1 ONE 5 v 2 two 5 v + 2 TWO 5 v 3 three 5 v 3 THREE 5 v +} + +do_execsql_test 2.2.9b { + SELECT * FROM t1, t2 ORDER BY k +} { + 1 one 2 ii 1 ONE 2 ii 2 two 2 ii + 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii + + 1 one 3 iii 1 ONE 3 iii 2 two 3 iii + 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii + + 1 one 4 iv 1 ONE 4 iv 2 two 4 iv + 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv + + 1 one 5 v 1 ONE 5 v 2 two 5 v + 2 TWO 5 v 3 three 5 v 3 THREE 5 v +} finish_test diff --git a/test/unionallfault.test b/test/unionallfault.test index f9cde26636..c78abe548c 100644 --- a/test/unionallfault.test +++ b/test/unionallfault.test @@ -25,9 +25,9 @@ do_faultsim_test 1 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { - SELECT * FROM ( + SELECT * FROM t1, ( SELECT x FROM t1 UNION ALL SELECT y FROM t1 - ) + ), t3 } } -test { faultsim_test_result {0 {}}