]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Progress toward adding new output modes to the CLI: json, table, and
authordrh <drh@noemail.net>
Thu, 28 May 2020 23:49:50 +0000 (23:49 +0000)
committerdrh <drh@noemail.net>
Thu, 28 May 2020 23:49:50 +0000 (23:49 +0000)
markdown.

FossilOrigin-Name: 14f55fafec11491e87e6526c72cf85c689d74ba18418a1ae9646586ec206767a

manifest
manifest.uuid
src/shell.c.in
test/shell1.test

index b57275c70fd65369f78794aa7b536bd72c1f9d1b..68a02ab2e3e71cf998bd41986246bed3e7b2615f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Enhance\sthe\s".quote"\smode\sin\sthe\sshell\sso\sthat\sit\shonors\s.separator.
-D 2020-05-28T20:37:17.793
+C Progress\stoward\sadding\snew\soutput\smodes\sto\sthe\sCLI:\s\sjson,\stable,\sand\nmarkdown.
+D 2020-05-28T23:49:50.023
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -534,7 +534,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c c2008519a0654f1e7490e9281ed0397d0f14bb840d81f0b96946248afcbdb25d
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 39a00a8bc89596dfb37c16afcbb1d33de5085b9963564b58aafe1566d08c0881
-F src/shell.c.in 2bca5f1474b43e7c0c1bcd0537b854c2e8a0fac0de2bde473b8c1e919554dcc6
+F src/shell.c.in d135e500f2c84808f86e8113fd22852af4c89f69305f122d3c529cd698ccb396
 F src/sqlite.h.in 74342b41e9d68ff9e56b192009046f8dd0aa2bd76ce1a588f330de614ba61de7
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
@@ -1338,7 +1338,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
-F test/shell1.test 5bd10014ec494744f5e966a1521334e9d612119a0afcfa5251684a4e1f2ffc66
+F test/shell1.test 1c4713ccec468f9300100d5e1419b414b8dcccc742978ad8942e8bd31d2adc9c
 F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
 F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
 F test/shell4.test 1c6aef11daaa2d6830acaba3ac9cbec93fbc1c3d5530743a637f39b3987d08ce
@@ -1866,7 +1866,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 98d4262018a81a9a36dd8beb4b02ff0e75cdcbb8a121d143157ffb37b228d60d
-R 6e7a8027469df73da3225b0c75827874
+P b5e33ed537e7d7dcabc9f6dc91d6838e0d1657f323440e09e2e24ffa2ba6141a
+R 65dafea1aa0e6dfee5aa69ebe4f5d82f
 U drh
-Z 824af62dceaa57c3d7f60896a599839b
+Z 8f810f781bfa2ede3701aac97fce45df
index 6cbf3738cfcb2bcfb2f5e1eac7945a906b146efe..7d744d62ce5fca0ea2ca874c8b8cd3359521d628 100644 (file)
@@ -1 +1 @@
-b5e33ed537e7d7dcabc9f6dc91d6838e0d1657f323440e09e2e24ffa2ba6141a
\ No newline at end of file
+14f55fafec11491e87e6526c72cf85c689d74ba18418a1ae9646586ec206767a
\ No newline at end of file
index 9f960a745cfdf3449a5b1e3f539945c07290d3cc..4795b832c549f7cf01b18aa513718ac6de3acbda 100644 (file)
@@ -1035,18 +1035,6 @@ struct OpenSession {
 };
 #endif
 
-/*
-** Shell output mode information from before ".explain on",
-** saved so that it can be restored by ".explain off"
-*/
-typedef struct SavedModeInfo SavedModeInfo;
-struct SavedModeInfo {
-  int valid;          /* Is there legit data in here? */
-  int mode;           /* Mode prior to ".explain on" */
-  int showHeader;     /* The ".header" setting prior to ".explain on" */
-  int colWidth[100];  /* Column widths prior to ".explain on" */
-};
-
 typedef struct ExpertInfo ExpertInfo;
 struct ExpertInfo {
   sqlite3expert *pExpert;
@@ -1202,6 +1190,9 @@ struct ShellState {
 #define MODE_Ascii   10  /* Use ASCII unit and record separators (0x1F/0x1E) */
 #define MODE_Pretty  11  /* Pretty-print schemas */
 #define MODE_EQP     12  /* Converts EXPLAIN QUERY PLAN output into a graph */
+#define MODE_Json    13  /* Output JSON */
+#define MODE_Markdown 14 /* Markdown formatting */
+#define MODE_Table   15  /* MySQL-style table formatting */
 
 static const char *modeDescr[] = {
   "line",
@@ -1216,7 +1207,10 @@ static const char *modeDescr[] = {
   "explain",
   "ascii",
   "prettyprint",
-  "eqp"
+  "eqp",
+  "json",
+  "markdown",
+  "table"
 };
 
 /*
@@ -1893,6 +1887,43 @@ static int progress_handler(void *pClientData) {
 }
 #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
 
+/*
+** Print N dashes
+*/
+static void print_dashes(FILE *out, int N){
+  const char zDash[] = "--------------------------------------------------";
+  const int nDash = sizeof(zDash) - 1;
+  while( N>nDash ){
+    fputs(zDash, out);
+    N -= nDash;
+  }
+  raw_printf(out, "%.*s", N, zDash);
+}
+
+/*
+** Print a markdown or table-style row separator
+*/
+static void print_row_separator(
+  ShellState *p,
+  int nArg,
+  const char *zSep
+){
+  int i;
+  for(i=0; i<nArg; i++){
+    int w;
+    if( i<ArraySize(p->actualWidth) ){
+      w = p->actualWidth[i];
+      if( w<0 ) w = -w;
+    }else{
+       w = 10;
+    }
+    fputs(zSep, p->out);
+    print_dashes(p->out, w+2);
+  }
+  fputs(zSep, p->out);
+  fputs("\n", p->out);
+}
+
 /*
 ** This is the callback routine that the shell
 ** invokes for each row of a query result.
@@ -1923,23 +1954,38 @@ static int shell_callback(
       }
       break;
     }
+    case MODE_Table:
+    case MODE_Markdown:
     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;
+      char *colSep;
+      char *rowStart;
       int nWidth;
       if( p->cMode==MODE_Column ){
         colWidth = p->colWidth;
         nWidth = ArraySize(p->colWidth);
         showHdr = p->showHeader;
         rowSep = p->rowSeparator;
-      }else{
+        colSep = "  ";
+        rowStart = "";
+      }else if( p->cMode==MODE_Explain ){
         colWidth = aExplainWidths;
         nWidth = ArraySize(aExplainWidths);
         showHdr = 1;
         rowSep = SEP_Row;
+        colSep = "  ";
+        rowStart = "";
+      }else{
+        colWidth = p->colWidth;
+        nWidth = ArraySize(p->colWidth);
+        showHdr = p->showHeader;
+        rowSep = " |\n";
+        colSep = " | ";
+        rowStart = "| ";
       }
       if( p->cnt++==0 ){
         for(i=0; i<nArg; i++){
@@ -1958,12 +2004,12 @@ static int shell_callback(
           if( i<ArraySize(p->actualWidth) ){
             p->actualWidth[i] = w;
           }
-          if( showHdr ){
-            utf8_width_print(p->out, w, azCol[i]);
-            utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : "  ");
-          }
         }
         if( showHdr ){
+          if( p->cMode==MODE_Table ){
+            print_row_separator(p, nArg, "+");
+          }
+          fputs(rowStart, p->out);
           for(i=0; i<nArg; i++){
             int w;
             if( i<ArraySize(p->actualWidth) ){
@@ -1972,14 +2018,34 @@ static int shell_callback(
             }else{
                w = 10;
             }
-            utf8_printf(p->out,"%-*.*s%s",w,w,
-                   "----------------------------------------------------------"
-                   "----------------------------------------------------------",
-                    i==nArg-1 ? rowSep : "  ");
+            utf8_width_print(p->out, w, azCol[i]);
+            fputs(i==nArg-1 ? rowSep : colSep, p->out);
+          }
+          for(i=0; i<nArg; i++){
+            int w;
+            if( i<ArraySize(p->actualWidth) ){
+               w = p->actualWidth[i];
+               if( w<0 ) w = -w;
+            }else{
+               w = 10;
+            }
+            if( p->cMode==MODE_Table || p->cMode==MODE_Markdown ){
+              char *zX = p->cMode==MODE_Markdown ? "|" : "+";
+              fputs(zX, p->out);
+              print_dashes(p->out, w+2);
+              if( i==nArg-1 ){
+                fputs(zX, p->out);
+                fputs("\n", p->out);
+              }
+            }else{
+              print_dashes(p->out, w);
+              fputs(i==nArg-1 ? rowSep : colSep, p->out);
+            }
           }
         }
       }
       if( azArg==0 ) break;
+      fputs(rowStart, p->out);
       for(i=0; i<nArg; i++){
         int w;
         if( i<ArraySize(p->actualWidth) ){
@@ -1997,7 +2063,7 @@ static int shell_callback(
           p->iIndent++;
         }
         utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue);
-        utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : "  ");
+        utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : colSep);
       }
       break;
     }
@@ -2201,6 +2267,50 @@ static int shell_callback(
       raw_printf(p->out,");\n");
       break;
     }
+    case MODE_Json: {
+      if( azArg==0 ) break;
+      if( p->cnt==0 ){
+        fputs("[{", p->out);
+      }else{
+        fputs(",\n{", p->out);
+      }
+      p->cnt++;
+      for(i=0; i<nArg; i++){
+        output_c_string(p->out, azCol[i]);
+        putc(':', p->out);
+        if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
+          fputs("null",p->out);
+        }else if( aiType && aiType[i]==SQLITE_FLOAT ){
+          char z[50];
+          double r = sqlite3_column_double(p->pStmt, i);
+          sqlite3_uint64 ur;
+          memcpy(&ur,&r,sizeof(r));
+          if( ur==0x7ff0000000000000LL ){
+            raw_printf(p->out, "1e999");
+          }else if( ur==0xfff0000000000000LL ){
+            raw_printf(p->out, "-1e999");
+          }else{
+            sqlite3_snprintf(50,z,"%!.20g", r);
+            raw_printf(p->out, "%s", z);
+          }
+        }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
+          const void *pBlob = sqlite3_column_blob(p->pStmt, i);
+          int nBlob = sqlite3_column_bytes(p->pStmt, i);
+          putc('"', p->out);
+          output_hex_blob(p->out, pBlob, nBlob);
+          putc('"', p->out);
+        }else if( aiType && aiType[i]==SQLITE_TEXT ){
+          output_c_string(p->out, azArg[i]);
+        }else{
+          utf8_printf(p->out,"%s", azArg[i]);
+        }
+        if( i<nArg-1 ){
+          putc(',', p->out);
+        }
+      }
+      putc('}', p->out);
+      break;
+    }
     case MODE_Quote: {
       if( azArg==0 ) break;
       if( p->cnt==0 && p->showHeader ){
@@ -2890,6 +3000,24 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
   sqlite3_finalize(pQ);
 }
 
+#if 0
+/*
+** Run a prepared statement and output the result in one of the
+** table-oriented formats, either MODE_Markdown or MODE_Table.
+**
+** This is different from ordinary exec_prepared_stmt() in that
+** it has to run the entire query and gather the results into memory
+** first, in order to determine column widths, before providing
+** any output.
+*/
+static void exec_prepared_stmt_tablemode(
+  ShellState *pArg,                                /* Pointer to ShellState */
+  sqlite3_stmt *pStmt                              /* Statment to run */
+){
+
+}
+#endif
+
 /*
 ** Run a prepared statement
 */
@@ -2946,6 +3074,11 @@ static void exec_prepared_stmt(
         }
       } while( SQLITE_ROW == rc );
       sqlite3_free(pData);
+      if( pArg->cMode==MODE_Table ){
+        print_row_separator(pArg, nCol, "+");
+      }else if( pArg->cMode==MODE_Json ){
+        fputs("]\n", pArg->out);
+      }
     }
   }
 }
@@ -8224,11 +8357,18 @@ static int do_meta_command(char *zLine, ShellState *p){
       p->mode = MODE_Ascii;
       sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
       sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
+    }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){
+      p->mode = MODE_Markdown;
+    }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){
+      p->mode = MODE_Table;
+    }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
+      p->mode = MODE_Json;
     }else if( nArg==1 ){
       raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
     }else{
       raw_printf(stderr, "Error: mode should be one of: "
-         "ascii column csv html insert line list quote tabs tcl\n");
+         "ascii column csv html insert json line list markdown "
+         "quote table tabs tcl\n");
       rc = 1;
     }
     p->cMode = p->mode;
@@ -10238,9 +10378,11 @@ static const char zOptions[] =
   "   -help                show this message\n"
   "   -html                set output mode to HTML\n"
   "   -interactive         force interactive I/O\n"
+  "   -json                set output mode to 'json'\n"
   "   -line                set output mode to 'line'\n"
   "   -list                set output mode to 'list'\n"
   "   -lookaside SIZE N    use N entries of SZ bytes for lookaside memory\n"
+  "   -markdown            set output mode to 'markdown'\n"
 #if defined(SQLITE_ENABLE_DESERIALIZE)
   "   -maxsize N           maximum size for a --deserialize database\n"
 #endif
@@ -10260,6 +10402,7 @@ static const char zOptions[] =
   "   -sorterref SIZE      sorter references threshold size\n"
 #endif
   "   -stats               print memory stats before each finalize\n"
+  "   -table               set output mode to 'table'\n"
   "   -version             show SQLite version\n"
   "   -vfs NAME            use NAME as the default VFS\n"
 #ifdef SQLITE_ENABLE_VFSTRACE
@@ -10661,6 +10804,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
       data.mode = MODE_Line;
     }else if( strcmp(z,"-column")==0 ){
       data.mode = MODE_Column;
+    }else if( strcmp(z,"-json")==0 ){
+      data.mode = MODE_Json;
+    }else if( strcmp(z,"-markdown")==0 ){
+      data.mode = MODE_Markdown;
+    }else if( strcmp(z,"-table")==0 ){
+      data.mode = MODE_Table;
     }else if( strcmp(z,"-csv")==0 ){
       data.mode = MODE_Csv;
       memcpy(data.colSeparator,",",2);
index c142ea72412022c12f3a40b82d2c6f87c3ce6453..a900dc3e1db17b1078c2107073d6bc1d9e436baa 100644 (file)
@@ -199,10 +199,10 @@ do_test shell1-2.2.4 {
 } {0 {}}
 do_test shell1-2.2.5 {
   catchcmd "test.db" ".mode \"insert FOO"
-} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}}
+} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
 do_test shell1-2.2.6 {
   catchcmd "test.db" ".mode \'insert FOO"
-} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}}
+} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
 
 # check multiple tokens, and quoted tokens
 do_test shell1-2.3.1 {
@@ -230,7 +230,7 @@ do_test shell1-2.3.7 {
 # check quoted args are unquoted
 do_test shell1-2.4.1 {
   catchcmd "test.db" ".mode FOO"
-} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}}
+} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
 do_test shell1-2.4.2 {
   catchcmd "test.db" ".mode csv"
 } {0 {}}
@@ -430,7 +430,7 @@ do_test shell1-3.13.1 {
 } {0 {current output mode: list}}
 do_test shell1-3.13.2 {
   catchcmd "test.db" ".mode FOO"
-} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}}
+} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
 do_test shell1-3.13.3 {
   catchcmd "test.db" ".mode csv"
 } {0 {}}
@@ -463,10 +463,10 @@ do_test shell1-3.13.11 {
 # don't allow partial mode type matches
 do_test shell1-3.13.12 {
   catchcmd "test.db" ".mode l"
-} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}}
+} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
 do_test shell1-3.13.13 {
   catchcmd "test.db" ".mode li"
-} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}}
+} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
 do_test shell1-3.13.14 {
   catchcmd "test.db" ".mode lin"
 } {0 {}}