Index: src/shell.c.in
==================================================================
--- src/shell.c.in
+++ src/shell.c.in
@@ -1770,11 +1770,11 @@
}
/*
** Display and reset the EXPLAIN QUERY PLAN data
*/
-static void eqp_render(ShellState *p){
+static void eqp_render(ShellState *p, sqlite3_stmt *pStmt){
EQPGraphRow *pRow = p->sGraph.pRow;
if( pRow ){
if( pRow->zText[0]=='-' ){
if( pRow->pNext==0 ){
eqp_reset(p);
@@ -1782,11 +1782,14 @@
}
utf8_printf(p->out, "%s\n", pRow->zText+3);
p->sGraph.pRow = pRow->pNext;
sqlite3_free(pRow);
}else{
- utf8_printf(p->out, "QUERY PLAN\n");
+ int iCost, nRow;
+ iCost = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_EST_COST, 0);
+ nRow = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_EST_ROWS, 0);
+ utf8_printf(p->out, "QUERY PLAN (log est cost=%d rows=%d)\n", iCost, nRow);
}
p->sGraph.zPrefix[0] = 0;
eqp_render_level(p, 0);
eqp_reset(p);
}
@@ -3073,14 +3076,14 @@
if( rc==SQLITE_OK ){
while( sqlite3_step(pExplain)==SQLITE_ROW ){
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
int iEqpId = sqlite3_column_int(pExplain, 0);
int iParentId = sqlite3_column_int(pExplain, 1);
- if( zEQPLine[0]=='-' ) eqp_render(pArg);
+ if( zEQPLine[0]=='-' ) eqp_render(pArg, pExplain);
eqp_append(pArg, iEqpId, iParentId, zEQPLine);
}
- eqp_render(pArg);
+ eqp_render(pArg, pExplain);
}
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
if( pArg->autoEQP>=AUTOEQP_full ){
/* Also do an EXPLAIN for ".eqp full" mode */
@@ -3124,11 +3127,11 @@
}
bind_prepared_stmt(pArg, pStmt);
exec_prepared_stmt(pArg, pStmt);
explain_data_delete(pArg);
- eqp_render(pArg);
+ eqp_render(pArg, pStmt);
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
display_stats(db, pArg, 0);
}
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -7845,10 +7845,19 @@
** [[SQLITE_STMTSTATUS_MEMUSED]]
SQLITE_STMTSTATUS_MEMUSED
** ^This is the approximate number of bytes of heap memory
** used to store the prepared statement. ^This value is not actually
** a counter, and so the resetFlg parameter to sqlite3_stmt_status()
** is ignored when the opcode is SQLITE_STMTSTATUS_MEMUSED.
+**
+** [[SQLITE_STMTSTATUS_EST_ROWS]] SQLITE_STMTSTATUS_EST_ROWS
+** ^A return value of X indicates that the query planner estimated
+** that the query will return pow(2,X/10.0) rows.
+**
+** [[SQLITE_STMTSTATUS_EST_COST]] SQLITE_STMTSTATUS_EST_COST
+** ^A return value of X indicates that the query planner estimated
+** the relative cost of running this statement to completion is
+** pow(2,X/10.0).
**
**
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
#define SQLITE_STMTSTATUS_SORT 2
@@ -7855,10 +7864,12 @@
#define SQLITE_STMTSTATUS_AUTOINDEX 3
#define SQLITE_STMTSTATUS_VM_STEP 4
#define SQLITE_STMTSTATUS_REPREPARE 5
#define SQLITE_STMTSTATUS_RUN 6
#define SQLITE_STMTSTATUS_MEMUSED 99
+#define SQLITE_STMTSTATUS_EST_ROWS 100
+#define SQLITE_STMTSTATUS_EST_COST 101
/*
** CAPI3REF: Custom Page Cache Object
**
** The sqlite3_pcache type is opaque. It is implemented by
Index: src/vdbe.h
==================================================================
--- src/vdbe.h
+++ src/vdbe.h
@@ -262,10 +262,11 @@
#endif
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8);
void sqlite3VdbeSetVarmask(Vdbe*, int);
+void sqlite3VdbeUpdateCostEstimates(Parse*, LogEst, LogEst);
#ifndef SQLITE_OMIT_TRACE
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3BlobCompare(const Mem*, const Mem*);
Index: src/vdbeInt.h
==================================================================
--- src/vdbeInt.h
+++ src/vdbeInt.h
@@ -423,10 +423,12 @@
bft changeCntOn:1; /* True to update the change-counter */
bft runOnlyOnce:1; /* Automatically expire on reset */
bft usesStmtJournal:1; /* True if uses a statement journal */
bft readOnly:1; /* True for statements that do not write */
bft bIsReader:1; /* True for statements that read */
+ LogEst nRowEst; /* Query planner of estimated number of output rows */
+ LogEst iCostEst; /* Query planner cost estimate */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */
char *zSql; /* Text of the SQL statement that generated this */
#ifdef SQLITE_ENABLE_NORMALIZE
Index: src/vdbeapi.c
==================================================================
--- src/vdbeapi.c
+++ src/vdbeapi.c
@@ -1656,31 +1656,43 @@
/*
** Return the value of a status counter for a prepared statement
*/
int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
Vdbe *pVdbe = (Vdbe*)pStmt;
- u32 v;
-#ifdef SQLITE_ENABLE_API_ARMOR
- if( !pStmt
- || (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter)))
- ){
+ u32 v = 0;
+ if( !pStmt ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
-#endif
- if( op==SQLITE_STMTSTATUS_MEMUSED ){
- sqlite3 *db = pVdbe->db;
- sqlite3_mutex_enter(db->mutex);
- v = 0;
- db->pnBytesFreed = (int*)&v;
- sqlite3VdbeClearObject(db, pVdbe);
- sqlite3DbFree(db, pVdbe);
- db->pnBytesFreed = 0;
- sqlite3_mutex_leave(db->mutex);
- }else{
- v = pVdbe->aCounter[op];
- if( resetFlag ) pVdbe->aCounter[op] = 0;
+ switch( op ){
+ case SQLITE_STMTSTATUS_MEMUSED: {
+ sqlite3 *db = pVdbe->db;
+ sqlite3_mutex_enter(db->mutex);
+ v = 0;
+ db->pnBytesFreed = (int*)&v;
+ sqlite3VdbeClearObject(db, pVdbe);
+ sqlite3DbFree(db, pVdbe);
+ db->pnBytesFreed = 0;
+ sqlite3_mutex_leave(db->mutex);
+ break;
+ }
+ case SQLITE_STMTSTATUS_EST_ROWS: {
+ v = pVdbe->nRowEst;
+ break;
+ }
+ case SQLITE_STMTSTATUS_EST_COST: {
+ v = pVdbe->iCostEst;
+ break;
+ }
+ default: {
+ if( op>=0 && opaCounter) ){
+ v = pVdbe->aCounter[op];
+ if( resetFlag ) pVdbe->aCounter[op] = 0;
+ }else{
+ (void)SQLITE_MISUSE_BKPT;
+ }
+ }
}
return (int)v;
}
/*
Index: src/vdbeaux.c
==================================================================
--- src/vdbeaux.c
+++ src/vdbeaux.c
@@ -4845,10 +4845,24 @@
v->expmask |= 0x80000000;
}else{
v->expmask |= ((u32)1 << (iVar-1));
}
}
+
+/*
+** Update the estimated cost fields
+*/
+void sqlite3VdbeUpdateCostEstimates(Parse *pParse, LogEst iCost, LogEst nRow){
+ Vdbe *v = pParse->pVdbe;
+ if( v->iCostEst ){
+ v->iCostEst = sqlite3LogEstAdd(v->iCostEst, iCost+pParse->nQueryLoop) + 1;
+ if( nRow > v->nRowEst ) v->nRowEst = nRow;
+ }else{
+ v->nRowEst = nRow;
+ v->iCostEst = iCost + 1;
+ }
+}
/*
** Cause a function to throw an error if it was call from OP_PureFunc
** rather than OP_Function.
**
Index: src/where.c
==================================================================
--- src/where.c
+++ src/where.c
@@ -4361,10 +4361,11 @@
}
}
pWInfo->nRowOut = pFrom->nRow;
+ pWInfo->iTotalCost = pFrom->rCost;
/* Free temporary memory and return success */
sqlite3DbFreeNN(db, pSpace);
return SQLITE_OK;
}
@@ -5143,10 +5144,11 @@
SrcList *pTabList = pWInfo->pTabList;
sqlite3 *db = pParse->db;
/* Generate loop termination code.
*/
+ sqlite3VdbeUpdateCostEstimates(pParse, pWInfo->iTotalCost, pWInfo->nRowOut);
VdbeModuleComment((v, "End WHERE-core"));
for(i=pWInfo->nLevel-1; i>=0; i--){
int addr;
pLevel = &pWInfo->a[i];
pLoop = pLevel->pWLoop;
Index: src/whereInt.h
==================================================================
--- src/whereInt.h
+++ src/whereInt.h
@@ -460,10 +460,11 @@
u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
int iTop; /* The very beginning of the WHERE loop */
WhereLoop *pLoops; /* List of all WhereLoop objects */
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
LogEst nRowOut; /* Estimated number of output rows */
+ LogEst iTotalCost; /* Cost estimate for the whole plan */
WhereClause sWC; /* Decomposition of the WHERE clause */
WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
WhereLevel a[1]; /* Information about each nest loop in WHERE */
};