-C cli:\sOmit\ssurplus\swhitespace\sat\sthe\send\sof\slines\sin\s.explain\soutput.
-D 2021-01-13T12:59:20.834
+C Allow\sthe\splanner\sto\sconvert\san\sEXISTS(SELECT...)\sexpression\sin\sa\sWHERE\sclause\sto\sthe\sequivalent\sIN(...)\sexpression\sin\ssituations\swhere\sthis\sis\spossible\sand\sadvantageous.
+D 2021-01-14T20:50:40.571
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/where.c 3d31871d03906312d7d71a9c0b28c97bcbaead7606dfc15f9b3d080b18702385
F src/whereInt.h 9a3f577619f07700d16d89eeb2f3d94d6b7ed7f109c2dacf0ce8844921549506
F src/wherecode.c a3a1aff30fe99a818d8e7c607980f033f40c68d890e03ed25838b9dbb7908bee
-F src/whereexpr.c 3a463e156ea388083c501502229c2c7f4f5c6b5330ea59bdf40d6eb6e155a25f
+F src/whereexpr.c e48e3edea45b4afabbf6f6ece9647734c828a20c49a8cc780ff4d69b42f55fa4
F src/window.c edd6f5e25a1e8f2b6f5305b7f5f7da7bb35f07f0d432b255b1d4c2fcab4205aa
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P db0ecfe66433f8915b6eb16d3735a4a0d0f8e0bbc395bc9c1364387506fc4657
-R 2ad612629a79dbad03e8b7f173945d59
-U drh
-Z f407a95e341e109313c0922f7d2a229b
+P 11e4eb095746602961a178044809a68a77ba7b367596997bef726e54062423d9
+R 1a9ea60bc9bb7dab69ccea9e0b8c40d8
+T *branch * exists-to-in
+T *sym-exists-to-in *
+T -sym-trunk *
+U dan
+Z d4d95c9878f460cd53a2ce8ada6afd2a
return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
}
+/*
+** Expression callback for exprUsesSrclist().
+*/
+static int exprUsesSrclistCb(Walker *p, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN ){
+ SrcList *pSrc = p->u.pSrcList;
+ int iCsr = pExpr->iTable;
+ int ii;
+ for(ii=0; ii<pSrc->nSrc; ii++){
+ if( pSrc->a[ii].iCursor==iCsr ){
+ return p->eCode ? WRC_Abort : WRC_Continue;
+ }
+ }
+ return p->eCode ? WRC_Continue : WRC_Abort;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Select callback for exprUsesSrclist().
+*/
+static int exprUsesSrclistSelectCb(Walker *p, Select *pSelect){
+ return WRC_Abort;
+}
+
+/*
+** This function always returns true if expression pExpr contains
+** a sub-select.
+**
+** If there is no sub-select and bUses is 1, then true is returned
+** if the expression contains at least one TK_COLUMN node that refers
+** to a table in pSrc.
+**
+** Or, if there is no sub-select and bUses is 0, then true is returned
+** if the expression contains at least one TK_COLUMN node that refers
+** to a table that is not in pSrc.
+*/
+static int exprUsesSrclist(SrcList *pSrc, Expr *pExpr, int bUses){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.eCode = bUses;
+ sWalker.u.pSrcList = pSrc;
+ sWalker.xExprCallback = exprUsesSrclistCb;
+ sWalker.xSelectCallback = exprUsesSrclistSelectCb;
+ return (sqlite3WalkExpr(&sWalker, pExpr)==WRC_Abort);
+}
+
+struct ExistsToInCtx {
+ SrcList *pSrc;
+ Expr *pInLhs;
+ Expr *pEq;
+ Expr **ppAnd;
+ Expr **ppParent;
+};
+
+static int exprExistsToInIter(
+ struct ExistsToInCtx *p,
+ Expr *pExpr,
+ Expr **ppExpr
+){
+ assert( ppExpr==0 || *ppExpr==pExpr );
+ switch( pExpr->op ){
+ case TK_AND:
+ p->ppParent = ppExpr;
+ if( exprExistsToInIter(p, pExpr->pLeft, &pExpr->pLeft) ) return 1;
+ p->ppParent = ppExpr;
+ if( exprExistsToInIter(p, pExpr->pRight, &pExpr->pRight) ) return 1;
+ break;
+ case TK_EQ: {
+ int bLeft = exprUsesSrclist(p->pSrc, pExpr->pLeft, 0);
+ int bRight = exprUsesSrclist(p->pSrc, pExpr->pRight, 0);
+ if( bLeft || bRight ){
+ if( (bLeft && bRight) || p->pInLhs ) return 1;
+ p->pInLhs = bLeft ? pExpr->pLeft : pExpr->pRight;
+ p->pEq = pExpr;
+ p->ppAnd = p->ppParent;
+ if( exprUsesSrclist(p->pSrc, p->pInLhs, 1) ) return 1;
+ }
+ break;
+ }
+ default:
+ if( exprUsesSrclist(p->pSrc, pExpr, 0) ){
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static Expr *exprAnalyzeExistsFindEq(
+ SrcList *pSrc,
+ Expr *pWhere, /* WHERE clause to traverse */
+ Expr **ppEq, /* OUT: == node from WHERE clause */
+ Expr ***pppAnd /* OUT: Pointer to parent of ==, if any */
+){
+ struct ExistsToInCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pSrc = pSrc;
+ if( exprExistsToInIter(&ctx, pWhere, 0) ){
+ return 0;
+ }
+ if( ppEq ) *ppEq = ctx.pEq;
+ if( pppAnd ) *pppAnd = ctx.ppAnd;
+ return ctx.pInLhs;
+}
+
+/*
+** Term idxTerm of the WHERE clause passed as the second argument is an
+** EXISTS expression with a correlated SELECT statement on the RHS.
+** This function analyzes the SELECT statement, and if possible adds an
+** equivalent "? IN(SELECT...)" virtual term to the WHERE clause.
+**
+** For an EXISTS term such as the following:
+**
+** EXISTS (SELECT ... FROM <srclist> WHERE <e1> = <e2> AND <e3>)
+**
+** The virtual IN() term added is:
+**
+** <e1> IN (SELECT <e2> FROM <srclist> WHERE <e3>)
+**
+** The virtual term is only added if the following conditions are met:
+**
+** 1. The sub-select must not be an aggregate or use window functions,
+**
+** 2. The sub-select must not be a compound SELECT,
+**
+** 3. Expression <e1> must refer to at least one column from the outer
+** query, and must not refer to any column from the inner query
+** (i.e. from <srclist>).
+**
+** 4. <e2> and <e3> must not refer to any values from the outer query.
+** In other words, once <e1> has been removed, the inner query
+** must not be correlated.
+**
+*/
+static void exprAnalyzeExists(
+ SrcList *pSrc, /* the FROM clause */
+ WhereClause *pWC, /* the WHERE clause */
+ int idxTerm /* Index of the term to be analyzed */
+){
+ Parse *pParse = pWC->pWInfo->pParse;
+ WhereTerm *pTerm = &pWC->a[idxTerm];
+ Expr *pExpr = pTerm->pExpr;
+ Select *pSel = pExpr->x.pSelect;
+ Expr *pDup = 0;
+ Expr *pEq = 0;
+ Expr *pRet = 0;
+ Expr *pInLhs = 0;
+ Expr **ppAnd = 0;
+ int idxNew;
+
+ assert( pExpr->op==TK_EXISTS );
+ assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
+
+ if( (pSel->selFlags & SF_Aggregate) || pSel->pWin ) return;
+ if( pSel->pPrior ) return;
+ if( pSel->pWhere==0 ) return;
+ if( 0==exprAnalyzeExistsFindEq(pSel->pSrc, pSel->pWhere, 0, 0) ) return;
+
+ pDup = sqlite3ExprDup(pParse->db, pExpr, 0);
+ if( pDup==0 ) return;
+ pSel = pDup->x.pSelect;
+ sqlite3ExprListDelete(pParse->db, pSel->pEList);
+ pSel->pEList = 0;
+
+ pInLhs = exprAnalyzeExistsFindEq(pSel->pSrc, pSel->pWhere, &pEq, &ppAnd);
+
+ assert( pDup->pLeft==0 );
+ pDup->op = TK_IN;
+ pDup->pLeft = pInLhs;
+ pDup->flags &= ~EP_VarSelect;
+ pRet = (pInLhs==pEq->pLeft) ? pEq->pRight : pEq->pLeft;
+ pSel->pEList = sqlite3ExprListAppend(pParse, 0, pRet);
+ pEq->pLeft = 0;
+ pEq->pRight = 0;
+ if( ppAnd ){
+ Expr *pAnd = *ppAnd;
+ Expr *pOther = (pAnd->pLeft==pEq) ? pAnd->pRight : pAnd->pLeft;
+ pAnd->pLeft = pAnd->pRight = 0;
+ sqlite3ExprDelete(pParse->db, pAnd);
+ *ppAnd = pOther;
+ }else{
+ assert( pSel->pWhere==pEq );
+ pSel->pWhere = 0;
+ }
+ sqlite3ExprDelete(pParse->db, pEq);
+
+ idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
+ if( idxNew ){
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ pWC->a[idxTerm].wtFlags |= TERM_COPIED;
+ }
+}
+
/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
}
#endif /* SQLITE_ENABLE_STAT4 */
+ if( pExpr->op==TK_EXISTS && (pExpr->flags & EP_VarSelect) ){
+ exprAnalyzeExists(pSrc, pWC, idxTerm);
+ }
+
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
*/