| | @@ -9700,22 +9700,10 @@ |
| 9700 | 9700 | char **azFilter; /* Array of xFilter rejection GLOB patterns */ |
| 9701 | 9701 | sqlite3_session *p; /* The open session */ |
| 9702 | 9702 | }; |
| 9703 | 9703 | #endif |
| 9704 | 9704 | |
| 9705 | | -/* |
| 9706 | | -** Shell output mode information from before ".explain on", |
| 9707 | | -** saved so that it can be restored by ".explain off" |
| 9708 | | -*/ |
| 9709 | | -typedef struct SavedModeInfo SavedModeInfo; |
| 9710 | | -struct SavedModeInfo { |
| 9711 | | - int valid; /* Is there legit data in here? */ |
| 9712 | | - int mode; /* Mode prior to ".explain on" */ |
| 9713 | | - int showHeader; /* The ".header" setting prior to ".explain on" */ |
| 9714 | | - int colWidth[100]; /* Column widths prior to ".explain on" */ |
| 9715 | | -}; |
| 9716 | | - |
| 9717 | 9705 | typedef struct ExpertInfo ExpertInfo; |
| 9718 | 9706 | struct ExpertInfo { |
| 9719 | 9707 | sqlite3expert *pExpert; |
| 9720 | 9708 | int bVerbose; |
| 9721 | 9709 | }; |
| | @@ -9781,12 +9769,13 @@ |
| 9781 | 9769 | char zTestcase[30]; /* Name of current test case */ |
| 9782 | 9770 | char colSeparator[20]; /* Column separator character for several modes */ |
| 9783 | 9771 | char rowSeparator[20]; /* Row separator character for MODE_Ascii */ |
| 9784 | 9772 | char colSepPrior[20]; /* Saved column separator */ |
| 9785 | 9773 | char rowSepPrior[20]; /* Saved row separator */ |
| 9786 | | - int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 9787 | | - int actualWidth[100]; /* Actual width of each column */ |
| 9774 | + int *colWidth; /* Requested width of each column in columnar modes */ |
| 9775 | + int *actualWidth; /* Actual width of each column */ |
| 9776 | + int nWidth; /* Number of slots in colWidth[] and actualWidth[] */ |
| 9788 | 9777 | char nullValue[20]; /* The text to print when a NULL comes back from |
| 9789 | 9778 | ** the database */ |
| 9790 | 9779 | char outfile[FILENAME_MAX]; /* Filename for *out */ |
| 9791 | 9780 | const char *zDbFilename; /* name of the database file */ |
| 9792 | 9781 | char *zFreeOnClose; /* Filename to free when closing */ |
| | @@ -9867,10 +9856,13 @@ |
| 9867 | 9856 | #define MODE_Csv 8 /* Quote strings, numbers are plain */ |
| 9868 | 9857 | #define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ |
| 9869 | 9858 | #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ |
| 9870 | 9859 | #define MODE_Pretty 11 /* Pretty-print schemas */ |
| 9871 | 9860 | #define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */ |
| 9861 | +#define MODE_Json 13 /* Output JSON */ |
| 9862 | +#define MODE_Markdown 14 /* Markdown formatting */ |
| 9863 | +#define MODE_Table 15 /* MySQL-style table formatting */ |
| 9872 | 9864 | |
| 9873 | 9865 | static const char *modeDescr[] = { |
| 9874 | 9866 | "line", |
| 9875 | 9867 | "column", |
| 9876 | 9868 | "list", |
| | @@ -9881,11 +9873,14 @@ |
| 9881 | 9873 | "tcl", |
| 9882 | 9874 | "csv", |
| 9883 | 9875 | "explain", |
| 9884 | 9876 | "ascii", |
| 9885 | 9877 | "prettyprint", |
| 9886 | | - "eqp" |
| 9878 | + "eqp", |
| 9879 | + "json", |
| 9880 | + "markdown", |
| 9881 | + "table" |
| 9887 | 9882 | }; |
| 9888 | 9883 | |
| 9889 | 9884 | /* |
| 9890 | 9885 | ** These are the column/row/line separators used by the various |
| 9891 | 9886 | ** import/export modes. |
| | @@ -10248,10 +10243,44 @@ |
| 10248 | 10243 | fputc(c, out); |
| 10249 | 10244 | } |
| 10250 | 10245 | } |
| 10251 | 10246 | fputc('"', out); |
| 10252 | 10247 | } |
| 10248 | + |
| 10249 | +/* |
| 10250 | +** Output the given string as a quoted according to JSON quoting rules. |
| 10251 | +*/ |
| 10252 | +static void output_json_string(FILE *out, const char *z, int n){ |
| 10253 | + unsigned int c; |
| 10254 | + if( n<0 ) n = (int)strlen(z); |
| 10255 | + fputc('"', out); |
| 10256 | + while( n-- ){ |
| 10257 | + c = *(z++); |
| 10258 | + if( c=='\\' || c=='"' ){ |
| 10259 | + fputc('\\', out); |
| 10260 | + fputc(c, out); |
| 10261 | + }else if( c<=0x1f ){ |
| 10262 | + fputc('\\', out); |
| 10263 | + if( c=='\b' ){ |
| 10264 | + fputc('b', out); |
| 10265 | + }else if( c=='\f' ){ |
| 10266 | + fputc('f', out); |
| 10267 | + }else if( c=='\n' ){ |
| 10268 | + fputc('n', out); |
| 10269 | + }else if( c=='\r' ){ |
| 10270 | + fputc('r', out); |
| 10271 | + }else if( c=='\t' ){ |
| 10272 | + fputc('t', out); |
| 10273 | + }else{ |
| 10274 | + raw_printf(out, "u%04x",c); |
| 10275 | + } |
| 10276 | + }else{ |
| 10277 | + fputc(c, out); |
| 10278 | + } |
| 10279 | + } |
| 10280 | + fputc('"', out); |
| 10281 | +} |
| 10253 | 10282 | |
| 10254 | 10283 | /* |
| 10255 | 10284 | ** Output the given string with characters that are special to |
| 10256 | 10285 | ** HTML escaped. |
| 10257 | 10286 | */ |
| | @@ -10557,10 +10586,40 @@ |
| 10557 | 10586 | raw_printf(p->out, "Progress %u\n", p->nProgress); |
| 10558 | 10587 | } |
| 10559 | 10588 | return 0; |
| 10560 | 10589 | } |
| 10561 | 10590 | #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
| 10591 | + |
| 10592 | +/* |
| 10593 | +** Print N dashes |
| 10594 | +*/ |
| 10595 | +static void print_dashes(FILE *out, int N){ |
| 10596 | + const char zDash[] = "--------------------------------------------------"; |
| 10597 | + const int nDash = sizeof(zDash) - 1; |
| 10598 | + while( N>nDash ){ |
| 10599 | + fputs(zDash, out); |
| 10600 | + N -= nDash; |
| 10601 | + } |
| 10602 | + raw_printf(out, "%.*s", N, zDash); |
| 10603 | +} |
| 10604 | + |
| 10605 | +/* |
| 10606 | +** Print a markdown or table-style row separator |
| 10607 | +*/ |
| 10608 | +static void print_row_separator( |
| 10609 | + ShellState *p, |
| 10610 | + int nArg, |
| 10611 | + const char *zSep |
| 10612 | +){ |
| 10613 | + int i; |
| 10614 | + for(i=0; i<nArg; i++){ |
| 10615 | + fputs(zSep, p->out); |
| 10616 | + print_dashes(p->out, p->actualWidth[i]+2); |
| 10617 | + } |
| 10618 | + fputs(zSep, p->out); |
| 10619 | + fputs("\n", p->out); |
| 10620 | +} |
| 10562 | 10621 | |
| 10563 | 10622 | /* |
| 10564 | 10623 | ** This is the callback routine that the shell |
| 10565 | 10624 | ** invokes for each row of a query result. |
| 10566 | 10625 | */ |
| | @@ -10567,11 +10626,11 @@ |
| 10567 | 10626 | static int shell_callback( |
| 10568 | 10627 | void *pArg, |
| 10569 | 10628 | int nArg, /* Number of result columns */ |
| 10570 | 10629 | char **azArg, /* Text of each result column */ |
| 10571 | 10630 | char **azCol, /* Column names */ |
| 10572 | | - int *aiType /* Column types */ |
| 10631 | + int *aiType /* Column types. Might be NULL */ |
| 10573 | 10632 | ){ |
| 10574 | 10633 | int i; |
| 10575 | 10634 | ShellState *p = (ShellState*)pArg; |
| 10576 | 10635 | |
| 10577 | 10636 | if( azArg==0 ) return 0; |
| | @@ -10588,85 +10647,36 @@ |
| 10588 | 10647 | utf8_printf(p->out,"%*s = %s%s", w, azCol[i], |
| 10589 | 10648 | azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
| 10590 | 10649 | } |
| 10591 | 10650 | break; |
| 10592 | 10651 | } |
| 10593 | | - case MODE_Explain: |
| 10594 | | - case MODE_Column: { |
| 10595 | | - static const int aExplainWidths[] = {4, 13, 4, 4, 4, 13, 2, 13}; |
| 10596 | | - const int *colWidth; |
| 10597 | | - int showHdr; |
| 10598 | | - char *rowSep; |
| 10599 | | - int nWidth; |
| 10600 | | - if( p->cMode==MODE_Column ){ |
| 10601 | | - colWidth = p->colWidth; |
| 10602 | | - nWidth = ArraySize(p->colWidth); |
| 10603 | | - showHdr = p->showHeader; |
| 10604 | | - rowSep = p->rowSeparator; |
| 10605 | | - }else{ |
| 10606 | | - colWidth = aExplainWidths; |
| 10607 | | - nWidth = ArraySize(aExplainWidths); |
| 10608 | | - showHdr = 1; |
| 10609 | | - rowSep = SEP_Row; |
| 10652 | + case MODE_Explain: { |
| 10653 | + static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; |
| 10654 | + if( nArg>ArraySize(aExplainWidth) ){ |
| 10655 | + nArg = ArraySize(aExplainWidth); |
| 10610 | 10656 | } |
| 10611 | 10657 | if( p->cnt++==0 ){ |
| 10612 | 10658 | for(i=0; i<nArg; i++){ |
| 10613 | | - int w, n; |
| 10614 | | - if( i<nWidth ){ |
| 10615 | | - w = colWidth[i]; |
| 10616 | | - }else{ |
| 10617 | | - w = 0; |
| 10618 | | - } |
| 10619 | | - if( w==0 ){ |
| 10620 | | - w = strlenChar(azCol[i] ? azCol[i] : ""); |
| 10621 | | - if( w<10 ) w = 10; |
| 10622 | | - n = strlenChar(azArg && azArg[i] ? azArg[i] : p->nullValue); |
| 10623 | | - if( w<n ) w = n; |
| 10624 | | - } |
| 10625 | | - if( i<ArraySize(p->actualWidth) ){ |
| 10626 | | - p->actualWidth[i] = w; |
| 10627 | | - } |
| 10628 | | - if( showHdr ){ |
| 10629 | | - utf8_width_print(p->out, w, azCol[i]); |
| 10630 | | - utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); |
| 10631 | | - } |
| 10632 | | - } |
| 10633 | | - if( showHdr ){ |
| 10634 | | - for(i=0; i<nArg; i++){ |
| 10635 | | - int w; |
| 10636 | | - if( i<ArraySize(p->actualWidth) ){ |
| 10637 | | - w = p->actualWidth[i]; |
| 10638 | | - if( w<0 ) w = -w; |
| 10639 | | - }else{ |
| 10640 | | - w = 10; |
| 10641 | | - } |
| 10642 | | - utf8_printf(p->out,"%-*.*s%s",w,w, |
| 10643 | | - "----------------------------------------------------------" |
| 10644 | | - "----------------------------------------------------------", |
| 10645 | | - i==nArg-1 ? rowSep : " "); |
| 10646 | | - } |
| 10659 | + int w = aExplainWidth[i]; |
| 10660 | + utf8_width_print(p->out, w, azCol[i]); |
| 10661 | + fputs(i==nArg-1 ? "\n" : " ", p->out); |
| 10647 | 10662 | } |
| 10648 | 10663 | } |
| 10649 | 10664 | if( azArg==0 ) break; |
| 10650 | 10665 | for(i=0; i<nArg; i++){ |
| 10651 | | - int w; |
| 10652 | | - if( i<ArraySize(p->actualWidth) ){ |
| 10653 | | - w = p->actualWidth[i]; |
| 10654 | | - }else{ |
| 10655 | | - w = 10; |
| 10656 | | - } |
| 10657 | | - if( p->cMode==MODE_Explain && azArg[i] && strlenChar(azArg[i])>w ){ |
| 10666 | + int w = aExplainWidth[i]; |
| 10667 | + if( azArg[i] && strlenChar(azArg[i])>w ){ |
| 10658 | 10668 | w = strlenChar(azArg[i]); |
| 10659 | 10669 | } |
| 10660 | 10670 | if( i==1 && p->aiIndent && p->pStmt ){ |
| 10661 | 10671 | if( p->iIndent<p->nIndent ){ |
| 10662 | 10672 | utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); |
| 10663 | 10673 | } |
| 10664 | 10674 | p->iIndent++; |
| 10665 | 10675 | } |
| 10666 | 10676 | utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); |
| 10667 | | - utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); |
| 10677 | + fputs(i==nArg-1 ? "\n" : " ", p->out); |
| 10668 | 10678 | } |
| 10669 | 10679 | break; |
| 10670 | 10680 | } |
| 10671 | 10681 | case MODE_Semi: { /* .schema and .fullschema output */ |
| 10672 | 10682 | printSchemaLine(p->out, azArg[0], ";\n"); |
| | @@ -10865,23 +10875,65 @@ |
| 10865 | 10875 | output_quoted_escaped_string(p->out, azArg[i]); |
| 10866 | 10876 | } |
| 10867 | 10877 | } |
| 10868 | 10878 | raw_printf(p->out,");\n"); |
| 10869 | 10879 | break; |
| 10880 | + } |
| 10881 | + case MODE_Json: { |
| 10882 | + if( azArg==0 ) break; |
| 10883 | + if( p->cnt==0 ){ |
| 10884 | + fputs("[{", p->out); |
| 10885 | + }else{ |
| 10886 | + fputs(",\n{", p->out); |
| 10887 | + } |
| 10888 | + p->cnt++; |
| 10889 | + for(i=0; i<nArg; i++){ |
| 10890 | + output_json_string(p->out, azCol[i], -1); |
| 10891 | + putc(':', p->out); |
| 10892 | + if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 10893 | + fputs("null",p->out); |
| 10894 | + }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 10895 | + char z[50]; |
| 10896 | + double r = sqlite3_column_double(p->pStmt, i); |
| 10897 | + sqlite3_uint64 ur; |
| 10898 | + memcpy(&ur,&r,sizeof(r)); |
| 10899 | + if( ur==0x7ff0000000000000LL ){ |
| 10900 | + raw_printf(p->out, "1e999"); |
| 10901 | + }else if( ur==0xfff0000000000000LL ){ |
| 10902 | + raw_printf(p->out, "-1e999"); |
| 10903 | + }else{ |
| 10904 | + sqlite3_snprintf(50,z,"%!.20g", r); |
| 10905 | + raw_printf(p->out, "%s", z); |
| 10906 | + } |
| 10907 | + }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 10908 | + const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 10909 | + int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 10910 | + output_json_string(p->out, pBlob, nBlob); |
| 10911 | + }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 10912 | + output_json_string(p->out, azArg[i], -1); |
| 10913 | + }else{ |
| 10914 | + utf8_printf(p->out,"%s", azArg[i]); |
| 10915 | + } |
| 10916 | + if( i<nArg-1 ){ |
| 10917 | + putc(',', p->out); |
| 10918 | + } |
| 10919 | + } |
| 10920 | + putc('}', p->out); |
| 10921 | + break; |
| 10870 | 10922 | } |
| 10871 | 10923 | case MODE_Quote: { |
| 10872 | 10924 | if( azArg==0 ) break; |
| 10873 | 10925 | if( p->cnt==0 && p->showHeader ){ |
| 10874 | 10926 | for(i=0; i<nArg; i++){ |
| 10875 | | - if( i>0 ) raw_printf(p->out, ","); |
| 10927 | + if( i>0 ) fputs(p->colSeparator, p->out); |
| 10876 | 10928 | output_quoted_string(p->out, azCol[i]); |
| 10877 | 10929 | } |
| 10878 | | - raw_printf(p->out,"\n"); |
| 10930 | + fputs(p->rowSeparator, p->out); |
| 10879 | 10931 | } |
| 10880 | 10932 | p->cnt++; |
| 10881 | 10933 | for(i=0; i<nArg; i++){ |
| 10882 | | - if( i>0 ) raw_printf(p->out, ","); |
| 10934 | + if( i>0 ) fputs(p->colSeparator, p->out); |
| 10883 | 10935 | if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 10884 | 10936 | utf8_printf(p->out,"NULL"); |
| 10885 | 10937 | }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 10886 | 10938 | output_quoted_string(p->out, azArg[i]); |
| 10887 | 10939 | }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| | @@ -10899,11 +10951,11 @@ |
| 10899 | 10951 | utf8_printf(p->out,"%s", azArg[i]); |
| 10900 | 10952 | }else{ |
| 10901 | 10953 | output_quoted_string(p->out, azArg[i]); |
| 10902 | 10954 | } |
| 10903 | 10955 | } |
| 10904 | | - raw_printf(p->out,"\n"); |
| 10956 | + fputs(p->rowSeparator, p->out); |
| 10905 | 10957 | break; |
| 10906 | 10958 | } |
| 10907 | 10959 | case MODE_Ascii: { |
| 10908 | 10960 | if( p->cnt++==0 && p->showHeader ){ |
| 10909 | 10961 | for(i=0; i<nArg; i++){ |
| | @@ -11554,19 +11606,127 @@ |
| 11554 | 11606 | } |
| 11555 | 11607 | sqlite3_reset(pQ); |
| 11556 | 11608 | } |
| 11557 | 11609 | sqlite3_finalize(pQ); |
| 11558 | 11610 | } |
| 11611 | + |
| 11612 | +/* |
| 11613 | +** Run a prepared statement and output the result in one of the |
| 11614 | +** table-oriented formats: MODE_Column, MODE_Markdown, or MODE_Table. |
| 11615 | +** |
| 11616 | +** This is different from ordinary exec_prepared_stmt() in that |
| 11617 | +** it has to run the entire query and gather the results into memory |
| 11618 | +** first, in order to determine column widths, before providing |
| 11619 | +** any output. |
| 11620 | +*/ |
| 11621 | +static void exec_prepared_stmt_columnar( |
| 11622 | + ShellState *p, /* Pointer to ShellState */ |
| 11623 | + sqlite3_stmt *pStmt /* Statment to run */ |
| 11624 | +){ |
| 11625 | + int nRow = 0; |
| 11626 | + int nColumn = 0; |
| 11627 | + char **azData = 0; |
| 11628 | + char *zMsg = 0; |
| 11629 | + const char *z; |
| 11630 | + int rc; |
| 11631 | + int i, j, nTotal, w, n; |
| 11632 | + const char *colSep; |
| 11633 | + const char *rowSep; |
| 11634 | + |
| 11635 | + rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt), |
| 11636 | + &azData, &nRow, &nColumn, &zMsg); |
| 11637 | + if( rc ){ |
| 11638 | + utf8_printf(p->out, "ERROR: %s\n", zMsg); |
| 11639 | + sqlite3_free(zMsg); |
| 11640 | + sqlite3_free_table(azData); |
| 11641 | + return; |
| 11642 | + } |
| 11643 | + if( nColumn>p->nWidth ){ |
| 11644 | + p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); |
| 11645 | + if( p->colWidth==0 ) shell_out_of_memory(); |
| 11646 | + for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0; |
| 11647 | + p->nWidth = nColumn; |
| 11648 | + p->actualWidth = &p->colWidth[nColumn]; |
| 11649 | + } |
| 11650 | + memset(p->actualWidth, 0, nColumn*sizeof(int)); |
| 11651 | + for(i=0; i<nColumn; i++){ |
| 11652 | + w = p->colWidth[i]; |
| 11653 | + if( w<0 ) w = -w; |
| 11654 | + p->actualWidth[i] = w; |
| 11655 | + } |
| 11656 | + nTotal = nColumn*(nRow+1); |
| 11657 | + for(i=0; i<nTotal; i++){ |
| 11658 | + z = azData[i]; |
| 11659 | + if( z==0 ) z = p->nullValue; |
| 11660 | + n = strlenChar(z); |
| 11661 | + j = i%nColumn; |
| 11662 | + if( n>p->actualWidth[j] ) p->actualWidth[j] = n; |
| 11663 | + } |
| 11664 | + if( p->cMode==MODE_Column ){ |
| 11665 | + colSep = " "; |
| 11666 | + rowSep = "\n"; |
| 11667 | + if( p->showHeader ){ |
| 11668 | + for(i=0; i<nColumn; i++){ |
| 11669 | + w = p->actualWidth[i]; |
| 11670 | + if( p->colWidth[i]<0 ) w = -w; |
| 11671 | + utf8_width_print(p->out, w, azData[i]); |
| 11672 | + fputs(i==nColumn-1?"\n":" ", p->out); |
| 11673 | + } |
| 11674 | + for(i=0; i<nColumn; i++){ |
| 11675 | + print_dashes(p->out, p->actualWidth[i]); |
| 11676 | + fputs(i==nColumn-1?"\n":" ", p->out); |
| 11677 | + } |
| 11678 | + } |
| 11679 | + }else{ |
| 11680 | + colSep = " | "; |
| 11681 | + rowSep = " |\n"; |
| 11682 | + if( p->cMode==MODE_Table ) print_row_separator(p, nColumn, "+"); |
| 11683 | + fputs("| ", p->out); |
| 11684 | + for(i=0; i<nColumn; i++){ |
| 11685 | + w = p->actualWidth[i]; |
| 11686 | + n = strlenChar(azData[i]); |
| 11687 | + utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); |
| 11688 | + fputs(i==nColumn-1?" |\n":" | ", p->out); |
| 11689 | + } |
| 11690 | + print_row_separator(p, nColumn, p->cMode==MODE_Table ? "+" : "|"); |
| 11691 | + } |
| 11692 | + for(i=nColumn, j=0; i<nTotal; i++, j++){ |
| 11693 | + if( j==0 && p->cMode!=MODE_Column ) fputs("| ", p->out); |
| 11694 | + z = azData[i]; |
| 11695 | + if( z==0 ) z = p->nullValue; |
| 11696 | + w = p->actualWidth[j]; |
| 11697 | + if( p->colWidth[j]<0 ) w = -w; |
| 11698 | + utf8_width_print(p->out, w, z); |
| 11699 | + if( j==nColumn-1 ){ |
| 11700 | + fputs(rowSep, p->out); |
| 11701 | + j = -1; |
| 11702 | + }else{ |
| 11703 | + fputs(colSep, p->out); |
| 11704 | + } |
| 11705 | + } |
| 11706 | + if( p->cMode==MODE_Table ){ |
| 11707 | + print_row_separator(p, nColumn, "+"); |
| 11708 | + } |
| 11709 | + sqlite3_free_table(azData); |
| 11710 | +} |
| 11559 | 11711 | |
| 11560 | 11712 | /* |
| 11561 | 11713 | ** Run a prepared statement |
| 11562 | 11714 | */ |
| 11563 | 11715 | static void exec_prepared_stmt( |
| 11564 | 11716 | ShellState *pArg, /* Pointer to ShellState */ |
| 11565 | 11717 | sqlite3_stmt *pStmt /* Statment to run */ |
| 11566 | 11718 | ){ |
| 11567 | 11719 | int rc; |
| 11720 | + |
| 11721 | + if( pArg->cMode==MODE_Column |
| 11722 | + || pArg->cMode==MODE_Table |
| 11723 | + || pArg->cMode==MODE_Markdown |
| 11724 | + ){ |
| 11725 | + exec_prepared_stmt_columnar(pArg, pStmt); |
| 11726 | + return; |
| 11727 | + } |
| 11568 | 11728 | |
| 11569 | 11729 | /* perform the first step. this will tell us if we |
| 11570 | 11730 | ** have a result set or not and how wide it is. |
| 11571 | 11731 | */ |
| 11572 | 11732 | rc = sqlite3_step(pStmt); |
| | @@ -11611,10 +11771,15 @@ |
| 11611 | 11771 | rc = sqlite3_step(pStmt); |
| 11612 | 11772 | } |
| 11613 | 11773 | } |
| 11614 | 11774 | } while( SQLITE_ROW == rc ); |
| 11615 | 11775 | sqlite3_free(pData); |
| 11776 | + if( pArg->cMode==MODE_Table ){ |
| 11777 | + print_row_separator(pArg, nCol, "+"); |
| 11778 | + }else if( pArg->cMode==MODE_Json ){ |
| 11779 | + fputs("]\n", pArg->out); |
| 11780 | + } |
| 11616 | 11781 | } |
| 11617 | 11782 | } |
| 11618 | 11783 | } |
| 11619 | 11784 | |
| 11620 | 11785 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | @@ -16883,19 +17048,28 @@ |
| 16883 | 17048 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 16884 | 17049 | p->mode = MODE_Insert; |
| 16885 | 17050 | set_table_name(p, nArg>=3 ? azArg[2] : "table"); |
| 16886 | 17051 | }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){ |
| 16887 | 17052 | p->mode = MODE_Quote; |
| 17053 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 17054 | + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 16888 | 17055 | }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ |
| 16889 | 17056 | p->mode = MODE_Ascii; |
| 16890 | 17057 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); |
| 16891 | 17058 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); |
| 17059 | + }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){ |
| 17060 | + p->mode = MODE_Markdown; |
| 17061 | + }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){ |
| 17062 | + p->mode = MODE_Table; |
| 17063 | + }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){ |
| 17064 | + p->mode = MODE_Json; |
| 16892 | 17065 | }else if( nArg==1 ){ |
| 16893 | 17066 | raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); |
| 16894 | 17067 | }else{ |
| 16895 | 17068 | raw_printf(stderr, "Error: mode should be one of: " |
| 16896 | | - "ascii column csv html insert line list quote tabs tcl\n"); |
| 17069 | + "ascii column csv html insert json line list markdown " |
| 17070 | + "quote table tabs tcl\n"); |
| 16897 | 17071 | rc = 1; |
| 16898 | 17072 | } |
| 16899 | 17073 | p->cMode = p->mode; |
| 16900 | 17074 | }else |
| 16901 | 17075 | |
| | @@ -17992,11 +18166,11 @@ |
| 17992 | 18166 | utf8_printf(p->out,"%12.12s: ", "rowseparator"); |
| 17993 | 18167 | output_c_string(p->out, p->rowSeparator); |
| 17994 | 18168 | raw_printf(p->out, "\n"); |
| 17995 | 18169 | utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]); |
| 17996 | 18170 | utf8_printf(p->out, "%12.12s: ", "width"); |
| 17997 | | - for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 18171 | + for (i=0;i<p->nWidth;i++) { |
| 17998 | 18172 | raw_printf(p->out, "%d ", p->colWidth[i]); |
| 17999 | 18173 | } |
| 18000 | 18174 | raw_printf(p->out, "\n"); |
| 18001 | 18175 | utf8_printf(p->out, "%12.12s: %s\n", "filename", |
| 18002 | 18176 | p->zDbFilename ? p->zDbFilename : ""); |
| | @@ -18541,11 +18715,15 @@ |
| 18541 | 18715 | #endif |
| 18542 | 18716 | |
| 18543 | 18717 | if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ |
| 18544 | 18718 | int j; |
| 18545 | 18719 | assert( nArg<=ArraySize(azArg) ); |
| 18546 | | - for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ |
| 18720 | + p->nWidth = nArg-1; |
| 18721 | + p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2); |
| 18722 | + if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory(); |
| 18723 | + if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth]; |
| 18724 | + for(j=1; j<nArg; j++){ |
| 18547 | 18725 | p->colWidth[j-1] = (int)integerValue(azArg[j]); |
| 18548 | 18726 | } |
| 18549 | 18727 | }else |
| 18550 | 18728 | |
| 18551 | 18729 | { |
| | @@ -18901,13 +19079,15 @@ |
| 18901 | 19079 | " -heap SIZE Size of heap for memsys3 or memsys5\n" |
| 18902 | 19080 | #endif |
| 18903 | 19081 | " -help show this message\n" |
| 18904 | 19082 | " -html set output mode to HTML\n" |
| 18905 | 19083 | " -interactive force interactive I/O\n" |
| 19084 | + " -json set output mode to 'json'\n" |
| 18906 | 19085 | " -line set output mode to 'line'\n" |
| 18907 | 19086 | " -list set output mode to 'list'\n" |
| 18908 | 19087 | " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" |
| 19088 | + " -markdown set output mode to 'markdown'\n" |
| 18909 | 19089 | #if defined(SQLITE_ENABLE_DESERIALIZE) |
| 18910 | 19090 | " -maxsize N maximum size for a --deserialize database\n" |
| 18911 | 19091 | #endif |
| 18912 | 19092 | " -memtrace trace all memory allocations and deallocations\n" |
| 18913 | 19093 | " -mmap N default mmap size set to N\n" |
| | @@ -18923,10 +19103,11 @@ |
| 18923 | 19103 | " -separator SEP set output column separator. Default: '|'\n" |
| 18924 | 19104 | #ifdef SQLITE_ENABLE_SORTER_REFERENCES |
| 18925 | 19105 | " -sorterref SIZE sorter references threshold size\n" |
| 18926 | 19106 | #endif |
| 18927 | 19107 | " -stats print memory stats before each finalize\n" |
| 19108 | + " -table set output mode to 'table'\n" |
| 18928 | 19109 | " -version show SQLite version\n" |
| 18929 | 19110 | " -vfs NAME use NAME as the default VFS\n" |
| 18930 | 19111 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 18931 | 19112 | " -vfstrace enable tracing of all VFS calls\n" |
| 18932 | 19113 | #endif |
| | @@ -19324,10 +19505,16 @@ |
| 19324 | 19505 | data.mode = MODE_Quote; |
| 19325 | 19506 | }else if( strcmp(z,"-line")==0 ){ |
| 19326 | 19507 | data.mode = MODE_Line; |
| 19327 | 19508 | }else if( strcmp(z,"-column")==0 ){ |
| 19328 | 19509 | data.mode = MODE_Column; |
| 19510 | + }else if( strcmp(z,"-json")==0 ){ |
| 19511 | + data.mode = MODE_Json; |
| 19512 | + }else if( strcmp(z,"-markdown")==0 ){ |
| 19513 | + data.mode = MODE_Markdown; |
| 19514 | + }else if( strcmp(z,"-table")==0 ){ |
| 19515 | + data.mode = MODE_Table; |
| 19329 | 19516 | }else if( strcmp(z,"-csv")==0 ){ |
| 19330 | 19517 | data.mode = MODE_Csv; |
| 19331 | 19518 | memcpy(data.colSeparator,",",2); |
| 19332 | 19519 | #ifdef SQLITE_HAVE_ZLIB |
| 19333 | 19520 | }else if( strcmp(z,"-zip")==0 ){ |
| | @@ -19541,10 +19728,11 @@ |
| 19541 | 19728 | clearTempFile(&data); |
| 19542 | 19729 | #if !SQLITE_SHELL_IS_UTF8 |
| 19543 | 19730 | for(i=0; i<argcToFree; i++) free(argvToFree[i]); |
| 19544 | 19731 | free(argvToFree); |
| 19545 | 19732 | #endif |
| 19733 | + free(data.colWidth); |
| 19546 | 19734 | /* Clear the global data structure so that valgrind will detect memory |
| 19547 | 19735 | ** leaks */ |
| 19548 | 19736 | memset(&data, 0, sizeof(data)); |
| 19549 | 19737 | return rc; |
| 19550 | 19738 | } |
| 19551 | 19739 | |