From 40c9becc812947508c453d44b9ed1846db228eff Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 16:57:49 +0000 Subject: [PATCH 1/7] Is the query flattener restriction 29 (do not allow both EP_InnerON and EP_OuterON constraints on the same FROM clause term) really necessary? This branch explores what happens if we omit that restriction. This is an effort to address the performance regression reported by [forum:/forumpost/96b9e5709cf47cda|forum post 96b9e5709cf47cda] that is caused by flattener restriction 29. FossilOrigin-Name: 19270577ad1d08c9e6e5f0600396b8a1ccda9df1e67a0df845aee6b60437f379 --- manifest | 17 ++++++++++------- manifest.uuid | 2 +- src/expr.c | 1 + src/select.c | 13 +++---------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 5284efaae6..5d537c49f8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Clarify\scomments\sthat\sdescribe\squery\sflattener\srestrictions\sassociated\swith\nouter\sjoins.\s\sNo\scode\schanges. -D 2022-09-19T19:25:15.448 +C Is\sthe\squery\sflattener\srestriction\s29\s(do\snot\sallow\sboth\sEP_InnerON\sand\nEP_OuterON\sconstraints\son\sthe\ssame\sFROM\sclause\sterm)\sreally\snecessary?\s\s\sThis\nbranch\sexplores\swhat\shappens\sif\swe\somit\sthat\srestriction.\s\sThis\sis\san\seffort\nto\saddress\sthe\sperformance\sregression\sreported\sby\n[forum:/forumpost/96b9e5709cf47cda|forum\spost\s96b9e5709cf47cda]\sthat\sis\scaused\nby\sflattener\srestriction\s29. +D 2022-09-20T16:57:49.607 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -539,7 +539,7 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5 F src/dbpage.c 5808e91bc27fa3981b028000f8fadfdc10ce9e59a34ce7dc4e035a69be3906ec F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e -F src/expr.c 24e828db6b2fab8aabfb5d2c0d83dbdfc5a1972b1147fa893350e317ab7e282f +F src/expr.c a6692ca00680c8ac4ec6cf637e04df1ca331e1a68e311b425eea61f0c8477019 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002 F src/func.c 8f72e88cccdee22185133c10f96ccd61dc34c5ea4b1fa9a73c237ef59b2e64f1 @@ -588,7 +588,7 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764 F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c dcfd2df77e4601555cef48174e3c61723f72fccfdd5b80caa5da63e23338c040 +F src/select.c 57813f5b8d716d69f3375053022d45150b7bb913bde8eac9cd6e64a519c13f8a F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33df F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -2000,8 +2000,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8032db4ade66f70a102abb9d3a49e20620e204c45ad7a954a49d20403985bf07 -R 279e5ed5d3d08518a51dc0906b1aae25 +P 2647d73bb1876d01851f52cb3fd6af08a962ab336b6d51c160d27b1baf94f75f +R 60a74f8c958f8f5c6d2d000c0573265e +T *branch * flattener-omit-restriction-29 +T *sym-flattener-omit-restriction-29 * +T -sym-trunk * U drh -Z 9a3376787d630872f605d6b2d292568c +Z a7b69d349e01c35beb24d52a569b48a5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6a2f858f62..4791f75a70 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2647d73bb1876d01851f52cb3fd6af08a962ab336b6d51c160d27b1baf94f75f \ No newline at end of file +19270577ad1d08c9e6e5f0600396b8a1ccda9df1e67a0df845aee6b60437f379 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 8572512926..064e78fdea 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3223,6 +3223,7 @@ void sqlite3CodeRhsOfIN( sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } if( addrOnce ){ + sqlite3VdbeAddOp2(v, OP_Rewind, iTab, sqlite3VdbeCurrentAddr(v)+1); sqlite3VdbeJumpHere(v, addrOnce); /* Subroutine return */ assert( ExprUseYSub(pExpr) ); diff --git a/src/select.c b/src/select.c index 7b7731c234..11e01d2f81 100644 --- a/src/select.c +++ b/src/select.c @@ -3724,7 +3724,7 @@ static int multiSelectOrderBy( ** the left operands of a RIGHT JOIN. In either case, we need to potentially ** bypass the substituted expression with OP_IfNullRow. ** -** Suppose the original expression integer constant. Even though the table +** Suppose the original expression is an integer constant. Even though the table ** has the nullRow flag set, because the expression is an integer constant, ** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode ** that checks to see if the nullRow flag is set on the table. If the nullRow @@ -4296,15 +4296,6 @@ static int flattenSubquery( } isOuterJoin = 1; } -#ifdef SQLITE_EXTRA_IFNULLROW - else if( iFrom>0 && !isAgg ){ - /* Setting isOuterJoin to -1 causes OP_IfNullRow opcodes to be generated for - ** every reference to any result column from subquery in a join, even - ** though they are not necessary. This will stress-test the OP_IfNullRow - ** opcode. */ - isOuterJoin = -1; - } -#endif assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */ if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ @@ -4314,6 +4305,7 @@ static int flattenSubquery( return 0; /* (28) */ } +#if 0 /* NO LONGER REQUIRED */ /* Restriction (29): ** ** We do not want two constraints on the same FROM-clause term of the @@ -4340,6 +4332,7 @@ static int flattenSubquery( return 0; } } +#endif /* NO LONGER REQUIRED */ /* Restriction (17): If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries From 5a0771a1f4b9b9baddb634cbadd823434b3f9346 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 18:18:00 +0000 Subject: [PATCH 2/7] Remove code that has been previously commented out. FossilOrigin-Name: 852b385a5de622aa32026824210d4bd23db52a4a8a697b83d22d6000edeba487 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/select.c | 34 ---------------------------------- 3 files changed, 7 insertions(+), 41 deletions(-) diff --git a/manifest b/manifest index e358a9d8ae..4ddfaacbfd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\sfuzzcheck\sfix\sfrom\strunk\sinto\sthe\sflattener\simprovement\sbranch. -D 2022-09-20T17:43:40.500 +C Remove\scode\sthat\shas\sbeen\spreviously\scommented\sout. +D 2022-09-20T18:18:00.607 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -588,7 +588,7 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764 F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c 57813f5b8d716d69f3375053022d45150b7bb913bde8eac9cd6e64a519c13f8a +F src/select.c bb18acf4eded647fef88d4d543c673874dbebff516fbeba90a85e6c13f2a58cd F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33df F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -2000,8 +2000,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 19270577ad1d08c9e6e5f0600396b8a1ccda9df1e67a0df845aee6b60437f379 8d8c124349d2a25200ace9a60d76751594b722468ad0c39d927a5d2c6e2464e0 -R e0204c145e73869d816b572fd2b71bc8 +P 930a8c1561d0d5dc70dc5378edac77f79ac59ab6569b561de3dfb02f160c38c5 +R dcb98f6a73c54efed4da4f980f298d5b U drh -Z dcbf3f74b6eb7e493002c755cbf2f6d6 +Z 1cd83b2593765cf9bcd77e53d7a14363 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f0098c2225..ac64a7f2b0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -930a8c1561d0d5dc70dc5378edac77f79ac59ab6569b561de3dfb02f160c38c5 \ No newline at end of file +852b385a5de622aa32026824210d4bd23db52a4a8a697b83d22d6000edeba487 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 11e01d2f81..2876f48ab8 100644 --- a/src/select.c +++ b/src/select.c @@ -4188,11 +4188,6 @@ static void renumberCursors( ** ** (28) The subquery is not a MATERIALIZED CTE. ** -** (29) Either the subquery is not the right-hand operand of a join with an -** ON or USING clause nor the right-hand operand of a NATURAL JOIN, or -** the right-most table within the FROM clause of the subquery -** is not part of an outer join. -** ** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query @@ -4305,35 +4300,6 @@ static int flattenSubquery( return 0; /* (28) */ } -#if 0 /* NO LONGER REQUIRED */ - /* Restriction (29): - ** - ** We do not want two constraints on the same FROM-clause term of the - ** flattened query where one constraint has the EP_InnerON flag and the - ** other has the EP_OuterON flag. - ** - ** To prevent this, one or the other of the following conditions must be - ** false: - ** - ** (29a) The right-most entry in the FROM clause of the subquery - ** must not be part of an outer join. - ** - ** (29b) The subquery itself must not be the right operand of a - ** NATURAL join or a join that has an ON or USING clause. - */ - if( pSubSrc->nSrc>=2 - && (pSubSrc->a[pSubSrc->nSrc-1].fg.jointype & JT_OUTER)!=0 - ){ - if( (pSubitem->fg.jointype & JT_NATURAL)!=0 - || pSubitem->fg.isUsing - || NEVER(pSubitem->u3.pOn!=0) /* ON clause already shifted into WHERE */ - || pSubitem->fg.isOn - ){ - return 0; - } - } -#endif /* NO LONGER REQUIRED */ - /* Restriction (17): If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries ** that make up the compound SELECT are allowed to be aggregate or distinct From 8b9a3d1fc8481c691f66e0793b63f8de29ca59cd Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 19:22:17 +0000 Subject: [PATCH 3/7] Modify the OP_IfNotOpen opcode so that the jump is taken if the cursor is open but was previously set to a NULL row using OP_NullRow. FossilOrigin-Name: 1292d68caa7086610ddda343f3852e63de8da1eb66536ee4716b6529f5a31bc6 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 2 +- src/vdbe.c | 10 +++++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 4ddfaacbfd..45f45fb98f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\scode\sthat\shas\sbeen\spreviously\scommented\sout. -D 2022-09-20T18:18:00.607 +C Modify\sthe\sOP_IfNotOpen\sopcode\sso\sthat\sthe\sjump\sis\staken\sif\sthe\scursor\sis\sopen\nbut\swas\spreviously\sset\sto\sa\sNULL\srow\susing\sOP_NullRow. +D 2022-09-20T19:22:17.805 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -539,7 +539,7 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5 F src/dbpage.c 5808e91bc27fa3981b028000f8fadfdc10ce9e59a34ce7dc4e035a69be3906ec F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e -F src/expr.c a6692ca00680c8ac4ec6cf637e04df1ca331e1a68e311b425eea61f0c8477019 +F src/expr.c 1cbdd76eeedb729ea9060df03e3e6b74a302784a13bfa38794a8194f894641ea F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002 F src/func.c 8f72e88cccdee22185133c10f96ccd61dc34c5ea4b1fa9a73c237ef59b2e64f1 @@ -660,7 +660,7 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 0be191521ff6d2805995f4910f0b6231b42843678b2efdc1abecaf39929a673f F src/vacuum.c bb346170b0b54c6683bba4a5983aea40485597fdf605c87ec8bc2e199fe88cd8 -F src/vdbe.c d27ec9a57f752fc2acf6a64d43bbf6072d2415efc976184f6d8a146e65819d3b +F src/vdbe.c 0d1e3c658d98a7bb7201532ea7a3e4d59bf9165421c780d5f84c361e372f1179 F src/vdbe.h 64619af62603dc3c4f5ff6ff6d2c8f389abd667a29ce6007ed44bd22b3211cd0 F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f F src/vdbeapi.c fc3183daf72808b4311b228989120fdbc2dc44972fb0d77d5c453460cc0e5b2c @@ -2000,8 +2000,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 930a8c1561d0d5dc70dc5378edac77f79ac59ab6569b561de3dfb02f160c38c5 -R dcb98f6a73c54efed4da4f980f298d5b +P 852b385a5de622aa32026824210d4bd23db52a4a8a697b83d22d6000edeba487 +R 58d9f89a36d9e4cf587663a233307943 U drh -Z 1cd83b2593765cf9bcd77e53d7a14363 +Z 7e40ae1a3e52a669a3942bf4dda137ff # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ac64a7f2b0..97e708724c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -852b385a5de622aa32026824210d4bd23db52a4a8a697b83d22d6000edeba487 \ No newline at end of file +1292d68caa7086610ddda343f3852e63de8da1eb66536ee4716b6529f5a31bc6 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 064e78fdea..baa0fe6476 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3223,7 +3223,7 @@ void sqlite3CodeRhsOfIN( sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } if( addrOnce ){ - sqlite3VdbeAddOp2(v, OP_Rewind, iTab, sqlite3VdbeCurrentAddr(v)+1); + sqlite3VdbeAddOp1(v, OP_NullRow, iTab); sqlite3VdbeJumpHere(v, addrOnce); /* Subroutine return */ assert( ExprUseYSub(pExpr) ); diff --git a/src/vdbe.c b/src/vdbe.c index 686ebf175f..ebf52e67af 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -4897,12 +4897,16 @@ case OP_SeekHit: { /* Opcode: IfNotOpen P1 P2 * * * ** Synopsis: if( !csr[P1] ) goto P2 ** -** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through. +** If cursor P1 is not open or if P1 is set to a NULL row using the +** OP_NullRow opcode, then jump to instruction P2. Otherwise, fall through. */ case OP_IfNotOpen: { /* jump */ + VdbeCursor *pCur; + assert( pOp->p1>=0 && pOp->p1nCursor ); - VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2); - if( !p->apCsr[pOp->p1] ){ + pCur = p->apCsr[pOp->p1]; + VdbeBranchTaken(pCur==0 || pCur->nullRow, 2); + if( pCur==0 || pCur->nullRow ){ goto jump_to_p2_and_check_for_interrupt; } break; From 4c5c941b6ebf92a2d72eb31b4db0a4da195ab7f6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 19:45:04 +0000 Subject: [PATCH 4/7] New test cases for query flattening when there are ON clauses and outer joins. FossilOrigin-Name: db55387b1972bc7bc6855bd3497af744a111e7d28d09893cd985fbb01be41bcc --- manifest | 12 +++++------ manifest.uuid | 2 +- test/join.test | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 45f45fb98f..feef4be417 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Modify\sthe\sOP_IfNotOpen\sopcode\sso\sthat\sthe\sjump\sis\staken\sif\sthe\scursor\sis\sopen\nbut\swas\spreviously\sset\sto\sa\sNULL\srow\susing\sOP_NullRow. -D 2022-09-20T19:22:17.805 +C New\stest\scases\sfor\squery\sflattening\swhen\sthere\sare\sON\sclauses\sand\souter\sjoins. +D 2022-09-20T19:45:04.738 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1180,7 +1180,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 21dbc65ab2476b10ae3ed1dcf64a99fb9a40473caa22f4cec2d6a829933c1442 +F test/join.test 41203c37d22e27b6b7211e3b1b875b6f0fc3c9fb2ea8fe31d3a8683ae3cf1a45 F test/join2.test 466b07233820f5deee66a6c3bf6e4500c8bbf7b83649e67606f5f649c07928c0 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 @@ -2000,8 +2000,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 852b385a5de622aa32026824210d4bd23db52a4a8a697b83d22d6000edeba487 -R 58d9f89a36d9e4cf587663a233307943 +P 1292d68caa7086610ddda343f3852e63de8da1eb66536ee4716b6529f5a31bc6 +R eab6511b05ba7254ebaf6e3422ee1bf8 U drh -Z 7e40ae1a3e52a669a3942bf4dda137ff +Z 0a097f1ec50fb2c86025df8ed078e191 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 97e708724c..e88b85fddc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1292d68caa7086610ddda343f3852e63de8da1eb66536ee4716b6529f5a31bc6 \ No newline at end of file +db55387b1972bc7bc6855bd3497af744a111e7d28d09893cd985fbb01be41bcc \ No newline at end of file diff --git a/test/join.test b/test/join.test index 82dbd447f9..8e7267d410 100644 --- a/test/join.test +++ b/test/join.test @@ -1130,5 +1130,63 @@ do_execsql_test join-27.5 { ) AS t99 ON b IN (1,2,3); } {} +db null NULL +do_execsql_test join-27.6 { + INSERT INTO t1 VALUES(3,4,NULL); + INSERT INTO t2 VALUES(1,2); + WITH t99(b) AS ( + SELECT coalesce(b,3) FROM t2 AS x LEFT JOIN t1 ON c IN (SELECT x FROM t3) + ) + SELECT d, e, b FROM t2 JOIN t99 ON b IN (1,2,3) ORDER BY +d; +} {NULL NULL 3 NULL NULL 3 1 2 3 1 2 3} +do_execsql_test join-27.7 { + SELECT d, e, b2 + FROM t2 + JOIN (SELECT coalesce(b,3) AS b2 FROM t2 AS x LEFT JOIN t1 + ON c IN (SELECT x FROM t3)) AS t99 + ON b2 IN (1,2,3) ORDER BY +d; +} {NULL NULL 3 NULL NULL 3 1 2 3 1 2 3} + +do_execsql_test join-27.8 { + DELETE FROM t1; + DELETE FROM t2 WHERE d IS NOT NULL; + DELETE FROM t3; + SELECT * FROM t2 JOIN (SELECT b FROM t2 LEFT JOIN t1 + ON c IN (SELECT x FROM t3)) AS t99 ON b IN (1,2,3); +} {} + +# 2022-09-19 https://sqlite.org/forum/forumpost/96b9e5709cf47cda +# Performance regression relative to version 3.38.0 that resulted from +# a new query flattener restriction that was added to fixes the join-27.* +# tests above. The restriction needed to be removed and the join-27.* +# problem fixed another way. +# +reset_db +do_execsql_test join-28.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT); + CREATE TABLE t2(d INTEGER PRIMARY KEY, e INT); + CREATE VIEW t3(a,b,c,d,e) AS SELECT * FROM t1 LEFT JOIN t2 ON d=c; + CREATE TABLE t4(x INT, y INT); + INSERT INTO t1 VALUES(1,2,3); + INSERT INTO t2 VALUES(1,5); + INSERT INTO t4 VALUES(1,4); + SELECT a, b, y FROM t4 JOIN t3 ON a=x; +} {1 2 4} +do_eqp_test join-28.2 { + SELECT a, b, y FROM t4 JOIN t3 ON a=x; +} { + QUERY PLAN + |--SCAN t4 + `--SEARCH t1 USING INTEGER PRIMARY KEY (rowid=?) +} +# ^^^^^^^ Without the fix (if the query flattening optimization does not +# run) the query plan above would look like this: +# +# QUERY PLAN +# |--MATERIALIZE t3 +# | |--SCAN t1 +# | `--SEARCH t2 USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN +# |--SCAN t4 +# `--SEARCH t3 USING AUTOMATIC COVERING INDEX (a=?) finish_test From 0cc6f78bcf416ab86be67ff91fa35964d3e76e6e Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 21:07:52 +0000 Subject: [PATCH 5/7] Add new test cases that (current) get an incorrect answer. FossilOrigin-Name: fe5c37736444e397ace387b761ec2491229785d7a437f3f60232aedf9d1f00ec --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/join.test | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index feef4be417..154099b58f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\sfor\squery\sflattening\swhen\sthere\sare\sON\sclauses\sand\souter\sjoins. -D 2022-09-20T19:45:04.738 +C Add\snew\stest\scases\sthat\s(current)\sget\san\sincorrect\sanswer. +D 2022-09-20T21:07:52.356 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1180,7 +1180,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 41203c37d22e27b6b7211e3b1b875b6f0fc3c9fb2ea8fe31d3a8683ae3cf1a45 +F test/join.test 37d48f2d22688e44bd858b27738265e4408919de40dbc85591217090a44e03c7 F test/join2.test 466b07233820f5deee66a6c3bf6e4500c8bbf7b83649e67606f5f649c07928c0 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 @@ -2000,8 +2000,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 1292d68caa7086610ddda343f3852e63de8da1eb66536ee4716b6529f5a31bc6 -R eab6511b05ba7254ebaf6e3422ee1bf8 +P db55387b1972bc7bc6855bd3497af744a111e7d28d09893cd985fbb01be41bcc +R 07f8452158d8de57be929257c37c037f U drh -Z 0a097f1ec50fb2c86025df8ed078e191 +Z 2388624530fdd8581cf3228402a79d3a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e88b85fddc..2e52f3b523 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -db55387b1972bc7bc6855bd3497af744a111e7d28d09893cd985fbb01be41bcc \ No newline at end of file +fe5c37736444e397ace387b761ec2491229785d7a437f3f60232aedf9d1f00ec \ No newline at end of file diff --git a/test/join.test b/test/join.test index 8e7267d410..790e2ee01b 100644 --- a/test/join.test +++ b/test/join.test @@ -1155,6 +1155,24 @@ do_execsql_test join-27.8 { ON c IN (SELECT x FROM t3)) AS t99 ON b IN (1,2,3); } {} +do_execsql_test join-27.9 { + DELETE FROM t1; + DELETE FROM t2; + DELETE FROM t3; + INSERT INTO t1 VALUES(4,3,5); + INSERT INTO t2 VALUES(1,2); + INSERT INTO t3 VALUES(5); + SELECT * FROM t2 JOIN (SELECT b FROM t2 LEFT JOIN t1 + ON c IN (SELECT x FROM t3)) AS t99 ON b IS NULL; +} {} +do_execsql_test join-27.10 { + WITH t99(b) AS ( + SELECT coalesce(b,3) FROM t2 AS x LEFT JOIN t1 ON c IN (SELECT x FROM t3) + ) + SELECT d, e, b FROM t2 JOIN t99 ON b IN (1,2,3); +} {} + + # 2022-09-19 https://sqlite.org/forum/forumpost/96b9e5709cf47cda # Performance regression relative to version 3.38.0 that resulted from # a new query flattener restriction that was added to fixes the join-27.* From 0f51aa3972c4699edbc30314af10da731c4bf805 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 22:19:13 +0000 Subject: [PATCH 6/7] Do not allow EP_InnerON terms to be used on a outer join. FossilOrigin-Name: f47aa745690c018800243bf76930b2499ff4537411c4e27b4b16ba3854cc2bf8 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 5 +++++ test/join.test | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 154099b58f..34a771310d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\stest\scases\sthat\s(current)\sget\san\sincorrect\sanswer. -D 2022-09-20T21:07:52.356 +C Do\snot\sallow\sEP_InnerON\sterms\sto\sbe\sused\son\sa\souter\sjoin. +D 2022-09-20T22:19:13.998 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -675,7 +675,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c 424c42590b71968a9b81cd890df2671902028613fee38a50ed4c2f7ca65315d3 +F src/where.c 075de11bb3a7be19cfe27e104d52e19d30096925ef2524341bf5a6bfcdbc4127 F src/whereInt.h 70cd30de9ed784aa33fa6bd1245f060617de7a00d992469b6d8e419eed915743 F src/wherecode.c 6bb1cf9d0a4e3e04dab0bf0ea4a8d936a0dcc05a7e2207beeda6c61aea6dd341 F src/whereexpr.c 55a39f42aaf982574fbf52906371a84cceed98a994422198dfd03db4fce4cc46 @@ -1180,7 +1180,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 37d48f2d22688e44bd858b27738265e4408919de40dbc85591217090a44e03c7 +F test/join.test e32cb9b1491eed682489e2cde33a22a4eb7611fe5aa3b0aa4b275fe27ab3f3ac F test/join2.test 466b07233820f5deee66a6c3bf6e4500c8bbf7b83649e67606f5f649c07928c0 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 @@ -2000,8 +2000,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 db55387b1972bc7bc6855bd3497af744a111e7d28d09893cd985fbb01be41bcc -R 07f8452158d8de57be929257c37c037f +P fe5c37736444e397ace387b761ec2491229785d7a437f3f60232aedf9d1f00ec +R b7bb9d0f74cad7fddb9b6178bd7d10bc U drh -Z 2388624530fdd8581cf3228402a79d3a +Z 62c9c805d4ceaa75bf6aed42a5148e09 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2e52f3b523..0c5527bedd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fe5c37736444e397ace387b761ec2491229785d7a437f3f60232aedf9d1f00ec \ No newline at end of file +f47aa745690c018800243bf76930b2499ff4537411c4e27b4b16ba3854cc2bf8 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 3e90fa9a7c..2105c4c035 100644 --- a/src/where.c +++ b/src/where.c @@ -2876,6 +2876,11 @@ static int whereLoopAddBtreeIndex( ){ continue; } + if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 + && ExprHasProperty(pTerm->pExpr, EP_InnerON) + ){ + continue; + } } if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ diff --git a/test/join.test b/test/join.test index 790e2ee01b..f0ac0be8b1 100644 --- a/test/join.test +++ b/test/join.test @@ -1167,9 +1167,9 @@ do_execsql_test join-27.9 { } {} do_execsql_test join-27.10 { WITH t99(b) AS ( - SELECT coalesce(b,3) FROM t2 AS x LEFT JOIN t1 ON c IN (SELECT x FROM t3) + SELECT b FROM t2 AS x LEFT JOIN t1 ON c IN (SELECT x FROM t3) ) - SELECT d, e, b FROM t2 JOIN t99 ON b IN (1,2,3); + SELECT d, e, b FROM t2 JOIN t99 ON b IS NULL; } {} From faaacd3fbb6088b1faedf1536b2366db0fb87ea3 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 21 Sep 2022 00:16:59 +0000 Subject: [PATCH 7/7] Break out the decision of whether or not a constraint term is usable by an outer join into a subroutine: constraintCompatibleWithOuterJoin(). FossilOrigin-Name: c7fbc9b0453050e2746af27e3a11e0c3701bef8c56da8e19173242c6ea3aff8b --- manifest | 12 ++++---- manifest.uuid | 2 +- src/where.c | 81 ++++++++++++++++++++++++++++----------------------- 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/manifest b/manifest index 34a771310d..40c1bb24e9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sallow\sEP_InnerON\sterms\sto\sbe\sused\son\sa\souter\sjoin. -D 2022-09-20T22:19:13.998 +C Break\sout\sthe\sdecision\sof\swhether\sor\snot\sa\sconstraint\sterm\sis\susable\sby\nan\souter\sjoin\sinto\sa\ssubroutine:\sconstraintCompatibleWithOuterJoin(). +D 2022-09-21T00:16:59.294 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -675,7 +675,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c 075de11bb3a7be19cfe27e104d52e19d30096925ef2524341bf5a6bfcdbc4127 +F src/where.c 3774d9831a6f4ac81f201fa9e2ec1bb40c99b7207dce7663c2e39fb73cc1bfd5 F src/whereInt.h 70cd30de9ed784aa33fa6bd1245f060617de7a00d992469b6d8e419eed915743 F src/wherecode.c 6bb1cf9d0a4e3e04dab0bf0ea4a8d936a0dcc05a7e2207beeda6c61aea6dd341 F src/whereexpr.c 55a39f42aaf982574fbf52906371a84cceed98a994422198dfd03db4fce4cc46 @@ -2000,8 +2000,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 fe5c37736444e397ace387b761ec2491229785d7a437f3f60232aedf9d1f00ec -R b7bb9d0f74cad7fddb9b6178bd7d10bc +P f47aa745690c018800243bf76930b2499ff4537411c4e27b4b16ba3854cc2bf8 +R ffb250842cc4325c51d9fb2f2a7cd199 U drh -Z 62c9c805d4ceaa75bf6aed42a5148e09 +Z 35e62397d6a559d77545ee94797c3cd6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0c5527bedd..dc64e5b341 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f47aa745690c018800243bf76930b2499ff4537411c4e27b4b16ba3854cc2bf8 \ No newline at end of file +c7fbc9b0453050e2746af27e3a11e0c3701bef8c56da8e19173242c6ea3aff8b \ No newline at end of file diff --git a/src/where.c b/src/where.c index 2105c4c035..5811d830d1 100644 --- a/src/where.c +++ b/src/where.c @@ -743,6 +743,38 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ #define whereTraceIndexInfoOutputs(A) #endif +/* +** We know that pSrc is an operand of an outer join. Return true if +** pTerm is a constraint that is compatible with that join. +** +** pTerm must be EP_OuterON if pSrc is the right operand of an +** outer join. pTerm can be either EP_OuterON or EP_InnerON if pSrc +** is the left operand of a RIGHT join. +*/ +static int constraintCompatibleWithOuterJoin( + const WhereTerm *pTerm, /* WHERE clause term to check */ + const SrcItem *pSrc /* Table we are trying to access */ +){ + assert( (pSrc->fg.jointype&(JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ); /* By caller */ + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) + || pTerm->pExpr->w.iJoin != pSrc->iCursor + ){ + return 0; + } + if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 + && ExprHasProperty(pTerm->pExpr, EP_InnerON) + ){ + return 0; + } + return 1; +} + + + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Return TRUE if the WHERE clause term pTerm is of a form where it @@ -758,16 +790,10 @@ static int termCanDriveIndex( if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); - testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) - testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); - if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) - || pTerm->pExpr->w.iJoin != pSrc->iCursor - ){ - return 0; /* See tag-20191211-001 */ - } + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ + return 0; } if( (pTerm->prereqRight & notReady)!=0 ) return 0; assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); @@ -1184,17 +1210,10 @@ static sqlite3_index_info *allocateIndexInfo( ** right-hand table of a LEFT JOIN nor to the either table of a ** RIGHT JOIN. See tag-20191211-001 for the ** equivalent restriction for ordinary tables. */ - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT ); - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); - testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ); - testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); - if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) - || pTerm->pExpr->w.iJoin != pSrc->iCursor - ){ - continue; - } + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ + continue; } nTerm++; pTerm->wtFlags |= TERM_OK; @@ -2865,22 +2884,10 @@ static int whereLoopAddBtreeIndex( ** 2022-06-10: The same condition applies to termCanDriveIndex() above. ** https://sqlite.org/forum/forumpost/51e6959f61 */ - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT ); - testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); - testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) - testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); - if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) - || pTerm->pExpr->w.iJoin != pSrc->iCursor - ){ - continue; - } - if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 - && ExprHasProperty(pTerm->pExpr, EP_InnerON) - ){ - continue; - } + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ + continue; } if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){