mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Add the sqlite3_stmt_explain() API.
FossilOrigin-Name: 0443c0ef85f8f6f3efdee68bf59126c9459f08a58458756f8cbeeaf043d43a86
This commit is contained in:
29
manifest
29
manifest
@@ -1,5 +1,5 @@
|
||||
C Minor\stweaks\sto\sthe\sJSON\sperformance\smeasurement\sdocumentation.
|
||||
D 2023-07-28T16:12:39.671
|
||||
C Add\sthe\ssqlite3_stmt_explain()\sAPI.
|
||||
D 2023-07-28T18:37:13.439
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -632,20 +632,20 @@ F src/os_win.c 7038223a1cda0a47e2ab4db47f63bf1833fe53ba0542f0f283a062ea13894103
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 5ddf3a74c633a008ea6b2f5b3186167e88e2c8ca8a252ecab06ab3f1eb48e60f
|
||||
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
|
||||
F src/parse.y 8828f9e15f04d469eab9c0f2aed504e534b1c97c68836bed6f07afab29c2ac0b
|
||||
F src/parse.y aeb7760d41cfa86465e3adba506500c021597049fd55f82a30e5b7045862c28c
|
||||
F src/pcache.c 4cd4a0043167da9ba7e19b4d179a0e6354e7fe32c16f781ecf9bf0a5ff63b40b
|
||||
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
|
||||
F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
|
||||
F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a
|
||||
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1
|
||||
F src/prepare.c 80548297dc0e1fb3139cdebffb5a1bcac3dfac66d791012dd74838e70445072d
|
||||
F src/printf.c e3ba080e2f409f9bfcc8d34724e6fc160e9c718dc92d0548f6b71b8b6f860ce2
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c 3ab1186290a311a8ceed1286c0e286209f7fe97b2d02c7593258004ce295dd88
|
||||
F src/shell.c.in 62b58d7be1e44ec3eb729411278b079c39949e683853445323fd3f250b674a29
|
||||
F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774
|
||||
F src/select.c 3328b8c758016b400a4fea053ab1f5927929083a49d0c3921802ce99eea73cde
|
||||
F src/shell.c.in 694aaf751f00610381533d4a31c83d142cfc83ef91ef65e2aa6912ace7c39b40
|
||||
F src/sqlite.h.in 7b95e5b11914726ebcf95c633be3341ad2cb3c8aac62ecf188a14f74827aa68a
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4
|
||||
F src/sqliteInt.h 6140eb4058d995f3004ee2732fa098b884f26eb497a7fbb3a51c781ec824d14b
|
||||
@@ -653,7 +653,7 @@ F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee
|
||||
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
F src/tclsqlite.c ecbc3c99c0d0c3ed122a913f143026c26d38d57f33e06bb71185dd5c1efe37cd
|
||||
F src/test1.c 9111b12427b3b94429aff68bbcf5b125ae70438f6ce2f3f033e2a69626ec26c9
|
||||
F src/test1.c dfd07574689ee7c57af219c5f627ff32dae65dba478c124ba0359a1bde9983be
|
||||
F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
|
||||
F src/test3.c e5178558c41ff53236ae0271e9acb3d6885a94981d2eb939536ee6474598840e
|
||||
F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
|
||||
@@ -715,9 +715,9 @@ F src/util.c c2aa170f2eb429235b1dddce8952770787ffa5124dc89d405bfbe8ebad8e7ebd
|
||||
F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
|
||||
F src/vdbe.c cd0396758da6a95e6c4e3bec7c3d6e767c3b39930c295c2425f83bb086cdc6ba
|
||||
F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
|
||||
F src/vdbeInt.h c30ef736774d876f923b42758b9210fe456104b39906e3901d21279443a17d47
|
||||
F src/vdbeapi.c dde6c4d0f87486f056b9db4d1ea185bb1d84a6839102b86e76316ba590d07cc7
|
||||
F src/vdbeaux.c 23f17d418d5b97138b7dbbd4c84d80c66dc7990653c705436c8162103b3cc9ba
|
||||
F src/vdbeInt.h cd829ff67fc18aff2476fe3209262ba8671cb43d51cb6913de4a670e37d2fbe3
|
||||
F src/vdbeapi.c 0cba9fe6e860e46bfb5e45a76a8c2c6ccd5ebc8f954319e4288e7793c2eca88c
|
||||
F src/vdbeaux.c a586f445945eef6ad1fcd7c94f700faa1baea93c0dbd446291c7cf65966c8470
|
||||
F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce
|
||||
F src/vdbemem.c adb5877dc54330865e45e0fba50f6455c1d9d8b83434c59f0e3d2d2a0b9f0e58
|
||||
F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015
|
||||
@@ -2049,8 +2049,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P f9213289d09adfb0461d9644e80c7e889f4bae51563ae2e575a2c95336052bcb
|
||||
R fb3fd77e80d1642b3cf0d0d40a08f14e
|
||||
P 0bed957e46aa3bf6a70292ae100de0459486c1469dd03de61207a708cc59a594 06eae046c1a1155f22590b88942db72f01482f2363da9753f8adfdb322679055
|
||||
R f4937c6da6b1777faf7c4e05e694ac0d
|
||||
T +closed 06eae046c1a1155f22590b88942db72f01482f2363da9753f8adfdb322679055
|
||||
U drh
|
||||
Z 4e3e52a362d42e532a27b931dd696fd5
|
||||
Z ff4a633a0dc8165d247380ac3053876f
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@@ -1 +1 @@
|
||||
0bed957e46aa3bf6a70292ae100de0459486c1469dd03de61207a708cc59a594
|
||||
0443c0ef85f8f6f3efdee68bf59126c9459f08a58458756f8cbeeaf043d43a86
|
@@ -148,8 +148,8 @@ ecmd ::= SEMI.
|
||||
ecmd ::= cmdx SEMI.
|
||||
%ifndef SQLITE_OMIT_EXPLAIN
|
||||
ecmd ::= explain cmdx SEMI. {NEVER-REDUCE}
|
||||
explain ::= EXPLAIN. { pParse->explain = 1; }
|
||||
explain ::= EXPLAIN QUERY PLAN. { pParse->explain = 2; }
|
||||
explain ::= EXPLAIN. { if( pParse->pReprepare==0 ) pParse->explain = 1; }
|
||||
explain ::= EXPLAIN QUERY PLAN. { if( pParse->pReprepare==0 ) pParse->explain = 2; }
|
||||
%endif SQLITE_OMIT_EXPLAIN
|
||||
cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
|
||||
|
||||
|
@@ -700,7 +700,12 @@ static int sqlite3Prepare(
|
||||
sParse.pOuterParse = db->pParse;
|
||||
db->pParse = &sParse;
|
||||
sParse.db = db;
|
||||
if( pReprepare ){
|
||||
sParse.pReprepare = pReprepare;
|
||||
sParse.explain = sqlite3_stmt_isexplain((sqlite3_stmt*)pReprepare);
|
||||
}else{
|
||||
assert( sParse.pReprepare==0 );
|
||||
}
|
||||
assert( ppStmt && *ppStmt==0 );
|
||||
if( db->mallocFailed ){
|
||||
sqlite3ErrorMsg(&sParse, "out of memory");
|
||||
|
@@ -2101,13 +2101,6 @@ void sqlite3GenerateColumnNames(
|
||||
int fullName; /* TABLE.COLUMN if no AS clause and is a direct table ref */
|
||||
int srcName; /* COLUMN or TABLE.COLUMN if no AS clause and is direct */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
/* If this is an EXPLAIN, skip this step */
|
||||
if( pParse->explain ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( pParse->colNamesSet ) return;
|
||||
/* Column names are determined by the left-most term of a compound select */
|
||||
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
|
||||
|
@@ -4368,16 +4368,15 @@ static int shell_exec(
|
||||
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
|
||||
if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
|
||||
sqlite3_stmt *pExplain;
|
||||
char *zEQP;
|
||||
int triggerEQP = 0;
|
||||
disable_debug_trace_modes();
|
||||
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP);
|
||||
if( pArg->autoEQP>=AUTOEQP_trigger ){
|
||||
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
|
||||
}
|
||||
zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql);
|
||||
shell_check_oom(zEQP);
|
||||
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
|
||||
pExplain = pStmt;
|
||||
sqlite3_reset(pExplain);
|
||||
rc = sqlite3_stmt_explain(pExplain, 2);
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pExplain)==SQLITE_ROW ){
|
||||
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
|
||||
@@ -4389,13 +4388,10 @@ static int shell_exec(
|
||||
}
|
||||
eqp_render(pArg, 0);
|
||||
}
|
||||
sqlite3_finalize(pExplain);
|
||||
sqlite3_free(zEQP);
|
||||
if( pArg->autoEQP>=AUTOEQP_full ){
|
||||
/* Also do an EXPLAIN for ".eqp full" mode */
|
||||
zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql);
|
||||
shell_check_oom(zEQP);
|
||||
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
|
||||
sqlite3_reset(pExplain);
|
||||
rc = sqlite3_stmt_explain(pExplain, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
pArg->cMode = MODE_Explain;
|
||||
assert( sqlite3_stmt_isexplain(pExplain)==1 );
|
||||
@@ -4403,16 +4399,12 @@ static int shell_exec(
|
||||
exec_prepared_stmt(pArg, pExplain);
|
||||
explain_data_delete(pArg);
|
||||
}
|
||||
sqlite3_finalize(pExplain);
|
||||
sqlite3_free(zEQP);
|
||||
}
|
||||
if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
|
||||
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
|
||||
/* Reprepare pStmt before reactivating trace modes */
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( pArg ) pArg->pStmt = pStmt;
|
||||
}
|
||||
sqlite3_reset(pStmt);
|
||||
sqlite3_stmt_explain(pStmt, 0);
|
||||
restore_debug_trace_modes();
|
||||
}
|
||||
|
||||
|
@@ -4421,6 +4421,41 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
||||
*/
|
||||
int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement
|
||||
** METHOD: sqlite3_stmt
|
||||
**
|
||||
** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN
|
||||
** setting for prepared statement S. If E is zero, then S becomes
|
||||
** a normal prepared statement. If E is 1, then S behaves as if
|
||||
** its SQL text began with "EXPLAIN". If E is 2, then S behaves as if
|
||||
** its SQL text began with "EXPLAIN QUERY PLAN".
|
||||
**
|
||||
** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared.
|
||||
** SQLite tries to avoid a reprepare, but a reprepare might be necessary
|
||||
** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode.
|
||||
**
|
||||
** Because of the potential need to reprepare, a call to
|
||||
** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be
|
||||
** reprepared because it was created using sqlite3_prepare() instead of
|
||||
** the newer sqlite_prepare_v2() or sqlite3_prepare_v3() interfaces and
|
||||
** hence has no saved SQL text with which to reprepare.
|
||||
**
|
||||
** Changing the explain setting for a prepared statement does not change
|
||||
** the original SQL text for the statement. Hence, if the SQL text originally
|
||||
** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0)
|
||||
** is called to convert the statement into an ordinary statement, the EXPLAIN
|
||||
** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S)
|
||||
** output, even though the statement now acts like a normal SQL statement.
|
||||
**
|
||||
** This routine returns SQLITE_OK if the explain mode is successfully
|
||||
** changed, or an error code if the explain mode could not be changed.
|
||||
** The explain mode cannot be changed while a statement is active.
|
||||
** Hence, it is good practice to call [sqlite3_reset(S)]
|
||||
** immediately prior to calling sqlite3_stmt_explain(S,E).
|
||||
*/
|
||||
int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
|
||||
** METHOD: sqlite3_stmt
|
||||
|
29
src/test1.c
29
src/test1.c
@@ -2922,6 +2922,34 @@ static int SQLITE_TCLAPI test_stmt_isexplain(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_stmt_explain STMT INT
|
||||
**
|
||||
** Set the explain to normal (0), EXPLAIN (1) or EXPLAIN QUERY PLAN (2).
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_stmt_explain(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int eMode = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " STMT INT", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &eMode) ) return TCL_ERROR;
|
||||
rc = sqlite3_stmt_explain(pStmt, eMode);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_stmt_busy STMT
|
||||
**
|
||||
@@ -8991,6 +9019,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_next_stmt", test_next_stmt ,0 },
|
||||
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
|
||||
{ "sqlite3_stmt_isexplain", test_stmt_isexplain,0 },
|
||||
{ "sqlite3_stmt_explain", test_stmt_explain ,0 },
|
||||
{ "sqlite3_stmt_busy", test_stmt_busy ,0 },
|
||||
{ "uses_stmt_journal", uses_stmt_journal ,0 },
|
||||
|
||||
|
@@ -481,16 +481,18 @@ struct Vdbe {
|
||||
u32 nWrite; /* Number of write operations that have occurred */
|
||||
#endif
|
||||
u16 nResColumn; /* Number of columns in one row of the result set */
|
||||
u16 nResAlloc; /* Column slots allocated to aColName[] */
|
||||
u8 errorAction; /* Recovery action to do in case of an error */
|
||||
u8 minWriteFileFormat; /* Minimum file format for writable database files */
|
||||
u8 prepFlags; /* SQLITE_PREPARE_* flags */
|
||||
u8 eVdbeState; /* On of the VDBE_*_STATE values */
|
||||
bft expired:2; /* 1: recompile VM immediately 2: when convenient */
|
||||
bft explain:2; /* True if EXPLAIN present on SQL command */
|
||||
bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */
|
||||
bft changeCntOn:1; /* True to update the change-counter */
|
||||
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 */
|
||||
bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */
|
||||
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
|
||||
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
|
||||
u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */
|
||||
|
@@ -1127,7 +1127,8 @@ int sqlite3_aggregate_count(sqlite3_context *p){
|
||||
*/
|
||||
int sqlite3_column_count(sqlite3_stmt *pStmt){
|
||||
Vdbe *pVm = (Vdbe *)pStmt;
|
||||
return pVm ? pVm->nResColumn : 0;
|
||||
if( pVm==0 ) return 0;
|
||||
return pVm->nResColumn;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1300,6 +1301,32 @@ int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
|
||||
return iType;
|
||||
}
|
||||
|
||||
/*
|
||||
** Column names appropriate for EXPLAIN or EXPLAIN QUERY PLAN.
|
||||
*/
|
||||
static const char * const azExplainColNames8[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", /* EXPLAIN */
|
||||
"id", "parent", "notused", "detail" /* EQP */
|
||||
};
|
||||
static const u16 azExplainColNames16data[] = {
|
||||
/* 0 */ 'a', 'd', 'd', 'r', 0,
|
||||
/* 5 */ 'o', 'p', 'c', 'o', 'd', 'e', 0,
|
||||
/* 12 */ 'p', '1', 0,
|
||||
/* 15 */ 'p', '2', 0,
|
||||
/* 18 */ 'p', '3', 0,
|
||||
/* 21 */ 'p', '4', 0,
|
||||
/* 24 */ 'p', '5', 0,
|
||||
/* 27 */ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0,
|
||||
/* 35 */ 'i', 'd', 0,
|
||||
/* 38 */ 'p', 'a', 'r', 'e', 'n', 't', 0,
|
||||
/* 45 */ 'n', 'o', 't', 'u', 's', 'e', 'd', 0,
|
||||
/* 53 */ 'd', 'e', 't', 'a', 'i', 'l', 0
|
||||
};
|
||||
static const u8 iExplainColNames16[] = {
|
||||
0, 5, 12, 15, 18, 21, 24, 27,
|
||||
35, 38, 45, 53
|
||||
};
|
||||
|
||||
/*
|
||||
** Convert the N-th element of pStmt->pColName[] into a string using
|
||||
** xFunc() then return that string. If N is out of range, return 0.
|
||||
@@ -1332,15 +1359,29 @@ static const void *columnName(
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if( N<0 ) return 0;
|
||||
ret = 0;
|
||||
p = (Vdbe *)pStmt;
|
||||
db = p->db;
|
||||
assert( db!=0 );
|
||||
n = sqlite3_column_count(pStmt);
|
||||
if( N<n && N>=0 ){
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
if( p->explain ){
|
||||
if( useType>0 ) goto columnName_end;
|
||||
n = p->explain==1 ? 8 : 4;
|
||||
if( N>=n ) goto columnName_end;
|
||||
if( useUtf16 ){
|
||||
int i = iExplainColNames16[N + 8*p->explain - 8];
|
||||
ret = (void*)&azExplainColNames16data[i];
|
||||
}else{
|
||||
ret = (void*)azExplainColNames8[N + 8*p->explain - 8];
|
||||
}
|
||||
goto columnName_end;
|
||||
}
|
||||
n = p->nResColumn;
|
||||
if( N<n ){
|
||||
u8 prior_mallocFailed = db->mallocFailed;
|
||||
N += useType*n;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( useUtf16 ){
|
||||
ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]);
|
||||
@@ -1357,8 +1398,9 @@ static const void *columnName(
|
||||
sqlite3OomClear(db);
|
||||
ret = 0;
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
}
|
||||
columnName_end:
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1815,6 +1857,35 @@ int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
|
||||
return pStmt ? ((Vdbe*)pStmt)->explain : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the explain mode for a statement.
|
||||
*/
|
||||
int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
int rc;
|
||||
if( v->eVdbeState!=VDBE_READY_STATE ) return SQLITE_BUSY;
|
||||
if( v->explain==eMode ) return SQLITE_OK;
|
||||
if( v->zSql==0 || eMode<0 || eMode>2 ) return SQLITE_ERROR;
|
||||
sqlite3_mutex_enter(v->db->mutex);
|
||||
if( v->nMem>=10 && (eMode!=2 || v->haveEqpOps) ){
|
||||
/* No reprepare necessary */
|
||||
v->explain = eMode;
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
int haveEqpOps = v->explain==2 || v->haveEqpOps;
|
||||
v->explain = eMode;
|
||||
rc = sqlite3Reprepare(v);
|
||||
v->haveEqpOps = haveEqpOps!=0;
|
||||
}
|
||||
if( v->explain ){
|
||||
v->nResColumn = 12 - 4*v->explain;
|
||||
}else{
|
||||
v->nResColumn = v->nResAlloc;
|
||||
}
|
||||
sqlite3_mutex_leave(v->db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the prepared statement is in need of being reset.
|
||||
*/
|
||||
|
@@ -2418,7 +2418,7 @@ int sqlite3VdbeList(
|
||||
sqlite3VdbeMemSetInt64(pMem+1, pOp->p2);
|
||||
sqlite3VdbeMemSetInt64(pMem+2, pOp->p3);
|
||||
sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free);
|
||||
p->nResColumn = 4;
|
||||
assert( p->nResColumn==4 );
|
||||
}else{
|
||||
sqlite3VdbeMemSetInt64(pMem+0, i);
|
||||
sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode),
|
||||
@@ -2437,7 +2437,7 @@ int sqlite3VdbeList(
|
||||
sqlite3VdbeMemSetNull(pMem+7);
|
||||
#endif
|
||||
sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free);
|
||||
p->nResColumn = 8;
|
||||
assert( p->nResColumn==8 );
|
||||
}
|
||||
p->pResultRow = pMem;
|
||||
if( db->mallocFailed ){
|
||||
@@ -2651,26 +2651,9 @@ void sqlite3VdbeMakeReady(
|
||||
resolveP2Values(p, &nArg);
|
||||
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
|
||||
if( pParse->explain ){
|
||||
static const char * const azColName[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
|
||||
"id", "parent", "notused", "detail"
|
||||
};
|
||||
int iFirst, mx, i;
|
||||
if( nMem<10 ) nMem = 10;
|
||||
p->explain = pParse->explain;
|
||||
if( pParse->explain==2 ){
|
||||
sqlite3VdbeSetNumCols(p, 4);
|
||||
iFirst = 8;
|
||||
mx = 12;
|
||||
}else{
|
||||
sqlite3VdbeSetNumCols(p, 8);
|
||||
iFirst = 0;
|
||||
mx = 8;
|
||||
}
|
||||
for(i=iFirst; i<mx; i++){
|
||||
sqlite3VdbeSetColName(p, i-iFirst, COLNAME_NAME,
|
||||
azColName[i], SQLITE_STATIC);
|
||||
}
|
||||
p->nResColumn = 12 - 4*p->explain;
|
||||
}
|
||||
p->expired = 0;
|
||||
|
||||
@@ -2839,12 +2822,12 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
|
||||
int n;
|
||||
sqlite3 *db = p->db;
|
||||
|
||||
if( p->nResColumn ){
|
||||
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
|
||||
if( p->nResAlloc ){
|
||||
releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N);
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
}
|
||||
n = nResColumn*COLNAME_N;
|
||||
p->nResColumn = (u16)nResColumn;
|
||||
p->nResColumn = p->nResAlloc = (u16)nResColumn;
|
||||
p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n );
|
||||
if( p->aColName==0 ) return;
|
||||
initMemArray(p->aColName, n, db, MEM_Null);
|
||||
@@ -2869,14 +2852,14 @@ int sqlite3VdbeSetColName(
|
||||
){
|
||||
int rc;
|
||||
Mem *pColName;
|
||||
assert( idx<p->nResColumn );
|
||||
assert( idx<p->nResAlloc );
|
||||
assert( var<COLNAME_N );
|
||||
if( p->db->mallocFailed ){
|
||||
assert( !zName || xDel!=SQLITE_DYNAMIC );
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
assert( p->aColName!=0 );
|
||||
pColName = &(p->aColName[idx+var*p->nResColumn]);
|
||||
pColName = &(p->aColName[idx+var*p->nResAlloc]);
|
||||
rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel);
|
||||
assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 );
|
||||
return rc;
|
||||
@@ -3700,7 +3683,7 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
|
||||
assert( db!=0 );
|
||||
assert( p->db==0 || p->db==db );
|
||||
if( p->aColName ){
|
||||
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
|
||||
releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N);
|
||||
sqlite3DbNNFreeNN(db, p->aColName);
|
||||
}
|
||||
for(pSub=p->pProgram; pSub; pSub=pNext){
|
||||
|
Reference in New Issue
Block a user