1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Be more aggressive about reusing subqueries that appear on the RHS of IN

operators that have been replicated due to the predicate push-down optimization.

FossilOrigin-Name: 2accf32b6e45a396503c29eecc14a103bcc7b4c313cde921b26b489704060177
This commit is contained in:
drh
2024-07-04 16:57:11 +00:00
parent 6357d35da9
commit 0cf237c5b0
6 changed files with 116 additions and 37 deletions

View File

@@ -3420,6 +3420,46 @@ void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){
}
}
#ifndef SQLITE_OMIT_SUBQUERY
/*
** Scan all previously generated bytecode looking for an OP_BeginSubrtn
** that is compatible with pExpr. If found, add the y.sub values
** to pExpr and return true. If not found, return false.
*/
static int findCompatibleInRhsSubrtn(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* IN operator with RHS that we want to reuse */
SubrtnSig *pNewSig /* Signature for the IN operator */
){
VdbeOp *pOp, *pEnd;
SubrtnSig *pSig;
Vdbe *v;
if( pNewSig==0 ) return 0;
assert( pExpr->op==TK_IN );
assert( !ExprUseYSub(pExpr) );
assert( ExprUseXSelect(pExpr) );
v = pParse->pVdbe;
assert( v!=0 );
pOp = sqlite3VdbeGetOp(v, 1);
pEnd = sqlite3VdbeGetLastOp(v);
for(; pOp<pEnd; pOp++){
if( pOp->opcode!=OP_BeginSubrtn ) continue;
if( pOp->p4type!=P4_SUBRTNSIG ) continue;
pSig = pOp->p4.pSubrtnSig;
assert( pSig!=0 );
if( pNewSig->selId!=pSig->selId ) continue;
if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue;
pExpr->y.sub.iAddr = pSig->iAddr;
pExpr->y.sub.regReturn = pSig->regReturn;
pExpr->iTable = pSig->iTable;
ExprSetProperty(pExpr, EP_Subrtn);
return 1;
}
return 0;
}
#endif /* SQLITE_OMIT_SUBQUERY */
#ifndef SQLITE_OMIT_SUBQUERY
/*
** Generate code that will construct an ephemeral table containing all terms
@@ -3469,11 +3509,30 @@ void sqlite3CodeRhsOfIN(
** and reuse it many names.
*/
if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){
/* Reuse of the RHS is allowed */
/* If this routine has already been coded, but the previous code
** might not have been invoked yet, so invoke it now as a subroutine.
/* Reuse of the RHS is allowed
**
** Compute a signature for the RHS of the IN operator to facility
** finding and reusing prior instances of the same IN operator.
*/
if( ExprHasProperty(pExpr, EP_Subrtn) ){
SubrtnSig *pSig;
if( !ExprUseXSelect(pExpr) ){
pSig = 0;
}else{
assert( pExpr->x.pSelect!=0 );
pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0]));
if( pSig ){
pSig->selId = pExpr->x.pSelect->selId;
pSig->zAff = exprINAffinity(pParse, pExpr);
}
}
/* Check to see if there is a prior materialization of the RHS of
** this IN operator. If there is, then make use of that prior
** materialization rather than recomputing it.
*/
if( ExprHasProperty(pExpr, EP_Subrtn)
|| findCompatibleInRhsSubrtn(pParse, pExpr, pSig)
){
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
if( ExprUseXSelect(pExpr) ){
ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d",
@@ -3485,6 +3544,10 @@ void sqlite3CodeRhsOfIN(
assert( iTab!=pExpr->iTable );
sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable);
sqlite3VdbeJumpHere(v, addrOnce);
if( pSig ){
sqlite3DbFree(pParse->db, pSig->zAff);
sqlite3DbFree(pParse->db, pSig);
}
return;
}
@@ -3495,7 +3558,12 @@ void sqlite3CodeRhsOfIN(
pExpr->y.sub.regReturn = ++pParse->nMem;
pExpr->y.sub.iAddr =
sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1;
if( pSig ){
pSig->iAddr = pExpr->y.sub.iAddr;
pSig->regReturn = pExpr->y.sub.regReturn;
pSig->iTable = iTab;
sqlite3VdbeChangeP4(v, -1, (const char*)pSig, P4_SUBRTNSIG);
}
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}