From: drh Date: Tue, 9 Feb 2016 18:39:25 +0000 (+0000) Subject: Add auto-explain mode to the command-line shell. Default on. Auto-explain X-Git-Tag: version-3.11.0~29^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=700c252a726ffa142704197ac12ccd34127a6850;p=thirdparty%2Fsqlite.git Add auto-explain mode to the command-line shell. Default on. Auto-explain tries to automatically detect EXPLAIN queries and format them appropriately. FossilOrigin-Name: 1d62aa6b315df47cafb33da7ca79d3386a2fdd48 --- diff --git a/manifest b/manifest index 972cf74353..4e297feee8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sMSVC\smakefile\soptions\sthat\senable\scontrol-flow\sguard. -D 2016-02-09T18:28:20.640 +C Add\sauto-explain\smode\sto\sthe\scommand-line\sshell.\s\sDefault\son.\s\sAuto-explain\ntries\sto\sautomatically\sdetect\sEXPLAIN\squeries\sand\sformat\sthem\sappropriately. +D 2016-02-09T18:39:25.001 F Makefile.in 95ea52e9c02962e31f986fe8ea5805104c84f94b F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 0fe3b22f8e29bcde0533ada7957a5f15835d797a @@ -349,7 +349,7 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d -F src/shell.c dcd7a83645ef2a58ee9c6d0ea4714d877d7835c4 +F src/shell.c eae68d3a7aff0f4195074d5f204dc2c219e748fd F src/sqlite.h.in cf22ad1d52dca2c9862d63833e581028119aab7e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d @@ -1427,7 +1427,10 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 992282399cd2d1fc52ff5a1a4bff59f30b62899a -R 339c4dc7dafb8a9f839919868c327cb9 -U mistachkin -Z 87542794a9bfaf638d39bc9a323993e0 +P 51b6823f4c9376d549f572f5a33cac1e4c9783a2 +R 38fbc467636eda7f2b73a6fc60969ca8 +T *branch * auto-explain +T *sym-auto-explain * +T -sym-trunk * +U drh +Z 2feb45f9e3784b1a58bde6f86e718a1d diff --git a/manifest.uuid b/manifest.uuid index 59daec470f..6a13da9877 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -51b6823f4c9376d549f572f5a33cac1e4c9783a2 \ No newline at end of file +1d62aa6b315df47cafb33da7ca79d3386a2fdd48 \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 3f8b22f4fa..c45b28b999 100644 --- a/src/shell.c +++ b/src/shell.c @@ -592,6 +592,7 @@ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ int echoOn; /* True to echo input commands */ + int autoExplain; /* Automatically turn on .explain mode */ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ int statsOn; /* True to display memory stats before each finalize */ int scanstatsOn; /* True to display scan stats before each finalize */ @@ -603,6 +604,8 @@ struct ShellState { FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ + int cMode; /* temporary output mode for the current query */ + int normalMode; /* Output mode before ".explain on" */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ @@ -613,7 +616,6 @@ struct ShellState { int actualWidth[100]; /* Actual width of each column */ char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ - SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ char *zFreeOnClose; /* Filename to free when closing */ @@ -882,7 +884,7 @@ static int shell_callback( int i; ShellState *p = (ShellState*)pArg; - switch( p->mode ){ + switch( p->cMode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; @@ -899,11 +901,24 @@ static int shell_callback( } case MODE_Explain: case MODE_Column: { + static const int aExplainWidths[] = {4, 13, 4, 4, 4, 13, 2, 13}; + const int *colWidth; + int showHdr; + char *rowSep; + if( p->cMode==MODE_Column ){ + colWidth = p->colWidth; + showHdr = p->showHeader; + rowSep = p->rowSeparator; + }else{ + colWidth = aExplainWidths; + showHdr = 1; + rowSep = "\n"; + } if( p->cnt++==0 ){ for(i=0; icolWidth) ){ - w = p->colWidth[i]; + w = colWidth[i]; }else{ w = 0; } @@ -916,17 +931,17 @@ static int shell_callback( if( iactualWidth) ){ p->actualWidth[i] = w; } - if( p->showHeader ){ + if( showHdr ){ if( w<0 ){ utf8_printf(p->out,"%*.*s%s",-w,-w,azCol[i], - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); }else{ utf8_printf(p->out,"%-*.*s%s",w,w,azCol[i], - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); } } } - if( p->showHeader ){ + if( showHdr ){ for(i=0; iactualWidth) ){ @@ -938,7 +953,7 @@ static int shell_callback( utf8_printf(p->out,"%-*.*s%s",w,w, "----------------------------------------------------------" "----------------------------------------------------------", - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); } } } @@ -950,7 +965,7 @@ static int shell_callback( }else{ w = 10; } - if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ + if( p->cMode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ w = strlen30(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ @@ -962,11 +977,11 @@ static int shell_callback( if( w<0 ){ utf8_printf(p->out,"%*.*s%s",-w,-w, azArg[i] ? azArg[i] : p->nullValue, - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); }else{ utf8_printf(p->out,"%-*.*s%s",w,w, azArg[i] ? azArg[i] : p->nullValue, - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); } } break; @@ -986,7 +1001,7 @@ static int shell_callback( utf8_printf(p->out, "%s", z); if( iout, "%s", p->colSeparator); - }else if( p->mode==MODE_Semi ){ + }else if( p->cMode==MODE_Semi ){ utf8_printf(p->out, ";%s", p->rowSeparator); }else{ utf8_printf(p->out, "%s", p->rowSeparator); @@ -1614,10 +1629,20 @@ static int shell_exec( sqlite3_free(zEQP); } - /* If the shell is currently in ".explain" mode, gather the extra - ** data required to add indents to the output.*/ - if( pArg && pArg->mode==MODE_Explain ){ - explain_data_prepare(pArg, pStmt); + if( pArg ){ + pArg->cMode = pArg->mode; + if( sqlite3_column_count(pStmt)==8 + && sqlite3_strlike("%EXPLAIN%", sqlite3_sql(pStmt),0)==0 + && sqlite3_strlike("%QUERY%", sqlite3_sql(pStmt),0)!=0 + ){ + pArg->cMode = MODE_Explain; + } + + /* If the shell is currently in ".explain" mode, gather the extra + ** data required to add indents to the output.*/ + if( pArg->cMode==MODE_Explain ){ + explain_data_prepare(pArg, pStmt); + } } /* perform the first step. this will tell us if we @@ -1647,7 +1672,7 @@ static int shell_exec( /* extract the data and data types */ for(i=0; imode==MODE_Insert ){ + if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ azVals[i] = ""; }else{ azVals[i] = (char*)sqlite3_column_text(pStmt, i); @@ -1867,8 +1892,7 @@ static char zHelp[] = ".echo on|off Turn command echo on or off\n" ".eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN\n" ".exit Exit this program\n" - ".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n" - " With no args, it turns EXPLAIN on.\n" + ".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n" ".fullschema Show schema and the content of sqlite_stat tables\n" ".headers on|off Turn display of headers on or off\n" ".help Show this message\n" @@ -2855,7 +2879,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 1; - data.mode = MODE_Column; + data.cMode = data.mode = MODE_Column; data.colWidth[0] = 3; data.colWidth[1] = 15; data.colWidth[2] = 58; @@ -2950,37 +2974,24 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ - int val = nArg>=2 ? booleanValue(azArg[1]) : 1; - if(val == 1) { - if(!p->normalMode.valid) { - p->normalMode.valid = 1; - p->normalMode.mode = p->mode; - p->normalMode.showHeader = p->showHeader; - memcpy(p->normalMode.colWidth,p->colWidth,sizeof(p->colWidth)); + int val = 1; + if( nArg>=2 ){ + if( strcmp(azArg[1],"auto")==0 ){ + val = 99; + }else{ + val = booleanValue(azArg[1]); } - /* We could put this code under the !p->explainValid - ** condition so that it does not execute if we are already in - ** explain mode. However, always executing it allows us an easy - ** was to reset to explain mode in case the user previously - ** did an .explain followed by a .width, .mode or .header - ** command. - */ + } + if( val==1 && p->mode!=MODE_Explain ){ + p->normalMode = p->mode; p->mode = MODE_Explain; - p->showHeader = 1; - memset(p->colWidth,0,sizeof(p->colWidth)); - p->colWidth[0] = 4; /* addr */ - p->colWidth[1] = 13; /* opcode */ - p->colWidth[2] = 4; /* P1 */ - p->colWidth[3] = 4; /* P2 */ - p->colWidth[4] = 4; /* P3 */ - p->colWidth[5] = 13; /* P4 */ - p->colWidth[6] = 2; /* P5 */ - p->colWidth[7] = 13; /* Comment */ - }else if (p->normalMode.valid) { - p->normalMode.valid = 0; - p->mode = p->normalMode.mode; - p->showHeader = p->normalMode.showHeader; - memcpy(p->colWidth,p->normalMode.colWidth,sizeof(p->colWidth)); + p->autoExplain = 0; + }else if( val==0 ){ + if( p->mode==MODE_Explain ) p->mode = p->normalMode; + p->autoExplain = 0; + }else if( val==99 ){ + if( p->mode==MODE_Explain ) p->mode = p->normalMode; + p->autoExplain = 1; } }else @@ -2996,7 +3007,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; - data.mode = MODE_Semi; + data.cMode = data.mode = MODE_Semi; rc = sqlite3_exec(p->db, "SELECT sql FROM" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" @@ -3021,7 +3032,7 @@ static int do_meta_command(char *zLine, ShellState *p){ raw_printf(p->out, "ANALYZE sqlite_master;\n"); sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", callback, &data, &zErrMsg); - data.mode = MODE_Insert; + data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(p->db, "SELECT * FROM sqlite_stat1", shell_callback, &data,&zErrMsg); @@ -3253,7 +3264,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; - data.mode = MODE_List; + data.cMode = data.mode = MODE_List; if( nArg==1 ){ rc = sqlite3_exec(p->db, "SELECT name FROM sqlite_master " @@ -3439,6 +3450,7 @@ static int do_meta_command(char *zLine, ShellState *p){ "ascii column csv html insert line list tabs tcl\n"); rc = 1; } + p->cMode = p->mode; }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ @@ -3628,7 +3640,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; - data.mode = MODE_Semi; + data.cMode = data.mode = MODE_Semi; if( nArg==2 ){ int i; for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]); @@ -3776,7 +3788,8 @@ static int do_meta_command(char *zLine, ShellState *p){ } utf8_printf(p->out, "%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); utf8_printf(p->out, "%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); - utf8_printf(p->out,"%9.9s: %s\n","explain",p->normalMode.valid?"on":"off"); + utf8_printf(p->out, "%12.12s: %s\n","explain", + p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); utf8_printf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); utf8_printf(p->out, "%12.12s: ", "nullvalue"); @@ -4575,7 +4588,8 @@ static void usage(int showDetail){ */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); - data->mode = MODE_List; + data->normalMode = data->cMode = data->mode = MODE_List; + data->autoExplain = 1; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; @@ -4908,6 +4922,7 @@ int SQLITE_CDECL main(int argc, char **argv){ raw_printf(stderr,"Use -help for a list of options.\n"); return 1; } + data.cMode = data.mode; } if( !readStdin ){