From: drh Date: Fri, 26 Apr 2019 17:20:33 +0000 (+0000) Subject: An experimental interface for retrieving the estimated cost and estimated X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ac2fa465e9e9f264ab876d0df360a485ffbbbe15;p=thirdparty%2Fsqlite.git An experimental interface for retrieving the estimated cost and estimated number of output rows for a query. FossilOrigin-Name: 1b25fa108ab2c4ada75935abf919de2b4c3b39553b2a0ab2a485645a02352e7e --- diff --git a/manifest b/manifest index 261ca06463..f9983c512a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\sin\stest/fuzzdata8.db. -D 2019-04-24T17:04:02.548 +C An\sexperimental\sinterface\sfor\sretrieving\sthe\sestimated\scost\sand\sestimated\nnumber\sof\soutput\srows\sfor\sa\squery. +D 2019-04-26T17:20:33.254 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -519,8 +519,8 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8 F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93 F src/select.c b7304d2f491c11a03a7fbdf34bc218282ac54052377809d4dc3b4b1e7f4bfc93 -F src/shell.c.in bcfa17eb257bf8dc2359e99ba7e6bdfab7901705db013bc47a5be6d7fa7a037e -F src/sqlite.h.in 38390767acc1914d58930e03149595ee4710afa4e3c43ab6c3a8aea3f1a6b8cd +F src/shell.c.in 7544d68921f2c3919da2150c1f7b53a4942c212dd59e68b7926fd903101c3cab +F src/sqlite.h.in 0c4ec0ea145005e2a458b892a8e6778d980efcd2ad36a68565b8774712eb79c1 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5 F src/sqliteInt.h 866311ac436c0c2039fccc7ea976fbc79d40c1c2ea687161fa4ba64379b53ae6 @@ -591,10 +591,10 @@ F src/utf.c 2f0fac345c7660d5c5bd3df9e9d8d33d4c27f366bcfb09e07443064d751a0507 F src/util.c 5061987401c2e8003177fa30d73196aa036727c8f04bf36a2df0c82b1904a236 F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf F src/vdbe.c 711ef421b3bb3db3b2476067b2dc3c71ef5844d9b1a723026578f89f6da621e8 -F src/vdbe.h 712bca562eaed1c25506b9faf9680bdc75fc42e2f4a1cd518d883fa79c7a4237 -F src/vdbeInt.h 2c12704db9740c8e899786ecfc7a5797a9d067563496eb1b6ed03c592d7b8d90 -F src/vdbeapi.c 2ddd60f4a351f15ee98d841e346af16111ad59dfa4d25d2dd4012e9875bf7d92 -F src/vdbeaux.c f873b5c2efcf8a4d6ecfc5b1a5b06fd810419198f3bd882175d371cc03801873 +F src/vdbe.h f99dbc42943d945f2aa61377dc67e571688310be51dbc9b3e4299bd0124fdd70 +F src/vdbeInt.h a9089cdf0d5f4a1f076d5b22e5690b6ee2f70b1db0a4814ae1a6397c497c838b +F src/vdbeapi.c 69fae8eb6e1e762e04bfce308f8180294f98a95f7b346f6cb3a1364fd8095cdd +F src/vdbeaux.c cf9159eaf4b2ac4d0c0daa61544055d9db924989d433c1f77e2af61ebdcb6a05 F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191 F src/vdbemem.c dd2ee49255c4c5450f2b0887ef44cea8faa1cd7a46501b39a1a82b113ae418e3 F src/vdbesort.c 66592d478dbb46f19aed0b42222325eadb84deb40a90eebe25c6e7c1d8468f47 @@ -604,8 +604,8 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b09a2a9cab50efa08451a8c81d47052120ad5da174048c6d0b08d405384abdf2 F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a F src/walker.c 7607f1a68130c028255d8d56094ea602fc402c79e1e35a46e6282849d90d5fe4 -F src/where.c 99c7b718ef846ac952016083aaf4e22ede2290beceaf4730a2df55c023251369 -F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88 +F src/where.c 3e9689df25c0410dc60ee28d81ca21ea4dcdbdd925bff764afea95ef6c105975 +F src/whereInt.h 50e1ddaae281e056eb71d8209ffc194e730745fb521fa8f22d0867cc34e9f3d7 F src/wherecode.c 0e76672930bea322eb3606d891a4744be55c09bcd3a995bfd501af62a46e0625 F src/whereexpr.c 90859652920f153d2c03f075488744be2926625ebd36911bcbcb17d0d29c891c F src/window.c 038c248267e74ff70a2bb9b1884d40fd145c5183b017823ecb6cbb14bc781478 @@ -1818,7 +1818,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e1724f1d618cfbcfd1e495d8965a395656cfc1114e1bffd4bc3be0bd5cdb6550 -R 3e1175ca2cd3d699a0989ce182ed2c32 +P 7be6222c9ec44596e4eddd906c831eb1272b90fbdf68641d791f216264feb7cf +R f445bfaab7875d7e9d6792f1def24978 +T *branch * cost-est +T *sym-cost-est * +T -sym-trunk * U drh -Z b7a4bb53a6677ebeb4f1be668326c373 +Z b1b37fe7015f8bb5921c7a5e02cfd8b8 diff --git a/manifest.uuid b/manifest.uuid index 7b9871a25b..575253ef18 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7be6222c9ec44596e4eddd906c831eb1272b90fbdf68641d791f216264feb7cf \ No newline at end of file +1b25fa108ab2c4ada75935abf919de2b4c3b39553b2a0ab2a485645a02352e7e \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 1f57d1ec3d..adce32512c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1772,7 +1772,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){ /* ** 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]=='-' ){ @@ -1784,7 +1784,10 @@ static void eqp_render(ShellState *p){ 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); @@ -3075,10 +3078,10 @@ static int shell_exec( 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); @@ -3126,7 +3129,7 @@ static int shell_exec( 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 ){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index cf390ac372..105bc8a201 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -7847,6 +7847,15 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); ** 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). **
** */ @@ -7857,6 +7866,8 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); #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 diff --git a/src/vdbe.h b/src/vdbe.h index 041a91c51f..639f3e1e54 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -264,6 +264,7 @@ 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 diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 15a371d550..a19ae1187a 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -425,6 +425,8 @@ struct Vdbe { 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() */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 00eea75a45..eba342b74c 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -1658,27 +1658,39 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ */ 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; } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 822c6fe60c..d2b58aec8a 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -4848,6 +4848,20 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ } } +/* +** 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. diff --git a/src/where.c b/src/where.c index b421e4ae93..81fc9158ae 100644 --- a/src/where.c +++ b/src/where.c @@ -4363,6 +4363,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pWInfo->nRowOut = pFrom->nRow; + pWInfo->iTotalCost = pFrom->rCost; /* Free temporary memory and return success */ sqlite3DbFreeNN(db, pSpace); @@ -5145,6 +5146,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* 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; diff --git a/src/whereInt.h b/src/whereInt.h index 07876f4353..6728732c20 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -462,6 +462,7 @@ struct WhereInfo { 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 */