]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
In the shell tool, if an "EXPLAIN" command is executed in ".explain on" mode, attempt...
authordan <dan@noemail.net>
Wed, 13 Nov 2013 18:35:01 +0000 (18:35 +0000)
committerdan <dan@noemail.net>
Wed, 13 Nov 2013 18:35:01 +0000 (18:35 +0000)
FossilOrigin-Name: e7d34ec6814ed4606a6d5d7f68c218ae4d25e666

manifest
manifest.uuid
src/shell.c

index 3448386df97a91f957dc93dd8583cbbde91d2222..78bd21a26796e58ade67a1aebe8e722533774679 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\s"PRAGMA\svdbe_eqp"\scommand,\sonly\savailable\swith\sSQLITE_DEBUG.\s\sSimplify\nsome\sof\sthe\sother\sdebugging\slogic.
-D 2013-11-13T17:58:23.573
+C In\sthe\sshell\stool,\sif\san\s"EXPLAIN"\scommand\sis\sexecuted\sin\s".explain\son"\smode,\sattempt\sto\sautomatically\sindent\sthe\sbodies\sof\sloops\sin\sthe\soutput\sVDBE\sprogram.
+D 2013-11-13T18:35:01.894
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 8a07bebafbfda0eb67728f4bd15a36201662d1a1
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -220,7 +220,7 @@ F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68
 F src/resolve.c fc4673cc49b116e51e7f12de074c0acf8f2388f9
 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
 F src/select.c 7317406831ecced390edba972818f3c5f82238c0
-F src/shell.c 03d8d9b4052430343ff30d646334621f980f1202
+F src/shell.c 3b23017da75118da0a7e3518c6ce78a7b747fb05
 F src/sqlite.h.in 4dedcab5b32358bf7a596badffe7363be1f1a82d
 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
@@ -1138,7 +1138,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P 5196000930600d0cd931b87e864507791b9dab08
-R f5035ff28ea611640fd4871588416d7f
-U drh
-Z f0bce2e671cb5c826c0cf7f8a42a33dc
+P 8ce33f4c818e1c785a1c176f6f631b8184e1166b
+R 5d7c6b4eb6f0cadad72d4cdb7ed00f79
+U dan
+Z 87b1c90d92386c54cb5a5e86314bc13e
index 834dfca91b3afacbc137dd1f9d39cd257c0be852..e811c26432c61ff052bf20b4de5862f91a2845ab 100644 (file)
@@ -1 +1 @@
-8ce33f4c818e1c785a1c176f6f631b8184e1166b
\ No newline at end of file
+e7d34ec6814ed4606a6d5d7f68c218ae4d25e666
\ No newline at end of file
index c3aee0463398e8195aedb9acc14e9377cb118407..676175081605a583892df2006ef421001c12b817 100644 (file)
@@ -464,6 +464,8 @@ struct callback_data {
   const char *zVfs;           /* Name of VFS to use */
   sqlite3_stmt *pStmt;   /* Current statement if any. */
   FILE *pLog;            /* Write log output here */
+  int *aiIndent;         /* Array of indents used in MODE_Explain */
+  int nIndent;           /* Size of array aiIndent[] */
 };
 
 /*
@@ -765,10 +767,15 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
         }else{
            w = 10;
         }
-        if( p->mode==MODE_Explain && azArg[i] && 
-           strlen30(azArg[i])>w ){
+        if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){
           w = strlen30(azArg[i]);
         }
+        if( i==1 && p->aiIndent && p->pStmt ){
+          int iOp = sqlite3_column_int(p->pStmt, 0);
+          if( iOp<p->nIndent ){
+            fprintf(p->out, "%*.s", p->aiIndent[iOp], "");
+          }
+        }
         if( w<0 ){
           fprintf(p->out,"%*.*s%s",-w,-w,
               azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": "  ");
@@ -1141,6 +1148,89 @@ static int display_stats(
   return 0;
 }
 
+/*
+** Parameter azArray points to a zero-terminated array of strings. zStr
+** points to a single nul-terminated string. Return non-zero if zStr
+** is equal, according to strcmp(), to any of the strings in the array.
+** Otherwise, return zero.
+*/
+static int str_in_array(const char *zStr, const char **azArray){
+  int i;
+  for(i=0; azArray[i]; i++){
+    if( 0==strcmp(zStr, azArray[i]) ) return 1;
+  }
+  return 0;
+}
+
+/*
+** If compiled statement pSql appears to be an EXPLAIN statement, allocate
+** and populate the callback_data.aiIndent[] array with the number of
+** spaces each opcode should be indented before it is output. 
+**
+** The indenting rules are:
+**
+**     * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent
+**       all opcodes that occur between the p2 jump destination and the opcode
+**       itself by 2 spaces.
+**
+**     * For each "Goto", if the jump destination is a "Yield" instruction
+**       that occurs earlier in the program than the Goto itself, indent
+**       all opcodes between the "Yield" and "Goto" by 2 spaces.
+*/
+static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){
+  const char *zSql;               /* The text of the SQL statement */
+  const char *z;                  /* Used to check if this is an EXPLAIN */
+  int *abYield = 0;               /* True if op is an OP_Yield */
+  int nAlloc = 0;                 /* Allocated size of p->aiIndent[], abYield */
+  int iOp;
+
+  const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", 0 };
+  const char *azYield[] = { "Yield", 0 };
+  const char *azGoto[] = { "Goto", 0 };
+
+  /* Try to figure out if this is really an EXPLAIN statement. If this
+  ** cannot be verified, return early.  */
+  zSql = sqlite3_sql(pSql);
+  if( zSql==0 ) return;
+  for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++);
+  if( sqlite3_strnicmp(z, "explain", 7) ) return;
+
+  for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){
+    int i;
+    const char *zOp = (const char*)sqlite3_column_text(pSql, 1);
+    int p2 = sqlite3_column_int(pSql, 3);
+
+    /* Grow the p->aiIndent array as required */
+    if( iOp>=nAlloc ){
+      nAlloc += 100;
+      p->aiIndent = (int*)sqlite3_realloc(p->aiIndent, nAlloc*sizeof(int));
+      abYield = (int*)sqlite3_realloc(abYield, nAlloc*sizeof(int));
+    }
+    abYield[iOp] = str_in_array(zOp, azYield);
+    p->aiIndent[iOp] = 0;
+    p->nIndent = iOp+1;
+
+    if( str_in_array(zOp, azNext) ){
+      for(i=p2; i<iOp; i++) p->aiIndent[i] += 2;
+    }
+    if( str_in_array(zOp, azGoto) && p2<p->nIndent && abYield[p2] ){
+      for(i=p2+1; i<iOp; i++) p->aiIndent[i] += 2;
+    }
+  }
+
+  sqlite3_free(abYield);
+  sqlite3_reset(pSql);
+}
+
+/*
+** Free the array allocated by explain_data_prepare().
+*/
+static void explain_data_delete(struct callback_data *p){
+  sqlite3_free(p->aiIndent);
+  p->aiIndent = 0;
+  p->nIndent = 0;
+}
+
 /*
 ** Execute a statement or set of statements.  Print 
 ** any result rows/columns depending on the current mode 
@@ -1202,6 +1292,12 @@ static int shell_exec(
         }
       }
 
+      /* If the shell is currently in ".explain" mode, gather the extra
+      ** data required to add indents to the output.*/
+      if( pArg->mode==MODE_Explain ){
+        explain_data_prepare(pArg, pStmt);
+      }
+
       /* perform the first step.  this will tell us if we
       ** have a result set or not and how wide it is.
       */
@@ -1259,6 +1355,8 @@ static int shell_exec(
         }
       }
 
+      explain_data_delete(pArg);
+
       /* print usage stats if stats on */
       if( pArg && pArg->statsOn ){
         display_stats(db, pArg, 0);