1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-14 00:22:38 +03:00

Enhance the command-line shell to be in auto-explain mode by default. It is

no longer necessary to use the ".explain" command to put the shell into a mode
where the EXPLAIN output is formatted nicely.  That now happens automatically.

FossilOrigin-Name: 751915cb7e4981661a40dc5e4d029ab27434c2d9
This commit is contained in:
drh
2016-02-09 20:11:14 +00:00
3 changed files with 101 additions and 64 deletions

View File

@@ -1,5 +1,5 @@
C Fix\sMSVC\smakefile\soptions\sthat\senable\scontrol-flow\sguard. C Enhance\sthe\scommand-line\sshell\sto\sbe\sin\sauto-explain\smode\sby\sdefault.\s\sIt\sis\nno\slonger\snecessary\sto\suse\sthe\s".explain"\scommand\sto\sput\sthe\sshell\sinto\sa\smode\nwhere\sthe\sEXPLAIN\soutput\sis\sformatted\snicely.\s\sThat\snow\shappens\sautomatically.
D 2016-02-09T18:28:20.640 D 2016-02-09T20:11:14.398
F Makefile.in 95ea52e9c02962e31f986fe8ea5805104c84f94b F Makefile.in 95ea52e9c02962e31f986fe8ea5805104c84f94b
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 0fe3b22f8e29bcde0533ada7957a5f15835d797a F Makefile.msc 0fe3b22f8e29bcde0533ada7957a5f15835d797a
@@ -349,7 +349,7 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c
F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e
F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d
F src/shell.c dcd7a83645ef2a58ee9c6d0ea4714d877d7835c4 F src/shell.c dad82078194d5dae39d35f131e4b60dd3276ab27
F src/sqlite.h.in cf22ad1d52dca2c9862d63833e581028119aab7e F src/sqlite.h.in cf22ad1d52dca2c9862d63833e581028119aab7e
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
@@ -1427,7 +1427,8 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 992282399cd2d1fc52ff5a1a4bff59f30b62899a P 51b6823f4c9376d549f572f5a33cac1e4c9783a2 6c6d7a6e89e67cdb0813d3eebb869aafb43d43ed
R 339c4dc7dafb8a9f839919868c327cb9 R 7efe35d4fa73e934642e7f03926fba5a
U mistachkin T +closed 6c6d7a6e89e67cdb0813d3eebb869aafb43d43ed
Z 87542794a9bfaf638d39bc9a323993e0 U drh
Z 1d6fcdd166387e79c0e00d781ecb3274

View File

@@ -1 +1 @@
51b6823f4c9376d549f572f5a33cac1e4c9783a2 751915cb7e4981661a40dc5e4d029ab27434c2d9

View File

@@ -592,6 +592,7 @@ typedef struct ShellState ShellState;
struct ShellState { struct ShellState {
sqlite3 *db; /* The database */ sqlite3 *db; /* The database */
int echoOn; /* True to echo input commands */ 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 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
int statsOn; /* True to display memory stats before each finalize */ int statsOn; /* True to display memory stats before each finalize */
int scanstatsOn; /* True to display scan 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() */ FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */ int nErr; /* Number of errors seen */
int mode; /* An output mode setting */ 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 writableSchema; /* True if PRAGMA writable_schema=ON */
int showHeader; /* True to show column names in List or Column mode */ int showHeader; /* True to show column names in List or Column mode */
unsigned shellFlgs; /* Various flags */ unsigned shellFlgs; /* Various flags */
@@ -613,7 +616,6 @@ struct ShellState {
int actualWidth[100]; /* Actual width of each column */ int actualWidth[100]; /* Actual width of each column */
char nullValue[20]; /* The text to print when a NULL comes back from char nullValue[20]; /* The text to print when a NULL comes back from
** the database */ ** the database */
SavedModeInfo normalMode;/* Holds the mode just before .explain ON */
char outfile[FILENAME_MAX]; /* Filename for *out */ char outfile[FILENAME_MAX]; /* Filename for *out */
const char *zDbFilename; /* name of the database file */ const char *zDbFilename; /* name of the database file */
char *zFreeOnClose; /* Filename to free when closing */ char *zFreeOnClose; /* Filename to free when closing */
@@ -882,7 +884,7 @@ static int shell_callback(
int i; int i;
ShellState *p = (ShellState*)pArg; ShellState *p = (ShellState*)pArg;
switch( p->mode ){ switch( p->cMode ){
case MODE_Line: { case MODE_Line: {
int w = 5; int w = 5;
if( azArg==0 ) break; if( azArg==0 ) break;
@@ -899,11 +901,24 @@ static int shell_callback(
} }
case MODE_Explain: case MODE_Explain:
case MODE_Column: { 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 ){ if( p->cnt++==0 ){
for(i=0; i<nArg; i++){ for(i=0; i<nArg; i++){
int w, n; int w, n;
if( i<ArraySize(p->colWidth) ){ if( i<ArraySize(p->colWidth) ){
w = p->colWidth[i]; w = colWidth[i];
}else{ }else{
w = 0; w = 0;
} }
@@ -916,17 +931,17 @@ static int shell_callback(
if( i<ArraySize(p->actualWidth) ){ if( i<ArraySize(p->actualWidth) ){
p->actualWidth[i] = w; p->actualWidth[i] = w;
} }
if( p->showHeader ){ if( showHdr ){
if( w<0 ){ if( w<0 ){
utf8_printf(p->out,"%*.*s%s",-w,-w,azCol[i], utf8_printf(p->out,"%*.*s%s",-w,-w,azCol[i],
i==nArg-1 ? p->rowSeparator : " "); i==nArg-1 ? rowSep : " ");
}else{ }else{
utf8_printf(p->out,"%-*.*s%s",w,w,azCol[i], 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; i<nArg; i++){ for(i=0; i<nArg; i++){
int w; int w;
if( i<ArraySize(p->actualWidth) ){ if( i<ArraySize(p->actualWidth) ){
@@ -938,7 +953,7 @@ static int shell_callback(
utf8_printf(p->out,"%-*.*s%s",w,w, 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{ }else{
w = 10; 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]); w = strlen30(azArg[i]);
} }
if( i==1 && p->aiIndent && p->pStmt ){ if( i==1 && p->aiIndent && p->pStmt ){
@@ -962,11 +977,11 @@ static int shell_callback(
if( w<0 ){ if( w<0 ){
utf8_printf(p->out,"%*.*s%s",-w,-w, utf8_printf(p->out,"%*.*s%s",-w,-w,
azArg[i] ? azArg[i] : p->nullValue, azArg[i] ? azArg[i] : p->nullValue,
i==nArg-1 ? p->rowSeparator : " "); i==nArg-1 ? rowSep : " ");
}else{ }else{
utf8_printf(p->out,"%-*.*s%s",w,w, utf8_printf(p->out,"%-*.*s%s",w,w,
azArg[i] ? azArg[i] : p->nullValue, azArg[i] ? azArg[i] : p->nullValue,
i==nArg-1 ? p->rowSeparator : " "); i==nArg-1 ? rowSep : " ");
} }
} }
break; break;
@@ -986,7 +1001,7 @@ static int shell_callback(
utf8_printf(p->out, "%s", z); utf8_printf(p->out, "%s", z);
if( i<nArg-1 ){ if( i<nArg-1 ){
utf8_printf(p->out, "%s", p->colSeparator); utf8_printf(p->out, "%s", p->colSeparator);
}else if( p->mode==MODE_Semi ){ }else if( p->cMode==MODE_Semi ){
utf8_printf(p->out, ";%s", p->rowSeparator); utf8_printf(p->out, ";%s", p->rowSeparator);
}else{ }else{
utf8_printf(p->out, "%s", p->rowSeparator); utf8_printf(p->out, "%s", p->rowSeparator);
@@ -1491,10 +1506,17 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
/* Try to figure out if this is really an EXPLAIN statement. If this /* Try to figure out if this is really an EXPLAIN statement. If this
** cannot be verified, return early. */ ** cannot be verified, return early. */
if( sqlite3_column_count(pSql)!=8 ){
p->cMode = p->mode;
return;
}
zSql = sqlite3_sql(pSql); zSql = sqlite3_sql(pSql);
if( zSql==0 ) return; if( zSql==0 ) return;
for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++);
if( sqlite3_strnicmp(z, "explain", 7) ) return; if( sqlite3_strnicmp(z, "explain", 7) ){
p->cMode = p->mode;
return;
}
for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){
int i; int i;
@@ -1511,6 +1533,20 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
/* Grow the p->aiIndent array as required */ /* Grow the p->aiIndent array as required */
if( iOp>=nAlloc ){ if( iOp>=nAlloc ){
if( iOp==0 ){
/* Do further verfication that this is explain output. Abort if
** it is not */
static const char *explainCols[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" };
int jj;
for(jj=0; jj<ArraySize(explainCols); jj++){
if( strcmp(sqlite3_column_name(pSql,jj),explainCols[jj])!=0 ){
p->cMode = p->mode;
sqlite3_reset(pSql);
return;
}
}
}
nAlloc += 100; nAlloc += 100;
p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int));
abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
@@ -1614,10 +1650,20 @@ static int shell_exec(
sqlite3_free(zEQP); sqlite3_free(zEQP);
} }
/* If the shell is currently in ".explain" mode, gather the extra if( pArg ){
** data required to add indents to the output.*/ pArg->cMode = pArg->mode;
if( pArg && pArg->mode==MODE_Explain ){ if( pArg->autoExplain
explain_data_prepare(pArg, pStmt); && sqlite3_column_count(pStmt)==8
&& sqlite3_strlike("%EXPLAIN%", 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 /* perform the first step. this will tell us if we
@@ -1647,7 +1693,7 @@ static int shell_exec(
/* extract the data and data types */ /* extract the data and data types */
for(i=0; i<nCol; i++){ for(i=0; i<nCol; i++){
aiTypes[i] = x = sqlite3_column_type(pStmt, i); aiTypes[i] = x = sqlite3_column_type(pStmt, i);
if( x==SQLITE_BLOB && pArg && pArg->mode==MODE_Insert ){ if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){
azVals[i] = ""; azVals[i] = "";
}else{ }else{
azVals[i] = (char*)sqlite3_column_text(pStmt, i); azVals[i] = (char*)sqlite3_column_text(pStmt, i);
@@ -1867,8 +1913,7 @@ static char zHelp[] =
".echo on|off Turn command echo on or off\n" ".echo on|off Turn command echo on or off\n"
".eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN\n" ".eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN\n"
".exit Exit this program\n" ".exit Exit this program\n"
".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n" ".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n"
" With no args, it turns EXPLAIN on.\n"
".fullschema Show schema and the content of sqlite_stat tables\n" ".fullschema Show schema and the content of sqlite_stat tables\n"
".headers on|off Turn display of headers on or off\n" ".headers on|off Turn display of headers on or off\n"
".help Show this message\n" ".help Show this message\n"
@@ -2855,7 +2900,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0); open_db(p, 0);
memcpy(&data, p, sizeof(data)); memcpy(&data, p, sizeof(data));
data.showHeader = 1; data.showHeader = 1;
data.mode = MODE_Column; data.cMode = data.mode = MODE_Column;
data.colWidth[0] = 3; data.colWidth[0] = 3;
data.colWidth[1] = 15; data.colWidth[1] = 15;
data.colWidth[2] = 58; data.colWidth[2] = 58;
@@ -2950,37 +2995,24 @@ static int do_meta_command(char *zLine, ShellState *p){
}else }else
if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
int val = nArg>=2 ? booleanValue(azArg[1]) : 1; int val = 1;
if(val == 1) { if( nArg>=2 ){
if(!p->normalMode.valid) { if( strcmp(azArg[1],"auto")==0 ){
p->normalMode.valid = 1; val = 99;
p->normalMode.mode = p->mode; }else{
p->normalMode.showHeader = p->showHeader; val = booleanValue(azArg[1]);
memcpy(p->normalMode.colWidth,p->colWidth,sizeof(p->colWidth));
} }
/* We could put this code under the !p->explainValid }
** condition so that it does not execute if we are already in if( val==1 && p->mode!=MODE_Explain ){
** explain mode. However, always executing it allows us an easy p->normalMode = p->mode;
** was to reset to explain mode in case the user previously
** did an .explain followed by a .width, .mode or .header
** command.
*/
p->mode = MODE_Explain; p->mode = MODE_Explain;
p->showHeader = 1; p->autoExplain = 0;
memset(p->colWidth,0,sizeof(p->colWidth)); }else if( val==0 ){
p->colWidth[0] = 4; /* addr */ if( p->mode==MODE_Explain ) p->mode = p->normalMode;
p->colWidth[1] = 13; /* opcode */ p->autoExplain = 0;
p->colWidth[2] = 4; /* P1 */ }else if( val==99 ){
p->colWidth[3] = 4; /* P2 */ if( p->mode==MODE_Explain ) p->mode = p->normalMode;
p->colWidth[4] = 4; /* P3 */ p->autoExplain = 1;
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));
} }
}else }else
@@ -2996,7 +3028,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0); open_db(p, 0);
memcpy(&data, p, sizeof(data)); memcpy(&data, p, sizeof(data));
data.showHeader = 0; data.showHeader = 0;
data.mode = MODE_Semi; data.cMode = data.mode = MODE_Semi;
rc = sqlite3_exec(p->db, rc = sqlite3_exec(p->db,
"SELECT sql FROM" "SELECT sql FROM"
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
@@ -3021,7 +3053,7 @@ static int do_meta_command(char *zLine, ShellState *p){
raw_printf(p->out, "ANALYZE sqlite_master;\n"); raw_printf(p->out, "ANALYZE sqlite_master;\n");
sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'",
callback, &data, &zErrMsg); callback, &data, &zErrMsg);
data.mode = MODE_Insert; data.cMode = data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1"; data.zDestTable = "sqlite_stat1";
shell_exec(p->db, "SELECT * FROM sqlite_stat1", shell_exec(p->db, "SELECT * FROM sqlite_stat1",
shell_callback, &data,&zErrMsg); shell_callback, &data,&zErrMsg);
@@ -3253,7 +3285,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0); open_db(p, 0);
memcpy(&data, p, sizeof(data)); memcpy(&data, p, sizeof(data));
data.showHeader = 0; data.showHeader = 0;
data.mode = MODE_List; data.cMode = data.mode = MODE_List;
if( nArg==1 ){ if( nArg==1 ){
rc = sqlite3_exec(p->db, rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master " "SELECT name FROM sqlite_master "
@@ -3439,6 +3471,7 @@ static int do_meta_command(char *zLine, ShellState *p){
"ascii column csv html insert line list tabs tcl\n"); "ascii column csv html insert line list tabs tcl\n");
rc = 1; rc = 1;
} }
p->cMode = p->mode;
}else }else
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
@@ -3628,7 +3661,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0); open_db(p, 0);
memcpy(&data, p, sizeof(data)); memcpy(&data, p, sizeof(data));
data.showHeader = 0; data.showHeader = 0;
data.mode = MODE_Semi; data.cMode = data.mode = MODE_Semi;
if( nArg==2 ){ if( nArg==2 ){
int i; int i;
for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]); for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
@@ -3776,7 +3809,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","echo", p->echoOn ? "on" : "off");
utf8_printf(p->out, "%12.12s: %s\n","eqp", p->autoEQP ? "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","headers", p->showHeader ? "on" : "off");
utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
utf8_printf(p->out, "%12.12s: ", "nullvalue"); utf8_printf(p->out, "%12.12s: ", "nullvalue");
@@ -4575,7 +4609,8 @@ static void usage(int showDetail){
*/ */
static void main_init(ShellState *data) { static void main_init(ShellState *data) {
memset(data, 0, sizeof(*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->colSeparator,SEP_Column, 2);
memcpy(data->rowSeparator,SEP_Row, 2); memcpy(data->rowSeparator,SEP_Row, 2);
data->showHeader = 0; data->showHeader = 0;
@@ -4908,6 +4943,7 @@ int SQLITE_CDECL main(int argc, char **argv){
raw_printf(stderr,"Use -help for a list of options.\n"); raw_printf(stderr,"Use -help for a list of options.\n");
return 1; return 1;
} }
data.cMode = data.mode;
} }
if( !readStdin ){ if( !readStdin ){