From: drh <> Date: Thu, 7 Apr 2022 01:11:13 +0000 (+0000) Subject: Improved technique for parsing the ON and USING clauses of a join is faster X-Git-Tag: version-3.39.0~233 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d44f8b2385eeb97d799fe5172c7514972c6f3359;p=thirdparty%2Fsqlite.git Improved technique for parsing the ON and USING clauses of a join is faster and uses less memory. FossilOrigin-Name: 158156a3e3d50042cafc75dea3aaaa68b1f2efb9c3d178518ea6e68e32e0d21c --- diff --git a/manifest b/manifest index 3290802af4..18e886918b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Faster\sparsing\sof\sthe\sFROM\sclause\sin\sjoins\sfor\sthe\scommon\scase\swhere\sthere\nis\sno\sINDEXED\sBY\sclause. -D 2022-04-06T19:46:20.282 +C Improved\stechnique\sfor\sparsing\sthe\sON\sand\sUSING\sclauses\sof\sa\sjoin\sis\sfaster\nand\suses\sless\smemory. +D 2022-04-07T01:11:13.364 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -485,9 +485,9 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 006325f8844c65d885b3ba469b4c08d9dd0cd3e9ec481d5bcff621f224cb2302 +F src/alter.c 9395ece9850ad57c6fbb453aeb5185be4bae3b159c4b37611425c565124ee849 F src/analyze.c 3a119baeb03053c154029877454d41bb8fd79d4d1eb583392f2289b3554a75bc -F src/attach.c f26d400f3ffe2cdca01406bca70e5f58c5488bf165b4fc37c228136dfcf1b583 +F src/attach.c 4431f82f0247bf3aaf91589acafdff77d1882235c95407b36da1585c765fbbc8 F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7 F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d @@ -495,15 +495,15 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 45161c2d5c9527b9c9bbfd7478daf3e0a619cf4bbe3278378aaea3d4b4e4f5b5 F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22 F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e -F src/build.c 4a265d49342cefc95ae739982a6197eae04a9258151f8e0bbe3ff8ab56aab801 +F src/build.c ff119be98394a65bc8be7afc39d4a791a66f03a778d396de3ec456f5dfaf39e8 F src/callback.c 4c19af69835787bfe790ac560f3071a824eb629f34e41f97b52ce5235c77de1c F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 026dbdcdbd8c3cde98a88483ee88310ff43150ab164ad768f12cc700a11495ad F src/date.c 15082566229d4b1e5f24fdb490bf9bcc68824b911d70e3573ef075a1b9e2d26f F src/dbpage.c a70be9a4879ac5392673a1050d526a72b8b2f9938df7049f65348566a2637db3 F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d -F src/delete.c dafdc71a097f84fbbf66164d8842ebcb9bacf74e069658001fd70b62ee0acbdb -F src/expr.c 3cdb00b6c15f815c94836e7b4474b675155d1279e64804f6ab5816188a9b05b6 +F src/delete.c a8e844af211a48b13b5b358be77a12c860c6a557c21990ad51a548e2536500ce +F src/expr.c 5e247a8dfabb92e9fd10b78a675dc5d25430433dfd9e316471b4447b548635ba F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 94927f9b46d72a9cb858c208febf04ceb0a3270c5fa5fd0b7f436cf16e09f72a F src/func.c a3407a6fbb0d4088d8d502e46f0ace63e0aeae7467ae23a9ca9815bbf9239761 @@ -541,7 +541,7 @@ F src/os_win.c a8ea80037e81127ca01959daa87387cc135f325c88dc745376c4f760de852a10 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 42120492784fc9bcd9082b5c9b5e329b7318c357f9f3574a1bbfcf7418910356 F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3 -F src/parse.y a2786e72c5ce8a91df7dbad8306616d822ba7093f87cfa0d09fe351b83fa5828 +F src/parse.y afae75ce87abc2d87a8277ed811bc09d04da4b1760ace3e845cb1553f3a3dd0a F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65 @@ -550,14 +550,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c fd940149c691684e7c1073c3787a7170e44852b02d1275d2e30a5b58e89cfcaf F src/printf.c 05d8dfd2018bc4fc3ddb8b37eb97ccef7abf985643fa1caebdcf2916ca90fa32 F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c -F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e +F src/resolve.c 18d99e7146852d6064559561769fcca0743eb32b14a97da6dbed373a30ee0e76 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c c3dab5fb5d25934bc384b0ffcbc1ab576ad8ce4a494a5eed3e1367c79c1416b1 +F src/select.c d6c04eb93395024af80f61a8c278a33c2a0333aeb7d57bb6aa737a6f1c4af4b8 F src/shell.c.in 18832612e74c92bbd25d88e1f92685f66589262f68cca1001d2a43bd6dd0ed60 F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e -F src/sqliteInt.h f9484b03c663a20ac998627a416d8ef368950596e52e1186c5188ee93b695285 +F src/sqliteInt.h dbd5537c36a6b01fd67890487c3f7da468f16845500c115886f20adec06869ca F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4a3da6d77eeb3531cb0dbdf7047772a2a1b99f98c69e90ce009c75fe6328b2c0 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c a2483d4fe7cde88638cd3140dd5d6ee3dc0c80d3b3fe20322a8d08dc451f97ae F src/whereInt.h 15d2975c3b4c193c78c26674400a840da8647fe1777ae3b026e2d15937b38a03 F src/wherecode.c 555f598a9ddad81761f084710fdb4f8733fe31bc14cd6b19f8ca4274a7eaa04c -F src/whereexpr.c 2a71f5491798460c9590317329234d332d9eb1717cba4f3403122189a75c465e +F src/whereexpr.c 612f58f5f6e3e3bb94d10e2c56672ade8bbf94d4a928d3edb4e84e2ed3c00dca F src/window.c 42a71595263dbd8ef8248218e4fc7d4b5ddccece52146ad48e079342d93f6f8f F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -878,7 +878,7 @@ F test/e_fts3.test 17ba7c373aba4d4f5696ba147ee23fd1a1ef70782af050e03e262ca187c5e F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e F test/e_reindex.test 2b0e29344497d9a8a999453a003cb476b6b1d2eef2d6c120f83c2d3a429f3164 F test/e_resolve.test a61751c368b109db73df0f20fc75fb47e166b1d8 -F test/e_select.test c5425a423da06d0494119db8361ebfc6de302929f7546ca596d56224137e0360 +F test/e_select.test 9b7ca08975c5444844b35ee60e09f973787a9f3317719715e8e6669abdf6aba2 F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f F test/e_totalchanges.test c927f7499dc3aa28b9b556b7d6d115a2f0fe41f012b128d16bf1f3b30e9b41e4 F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528 @@ -1140,7 +1140,7 @@ F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b F test/istrue.test e7f285bb70282625c258e866ce6337d4c762922f5a300e1b50f958aef6e7d9c9 -F test/join.test 25cf0ac11c3b81fedfd166f9062166bdb39dea92f5a7c16cacbf6dc1f7f67020 +F test/join.test 85e9c88bf4700b45a48a6362cd47e0c0aefc572629827c31aa58a5978cabdfc5 F test/join2.test 9bdc615841b91c97a16d68bad9508aea11fa0c6b34e5689847bcc4dac70e4990 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 @@ -1628,7 +1628,7 @@ F test/tkt3911.test 74cd324f3ba653040cc6d94cc4857b290d12d633 F test/tkt3918.test ea78bf164e4d55cbde0d83c671ef6fbe930a0032 F test/tkt3922.test f26be40ab4fe6c00795629bd2006d96e270d9b1a F test/tkt3929.test cdf67acf5aa936ec4ffead81db87f8a71fe40e59 -F test/tkt3935.test e15261fedb9e30a4305a311da614a5d8e693c767 +F test/tkt3935.test 1ffcfffc148df51c8a01d1b3efae2d6c44cbeb0af1e0c5b88f4afe9a86d4ddb6 F test/tkt3992.test f3e7d548ac26f763b47bc0f750da3d03c81071da F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd F test/tkt4018.test 18dbc6617f7a4b90e938d1bd6d26ad18daafaf08 @@ -1711,7 +1711,7 @@ F test/vtab2.test 14d4ab26cee13ba6cf5c5601b158e4f57552d3b055cdd9406cf7f711e9c840 F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e F test/vtab4.test 8e73ed268f3d596bc3590f45fc948fb40f28e9c3 F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391 -F test/vtab6.test 82d5bb8fd3c0643102c1209e9ea353b168b7eb9c8db4406ab2ee2cbbdaead62c +F test/vtab6.test 7167e8e526bc2e719e7818e18b2fd7bb8c455fa018b74e611943a86782e10125 F test/vtab7.test 70c6f4a1d6177144a8236e4172d5fba92e683440374664ad1f04851fbb335d3c F test/vtab8.test e19fa4a538fcd1bb66c22825fa8f71618fb13583 F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b @@ -1945,8 +1945,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 84c239a071cfaf8af107646f01ef269e2915fd2384e95927d484f2e408ba6bbf -R c85c19380822b3766b7352b207ade832 +P 848b7a0ea99ddc52091b78313f018c07d00a0e28aa6da8c1cae709c1d03468fe +R f48eedde1f7cf0c2cbb2f0efc2fa0619 U drh -Z f7c9c9fec330149ee82e748d50b754aa +Z 3a4c374a49d27fcf334d19f314ba8d15 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6856212ee1..7245377486 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -848b7a0ea99ddc52091b78313f018c07d00a0e28aa6da8c1cae709c1d03468fe \ No newline at end of file +158156a3e3d50042cafc75dea3aaaa68b1f2efb9c3d178518ea6e68e32e0d21c \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 1ec74cdcc7..62075df759 100644 --- a/src/alter.c +++ b/src/alter.c @@ -858,11 +858,10 @@ static void unmapColumnIdlistNames( Parse *pParse, const IdList *pIdList ){ - if( pIdList ){ - int ii; - for(ii=0; iinId; ii++){ - sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); - } + int ii; + assert( pIdList!=0 ); + for(ii=0; iinId; ii++){ + sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); } } @@ -890,8 +889,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ SrcList *pSrc = p->pSrc; for(i=0; inSrc; i++){ sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); - sqlite3WalkExpr(pWalker, pSrc->a[i].pOn); - unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); + if( pSrc->a[i].fg.isUsing==0 ){ + sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn); + }else{ + unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing); + } } } diff --git a/src/attach.c b/src/attach.c index e587f6cc5e..1732be27bf 100644 --- a/src/attach.c +++ b/src/attach.c @@ -480,7 +480,11 @@ static int fixSelectCb(Walker *p, Select *pSelect){ pItem->fg.fromDDL = 1; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) - if( sqlite3WalkExpr(&pFix->w, pList->a[i].pOn) ) return WRC_Abort; + if( pList->a[i].fg.isUsing==0 + && sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn) + ){ + return WRC_Abort; + } #endif } if( pSelect->pWith ){ diff --git a/src/build.c b/src/build.c index 35f1ea3639..da862fa707 100644 --- a/src/build.c +++ b/src/build.c @@ -4710,7 +4710,7 @@ void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ */ int sqlite3IdListIndex(IdList *pList, const char *zName){ int i; - if( pList==0 ) return -1; + assert( pList!=0 ); for(i=0; inId; i++){ if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; } @@ -4913,8 +4913,11 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); sqlite3DeleteTable(db, pItem->pTab); if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect); - if( pItem->pOn ) sqlite3ExprDelete(db, pItem->pOn); - if( pItem->pUsing ) sqlite3IdListDelete(db, pItem->pUsing); + if( pItem->fg.isUsing ){ + sqlite3IdListDelete(db, pItem->u3.pUsing); + }else if( pItem->u3.pOn ){ + sqlite3ExprDelete(db, pItem->u3.pOn); + } } sqlite3DbFreeNN(db, pList); } @@ -4942,14 +4945,13 @@ SrcList *sqlite3SrcListAppendFromTerm( Token *pDatabase, /* Name of the database containing pTable */ Token *pAlias, /* The right-hand side of the AS subexpression */ Select *pSubquery, /* A subquery used in place of a table name */ - Expr *pOn, /* The ON clause of a join */ - IdList *pUsing /* The USING clause of a join */ + OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */ ){ SrcItem *pItem; sqlite3 *db = pParse->db; - if( !p && (pOn || pUsing) ){ + if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){ sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", - (pOn ? "ON" : "USING") + (pOnUsing->pOn ? "ON" : "USING") ); goto append_from_error; } @@ -4970,14 +4972,21 @@ SrcList *sqlite3SrcListAppendFromTerm( pItem->zAlias = sqlite3NameFromToken(db, pAlias); } pItem->pSelect = pSubquery; - pItem->pOn = pOn; - pItem->pUsing = pUsing; + assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); + assert( pItem->fg.isUsing==0 ); + if( pOnUsing==0 ){ + pItem->u3.pOn = 0; + }else if( pOnUsing->pUsing ){ + pItem->fg.isUsing = 1; + pItem->u3.pUsing = pOnUsing->pUsing; + }else{ + pItem->u3.pOn = pOnUsing->pOn; + } return p; append_from_error: assert( p==0 ); - sqlite3ExprDelete(db, pOn); - sqlite3IdListDelete(db, pUsing); + sqlite3ClearOnOrUsing(db, pOnUsing); sqlite3SelectDelete(db, pSubquery); return 0; } diff --git a/src/delete.c b/src/delete.c index a4c5d53c58..df378d2d58 100644 --- a/src/delete.c +++ b/src/delete.c @@ -128,8 +128,8 @@ void sqlite3MaterializeView( assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); - assert( pFrom->a[0].pOn==0 ); - assert( pFrom->a[0].pUsing==0 ); + assert( pFrom->a[0].fg.isUsing==0 ); + assert( pFrom->a[0].u3.pOn==0 ); } pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, SF_IncludeHidden, pLimit); diff --git a/src/expr.c b/src/expr.c index 79889bdd75..2c00bb498d 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1249,6 +1249,18 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p ) sqlite3ExprDeleteNN(db, p); } +/* +** Clear both elements of an OnOrUsing object +*/ +void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ + if( p==0 ){ + /* Nothing to clear */ + }else if( p->pOn ){ + sqlite3ExprDeleteNN(db, p->pOn); + }else if( p->pUsing ){ + sqlite3IdListDelete(db, p->pUsing); + } +} /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. @@ -1671,8 +1683,12 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ pTab->nTabRef++; } pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); - pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); - pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); + if( pOldItem->fg.isUsing ){ + assert( pNewItem->fg.isUsing ); + pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing); + }else{ + pNewItem->u3.pOn = sqlite3ExprDup(db, pOldItem->u3.pOn, flags); + } pNewItem->colUsed = pOldItem->colUsed; } return pNew; diff --git a/src/parse.y b/src/parse.y index e413739ff7..7b3af1f1c2 100644 --- a/src/parse.y +++ b/src/parse.y @@ -570,7 +570,7 @@ selectnowith(A) ::= selectnowith(A) multiselect_op(Y) oneselect(Z). { Token x; x.n = 0; parserDoubleLinkSelect(pParse, pRhs); - pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); + pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0); pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ @@ -695,30 +695,26 @@ stl_prefix(A) ::= seltablist(A) joinop(Y). { if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].fg.jointype = (u8)Y; } stl_prefix(A) ::= . {A = 0;} -seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U); +seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) on_using(N). { + A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N); } -seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) indexed_by(I) - on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U); +seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) indexed_by(I) on_using(N). { + A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N); sqlite3SrcListIndexedBy(pParse, A, &I); } -seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) - on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U); +seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N). { + A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N); sqlite3SrcListFuncArgs(pParse, A, E); } %ifndef SQLITE_OMIT_SUBQUERY - seltablist(A) ::= stl_prefix(A) LP select(S) RP - as(Z) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,N,U); + seltablist(A) ::= stl_prefix(A) LP select(S) RP as(Z) on_using(N). { + A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,&N); } - seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP - as(Z) on_opt(N) using_opt(U). { - if( A==0 && Z.n==0 && N==0 && U==0 ){ + seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP as(Z) on_using(N). { + if( A==0 && Z.n==0 && N.pOn==0 && N.pUsing==0 ){ A = F; }else if( F->nSrc==1 ){ - A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,&N); if( A ){ SrcItem *pNew = &A->a[A->nSrc-1]; SrcItem *pOld = F->a; @@ -739,7 +735,7 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) Select *pSubquery; sqlite3SrcListShiftJoinType(F); pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0); - A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,&N); } } %endif SQLITE_OMIT_SUBQUERY @@ -797,13 +793,14 @@ joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN. // // INSERT INTO tab SELECT * FROM aaa JOIN bbb WHERE true ON CONFLICT ... // -// The [AND] and [OR] precedence marks in the rules for on_opt cause the +// The [AND] and [OR] precedence marks in the rules for on_using cause the // ON in this context to always be interpreted as belonging to the JOIN. // -%type on_opt {Expr*} -%destructor on_opt {sqlite3ExprDelete(pParse->db, $$);} -on_opt(N) ::= ON expr(E). {N = E;} -on_opt(N) ::= . [OR] {N = 0;} +%type on_using {OnOrUsing} +//%destructor on_using {sqlite3ClearOnOrUsing(pParse->db, &$$);} +on_using(N) ::= ON expr(E). {N.pOn = E; N.pUsing = 0;} +on_using(N) ::= USING LP idlist(L) RP. {N.pOn = 0; N.pUsing = L;} +on_using(N) ::= . [OR] {N.pOn = 0; N.pUsing = 0;} // Note that this block abuses the Token type just a little. If there is // no "INDEXED BY" clause, the returned token is empty (z==0 && n==0). If @@ -822,12 +819,6 @@ indexed_opt(A) ::= indexed_by(A). indexed_by(A) ::= INDEXED BY nm(X). {A = X;} indexed_by(A) ::= NOT INDEXED. {A.z=0; A.n=1;} -%type using_opt {IdList*} -%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);} -using_opt(U) ::= USING LP idlist(L) RP. {U = L;} -using_opt(U) ::= . {U = 0;} - - %type orderby_opt {ExprList*} %destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);} diff --git a/src/resolve.c b/src/resolve.c index 480694f6f5..30785ca706 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -123,11 +123,10 @@ static void resolveAlias( ** zCol. */ static int nameInUsingClause(IdList *pUsing, const char *zCol){ - if( pUsing ){ - int k; - for(k=0; knId; k++){ - if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; - } + int k; + assert( pUsing!=0 ); + for(k=0; knId; k++){ + if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; } return 0; } @@ -346,7 +345,11 @@ static int lookupName( */ if( cnt==1 ){ if( pItem->fg.jointype & JT_NATURAL ) continue; - if( nameInUsingClause(pItem->pUsing, zCol) ) continue; + if( pItem->fg.isUsing + && nameInUsingClause(pItem->u3.pUsing, zCol) + ){ + continue; + } } cnt++; pMatch = pItem; diff --git a/src/select.c b/src/select.c index a0d94c0c8d..989446af61 100644 --- a/src/select.c +++ b/src/select.c @@ -468,7 +468,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ ** every column that the two tables have in common. */ if( pRight->fg.jointype & JT_NATURAL ){ - if( pRight->pOn || pRight->pUsing ){ + if( pRight->fg.isUsing || pRight->u3.pOn ){ sqlite3ErrorMsg(pParse, "a NATURAL join may not have " "an ON or USING clause", 0); return 1; @@ -487,23 +487,6 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ } } - /* Disallow both ON and USING clauses in the same join - */ - if( pRight->pOn && pRight->pUsing ){ - sqlite3ErrorMsg(pParse, "cannot have both ON and USING " - "clauses in the same join"); - return 1; - } - - /* Add the ON clause to the end of the WHERE clause, connected by - ** an AND operator. - */ - if( pRight->pOn ){ - if( isOuter ) sqlite3SetJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn); - pRight->pOn = 0; - } - /* Create extra terms on the WHERE clause for each column named ** in the USING clause. Example: If the two tables to be joined are ** A and B and the USING clause names X, Y, and Z, then add this @@ -511,8 +494,9 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ ** Report an error if any column mentioned in the USING clause is ** not contained in both tables to be joined. */ - if( pRight->pUsing ){ - IdList *pList = pRight->pUsing; + if( pRight->fg.isUsing ){ + IdList *pList = pRight->u3.pUsing; + assert( pList!=0 ); for(j=0; jnId; j++){ char *zName; /* Name of the term in the USING clause */ int iLeft; /* Table on the left with matching column name */ @@ -532,6 +516,15 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ isOuter, &p->pWhere); } } + + /* Add the ON clause to the end of the WHERE clause, connected by + ** an AND operator. + */ + else if( pRight->u3.pOn ){ + if( isOuter ) sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor); + p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn); + pRight->u3.pOn = 0; + } } return 0; } @@ -4222,7 +4215,7 @@ static int flattenSubquery( pSubitem->zName = 0; pSubitem->zAlias = 0; pSubitem->pSelect = 0; - assert( pSubitem->pOn==0 ); + assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); /* If the sub-query is a compound SELECT statement, then (by restrictions ** 17 and 18 above) it must be a UNION ALL and the parent query must @@ -4366,9 +4359,10 @@ static int flattenSubquery( ** outer query. */ for(i=0; ia[i+iFrom].pUsing); - assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); - pSrc->a[i+iFrom] = pSubSrc->a[i]; + SrcItem *pItem = &pSrc->a[i+iFrom]; + if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); + assert( pItem->fg.isTabFunc==0 ); + *pItem = pSubSrc->a[i]; iNewParent = pSubSrc->a[i].iCursor; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } @@ -5092,7 +5086,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); if( pNew==0 ) return WRC_Abort; memset(&dummy, 0, sizeof(dummy)); - pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0); + pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); if( pNewSrc==0 ) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; @@ -5703,7 +5697,9 @@ static int selectExpander(Walker *pWalker, Select *p){ ** table to the right of the join */ continue; } - if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ + if( pFrom->fg.isUsing + && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0 + ){ /* In a join with a USING clause, omit columns in the ** using clause from the table on the right. */ continue; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a77df1d8b8..9f95ec8a9d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1191,6 +1191,7 @@ typedef struct Lookaside Lookaside; typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; +typedef struct OnOrUsing OnOrUsing; typedef struct Parse Parse; typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; @@ -3075,10 +3076,13 @@ struct SrcItem { unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ + unsigned isUsing :1; /* u3.pUsing is valid */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ - Expr *pOn; /* The ON clause of a join */ - IdList *pUsing; /* The USING clause of a join */ + union { + Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ + IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ + } u3; Bitmask colUsed; /* Bit N (1<" clause */ @@ -3090,6 +3094,15 @@ struct SrcItem { } u2; }; +/* +** The OnOrUsing object represents either an ON clause or a USING clause. +** It can never be both at the same time, but it can be neither. +*/ +struct OnOrUsing { + Expr *pOn; /* The ON clause of a join */ + IdList *pUsing; /* The USING clause of a join */ +}; + /* ** The following structure describes the FROM clause of a SELECT statement. ** Each table or subquery in the FROM clause is a separate element of @@ -4610,13 +4623,14 @@ SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, - Token*, Select*, Expr*, IdList*); + Token*, Select*, OnOrUsing*); void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); int sqlite3IndexedByLookup(Parse *, SrcItem *); void sqlite3SrcListShiftJoinType(SrcList*); void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(sqlite3*, IdList*); +void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*); void sqlite3SrcListDelete(sqlite3*, SrcList*); Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, diff --git a/src/whereexpr.c b/src/whereexpr.c index 19dd886de5..26a521fffd 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -951,7 +951,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ int i; for(i=0; inSrc; i++){ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); - mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); + if( pSrc->a[i].fg.isUsing==0 ){ + mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); + } if( pSrc->a[i].fg.isTabFunc ){ mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); } diff --git a/test/e_select.test b/test/e_select.test index a4e74d30e1..1ffd4021b6 100644 --- a/test/e_select.test +++ b/test/e_select.test @@ -96,7 +96,7 @@ do_join_test e_select-0.1.3 { } {9} do_catchsql_test e_select-0.1.4 { SELECT count(*) FROM t1, t2 ON (t1.a=t2.a) USING (a) -} {1 {cannot have both ON and USING clauses in the same join}} +} {1 {near "USING": syntax error}} do_catchsql_test e_select-0.1.5 { SELECT count(*) FROM t1, t2 USING (a) ON (t1.a=t2.a) } {1 {near "ON": syntax error}} diff --git a/test/join.test b/test/join.test index 221dbdd098..f48a1a149d 100644 --- a/test/join.test +++ b/test/join.test @@ -307,7 +307,7 @@ do_test join-3.3 { catchsql { SELECT * FROM t1 JOIN t2 ON t1.a=t2.b USING(b); } -} {1 {cannot have both ON and USING clauses in the same join}} +} {1 {near "USING": syntax error}} do_test join-3.4.1 { catchsql { SELECT * FROM t1 JOIN t2 USING(a); diff --git a/test/tkt3935.test b/test/tkt3935.test index abbeb3f866..a4f1f61dad 100644 --- a/test/tkt3935.test +++ b/test/tkt3935.test @@ -34,13 +34,13 @@ do_test tkt3935.3 { do_test tkt3935.4 { catchsql { SELECT a FROM (t1) AS t ON b USING(a) } -} {1 {a JOIN clause is required before ON}} +} {1 {near "USING": syntax error}} do_test tkt3935.5 { catchsql { SELECT a FROM (t1) AS t ON b } } {1 {a JOIN clause is required before ON}} do_test tkt3935.6 { catchsql { SELECT a FROM (SELECT * FROM t1) AS t ON b USING(a) } -} {1 {a JOIN clause is required before ON}} +} {1 {near "USING": syntax error}} do_test tkt3935.7 { catchsql { SELECT a FROM (SELECT * FROM t1) AS t ON b } } {1 {a JOIN clause is required before ON}} @@ -49,7 +49,7 @@ do_test tkt3935.8 { } {1 {a JOIN clause is required before ON}} do_test tkt3935.9 { catchsql { SELECT a FROM t1 AS t ON b USING(a) } -} {1 {a JOIN clause is required before ON}} +} {1 {near "USING": syntax error}} do_test tkt3935.10 { catchsql { SELECT a FROM t1 AS t USING(a) } } {1 {a JOIN clause is required before USING}} diff --git a/test/vtab6.test b/test/vtab6.test index ffbe430c6e..ab52c9f15c 100644 --- a/test/vtab6.test +++ b/test/vtab6.test @@ -263,7 +263,7 @@ do_test vtab6-3.3 { catchsql { SELECT * FROM t1 JOIN t2 ON t1.a=t2.b USING(b); } -} {1 {cannot have both ON and USING clauses in the same join}} +} {1 {near "USING": syntax error}} do_test vtab6-3.4 { catchsql { SELECT * FROM t1 JOIN t2 USING(a);