mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Experimental change to explain query plan to identify covering indexes on expressions.
FossilOrigin-Name: 3bb03a2891e30c58b66e3665a8877a8eab4a8bac57ee153d8d31358caeaf4b7c
This commit is contained in:
24
src/where.c
24
src/where.c
@ -7441,14 +7441,28 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pOp->p2 = x;
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
OpcodeRewriteTrace(db, k, pOp);
|
||||
}else{
|
||||
/* Unable to translate the table reference into an index
|
||||
** reference. Verify that this is harmless - that the
|
||||
** table being referenced really is open.
|
||||
*/
|
||||
}else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
|
||||
if( pLoop->wsFlags & WHERE_IDX_ONLY ){
|
||||
/* An error. pLoop is supposed to be a covering index loop,
|
||||
** and yet the VM code refers to a column of the table that
|
||||
** is not part of the index. */
|
||||
sqlite3ErrorMsg(pParse, "internal query planner error");
|
||||
pParse->rc = SQLITE_INTERNAL;
|
||||
}else{
|
||||
/* The WHERE_EXPRIDX flag is set by the planner when it is likely
|
||||
** that pLoop is a covering index loop, but it is not possible
|
||||
** to be 100% sure. In this case, any OP_Explain opcode
|
||||
** corresponding to this loop describes the index as a "COVERING
|
||||
** INDEX". But, pOp proves that pLoop is not actually a covering
|
||||
** index loop. So clear the WHERE_EXPRIDX flag and rewrite the
|
||||
** text that accompanies the OP_Explain opcode, if any. */
|
||||
pLoop->wsFlags &= ~WHERE_EXPRIDX;
|
||||
sqlite3WhereAddExplainText(pParse,
|
||||
pLevel->addrBody-1,
|
||||
pTabList,
|
||||
pLevel,
|
||||
pWInfo->wctrlFlags
|
||||
);
|
||||
}
|
||||
}
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
|
@ -533,9 +533,17 @@ int sqlite3WhereExplainBloomFilter(
|
||||
const WhereInfo *pWInfo, /* WHERE clause */
|
||||
const WhereLevel *pLevel /* Bloom filter on this level */
|
||||
);
|
||||
void sqlite3WhereAddExplainText(
|
||||
Parse *pParse, /* Parse context */
|
||||
int addr,
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
);
|
||||
#else
|
||||
# define sqlite3WhereExplainOneScan(u,v,w,x) 0
|
||||
# define sqlite3WhereExplainBloomFilter(u,v,w) 0
|
||||
# define sqlite3WhereAddExplainText(u,v,w,x,y)
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
void sqlite3WhereAddScanStatus(
|
||||
|
@ -110,27 +110,24 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
|
||||
** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
|
||||
** was defined at compile-time. If it is not a no-op, a single OP_Explain
|
||||
** opcode is added to the output to describe the table scan strategy in pLevel.
|
||||
**
|
||||
** If an OP_Explain opcode is added to the VM, its address is returned.
|
||||
** Otherwise, if no OP_Explain is coded, zero is returned.
|
||||
** This function sets the P4 value of an existing OP_Explain opcode to
|
||||
** text describing the loop in pLevel. If the OP_Explain opcode already has
|
||||
** a P4 value, it is freed before it is overwritten.
|
||||
*/
|
||||
int sqlite3WhereExplainOneScan(
|
||||
void sqlite3WhereAddExplainText(
|
||||
Parse *pParse, /* Parse context */
|
||||
int addr, /* Address of OP_Explain opcode */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
){
|
||||
int ret = 0;
|
||||
#if !defined(SQLITE_DEBUG)
|
||||
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
|
||||
#endif
|
||||
{
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
|
||||
|
||||
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
|
||||
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
||||
@ -139,9 +136,10 @@ int sqlite3WhereExplainOneScan(
|
||||
StrAccum str; /* EQP output string */
|
||||
char zBuf[100]; /* Initial space for EQP output string */
|
||||
|
||||
if( db->mallocFailed ) return;
|
||||
|
||||
pLoop = pLevel->pWLoop;
|
||||
flags = pLoop->wsFlags;
|
||||
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0;
|
||||
|
||||
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|
||||
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|
||||
@ -165,7 +163,7 @@ int sqlite3WhereExplainOneScan(
|
||||
zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
|
||||
}else if( flags & WHERE_AUTO_INDEX ){
|
||||
zFmt = "AUTOMATIC COVERING INDEX";
|
||||
}else if( flags & WHERE_IDX_ONLY ){
|
||||
}else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
|
||||
zFmt = "COVERING INDEX %s";
|
||||
}else{
|
||||
zFmt = "INDEX %s";
|
||||
@ -219,9 +217,46 @@ int sqlite3WhereExplainOneScan(
|
||||
#endif
|
||||
zMsg = sqlite3StrAccumFinish(&str);
|
||||
sqlite3ExplainBreakpoint("",zMsg);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
|
||||
pParse->addrExplain, pLoop->rRun,
|
||||
zMsg, P4_DYNAMIC);
|
||||
|
||||
assert( pOp->opcode==OP_Explain );
|
||||
assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 );
|
||||
sqlite3DbFree(db, pOp->p4.z);
|
||||
pOp->p4type = P4_DYNAMIC;
|
||||
pOp->p4.z = sqlite3StrAccumFinish(&str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
|
||||
** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
|
||||
** was defined at compile-time. If it is not a no-op, a single OP_Explain
|
||||
** opcode is added to the output to describe the table scan strategy in pLevel.
|
||||
**
|
||||
** If an OP_Explain opcode is added to the VM, its address is returned.
|
||||
** Otherwise, if no OP_Explain is coded, zero is returned.
|
||||
*/
|
||||
int sqlite3WhereExplainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
){
|
||||
int ret = 0;
|
||||
#if !defined(SQLITE_DEBUG)
|
||||
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
|
||||
#endif
|
||||
{
|
||||
if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0
|
||||
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int addr = sqlite3VdbeCurrentAddr(v);
|
||||
ret = sqlite3VdbeAddOp3(
|
||||
v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun
|
||||
);
|
||||
sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
Reference in New Issue
Block a user