| | @@ -23,10 +23,11 @@ |
| 23 | 23 | ** ext/misc/shathree.c |
| 24 | 24 | ** ext/misc/sqlar.c |
| 25 | 25 | ** ext/misc/sqlite3_stdio.c |
| 26 | 26 | ** ext/misc/sqlite3_stdio.h |
| 27 | 27 | ** ext/misc/stmtrand.c |
| 28 | +** ext/misc/strdup.c |
| 28 | 29 | ** ext/misc/uint.c |
| 29 | 30 | ** ext/misc/vfstrace.c |
| 30 | 31 | ** ext/misc/windirent.h |
| 31 | 32 | ** ext/misc/zipfile.c |
| 32 | 33 | ** ext/qrf/qrf.c |
| | @@ -733,10 +734,12 @@ |
| 733 | 734 | char *(*xRender)(void*,sqlite3_value*); /* Render a value */ |
| 734 | 735 | int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */ |
| 735 | 736 | void *pRenderArg; /* First argument to the xRender callback */ |
| 736 | 737 | void *pWriteArg; /* First argument to the xWrite callback */ |
| 737 | 738 | char **pzOutput; /* Storage location for output string */ |
| 739 | + /* The following are available in iVersion 2 and later */ |
| 740 | + unsigned char bRowCount; /* Show the number of rows at end of each query */ |
| 738 | 741 | /* Additional fields may be added in the future */ |
| 739 | 742 | }; |
| 740 | 743 | |
| 741 | 744 | /* |
| 742 | 745 | ** Interfaces |
| | @@ -821,10 +824,15 @@ |
| 821 | 824 | #define QRF_SW_On 2 /* This setting is forced on */ |
| 822 | 825 | #define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */ |
| 823 | 826 | #define QRF_No 1 /* Alternate spelling for QRF_SW_Off */ |
| 824 | 827 | #define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */ |
| 825 | 828 | |
| 829 | +/* |
| 830 | +** Additional value allowed for bTitles |
| 831 | +*/ |
| 832 | +#define QRF_Always 3 /* Always show titles, even if no rows */ |
| 833 | + |
| 826 | 834 | /* |
| 827 | 835 | ** Possible alignment values alignment settings |
| 828 | 836 | ** |
| 829 | 837 | ** Horizontal Vertial |
| 830 | 838 | ** ---------- -------- */ |
| | @@ -2527,10 +2535,14 @@ |
| 2527 | 2535 | /* Doubled horizontal lines: */ |
| 2528 | 2536 | #define DBL_24 "\342\225\220" /* U+2550 === */ |
| 2529 | 2537 | #define DBL_123 "\342\225\236" /* U+255e |= */ |
| 2530 | 2538 | #define DBL_134 "\342\225\241" /* U+2561 =| */ |
| 2531 | 2539 | #define DBL_1234 "\342\225\252" /* U+256a =|= */ |
| 2540 | +#define DBL_12 "\342\225\230" /* U+2558 `= */ |
| 2541 | +#define DBL_14 "\342\225\233" /* U+255b =' */ |
| 2542 | +#define DBL_124 "\342\225\247" /* U+2567 ='= */ |
| 2543 | + |
| 2532 | 2544 | |
| 2533 | 2545 | /* Draw horizontal line N characters long using unicode box |
| 2534 | 2546 | ** characters |
| 2535 | 2547 | */ |
| 2536 | 2548 | static void qrfBoxLine(sqlite3_str *pOut, int N, int bDbl){ |
| | @@ -2549,16 +2561,16 @@ |
| 2549 | 2561 | |
| 2550 | 2562 | /* |
| 2551 | 2563 | ** Draw a horizontal separator for a QRF_STYLE_Box table. |
| 2552 | 2564 | */ |
| 2553 | 2565 | static void qrfBoxSeparator( |
| 2554 | | - sqlite3_str *pOut, |
| 2555 | | - qrfColData *p, |
| 2556 | | - const char *zSep1, |
| 2557 | | - const char *zSep2, |
| 2558 | | - const char *zSep3, |
| 2559 | | - int bDbl |
| 2566 | + sqlite3_str *pOut, /* Output to this sqlite3_str */ |
| 2567 | + qrfColData *p, /* Columnar data */ |
| 2568 | + const char *zSep1, /* Left margin */ |
| 2569 | + const char *zSep2, /* Column separator */ |
| 2570 | + const char *zSep3, /* Right margin */ |
| 2571 | + int bDbl /* True for double-lines */ |
| 2560 | 2572 | ){ |
| 2561 | 2573 | int i; |
| 2562 | 2574 | if( p->nCol>0 ){ |
| 2563 | 2575 | int useBorder = p->p->spec.bBorder!=QRF_No; |
| 2564 | 2576 | if( useBorder ){ |
| | @@ -2573,10 +2585,36 @@ |
| 2573 | 2585 | sqlite3_str_appendall(pOut, zSep3); |
| 2574 | 2586 | } |
| 2575 | 2587 | } |
| 2576 | 2588 | sqlite3_str_append(pOut, "\n", 1); |
| 2577 | 2589 | } |
| 2590 | + |
| 2591 | +/* |
| 2592 | +** Draw a horizontal separator for a QRF_STYLE_Column table. |
| 2593 | +** This style of separator is only used for separating the title |
| 2594 | +** from the content. |
| 2595 | +*/ |
| 2596 | +static void qrfColumnSeparator( |
| 2597 | + sqlite3_str *pOut, /* Output to this sqlite3_str */ |
| 2598 | + qrfColData *p, /* Columnar data */ |
| 2599 | + const char *colSep, /* Column separator text */ |
| 2600 | + int szColSep, /* Size of zColSep in bytes */ |
| 2601 | + const char *rowSep, /* Row separator text */ |
| 2602 | + int szRowSep /* Size of zRowSep in bytes */ |
| 2603 | +){ |
| 2604 | + int j; |
| 2605 | + int nColumn = p->nCol; |
| 2606 | + for(j=0; j<nColumn; j++){ |
| 2607 | + sqlite3_str_appendchar(pOut, p->a[j].w, '-'); |
| 2608 | + if( j<nColumn-1 ){ |
| 2609 | + sqlite3_str_append(pOut, colSep, szColSep); |
| 2610 | + }else{ |
| 2611 | + qrfRTrim(pOut); |
| 2612 | + sqlite3_str_append(pOut, rowSep, szRowSep); |
| 2613 | + } |
| 2614 | + } |
| 2615 | +} |
| 2578 | 2616 | |
| 2579 | 2617 | /* |
| 2580 | 2618 | ** Load into pData the default alignment for the body of a table. |
| 2581 | 2619 | */ |
| 2582 | 2620 | static void qrfLoadAlignment(qrfColData *pData, Qrf *p){ |
| | @@ -2831,11 +2869,25 @@ |
| 2831 | 2869 | sqlite3_str *pStr; /* Temporary rendering */ |
| 2832 | 2870 | qrfColData data; /* Columnar layout data */ |
| 2833 | 2871 | int bRTrim; /* Trim trailing space */ |
| 2834 | 2872 | |
| 2835 | 2873 | rc = sqlite3_step(p->pStmt); |
| 2836 | | - if( rc!=SQLITE_ROW || nColumn==0 ){ |
| 2874 | + if( nColumn==0 ){ |
| 2875 | + return; /* Not a query. No results every shown. */ |
| 2876 | + } |
| 2877 | + if( rc==SQLITE_DONE && nColumn>0 ){ |
| 2878 | + /* Empty query */ |
| 2879 | + if( p->spec.bTitles==QRF_Always ){ |
| 2880 | + /* fall through into the columnar logic below */ |
| 2881 | + }else{ |
| 2882 | + if( p->spec.bRowCount==QRF_Yes ){ |
| 2883 | + sqlite3_str_appendf(p->pOut, "(0 rows)\n"); |
| 2884 | + qrfWrite(p); |
| 2885 | + } |
| 2886 | + return; /* No output, other than the row count */ |
| 2887 | + } |
| 2888 | + }else if( rc!=SQLITE_ROW ){ |
| 2837 | 2889 | return; /* No output */ |
| 2838 | 2890 | } |
| 2839 | 2891 | |
| 2840 | 2892 | /* Initialize the data container */ |
| 2841 | 2893 | memset(&data, 0, sizeof(data)); |
| | @@ -2848,12 +2900,12 @@ |
| 2848 | 2900 | } |
| 2849 | 2901 | memset(data.a, 0, nColumn*sizeof(struct qrfPerCol) ); |
| 2850 | 2902 | if( qrfColDataEnlarge(&data) ) return; |
| 2851 | 2903 | assert( data.az!=0 ); |
| 2852 | 2904 | |
| 2853 | | - /* Load the column header names and all cell content into data */ |
| 2854 | | - if( p->spec.bTitles==QRF_Yes ){ |
| 2905 | + /* Load the column header names into data */ |
| 2906 | + if( p->spec.bTitles>=QRF_Yes ){ |
| 2855 | 2907 | unsigned char saved_eText = p->spec.eText; |
| 2856 | 2908 | p->spec.eText = p->spec.eTitle; |
| 2857 | 2909 | memset(data.abNum, 0, nColumn); |
| 2858 | 2910 | for(i=0; i<nColumn; i++){ |
| 2859 | 2911 | const char *z = (const char*)sqlite3_column_name(p->pStmt,i); |
| | @@ -2874,13 +2926,14 @@ |
| 2874 | 2926 | data.n++; |
| 2875 | 2927 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| 2876 | 2928 | if( nNL ) data.bMultiRow = 1; |
| 2877 | 2929 | } |
| 2878 | 2930 | p->spec.eText = saved_eText; |
| 2879 | | - p->nRow++; |
| 2880 | 2931 | } |
| 2881 | | - do{ |
| 2932 | + |
| 2933 | + /* Load query results into data */ |
| 2934 | + while( rc==SQLITE_ROW && p->iErr==SQLITE_OK ){ |
| 2882 | 2935 | if( data.n+nColumn > data.nAlloc ){ |
| 2883 | 2936 | if( qrfColDataEnlarge(&data) ) return; |
| 2884 | 2937 | } |
| 2885 | 2938 | for(i=0; i<nColumn; i++){ |
| 2886 | 2939 | char *z; |
| | @@ -2897,11 +2950,12 @@ |
| 2897 | 2950 | data.n++; |
| 2898 | 2951 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| 2899 | 2952 | if( nNL ) data.bMultiRow = 1; |
| 2900 | 2953 | } |
| 2901 | 2954 | p->nRow++; |
| 2902 | | - }while( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK ); |
| 2955 | + rc = sqlite3_step(p->pStmt); |
| 2956 | + } |
| 2903 | 2957 | if( p->iErr ){ |
| 2904 | 2958 | qrfColDataFree(&data); |
| 2905 | 2959 | return; |
| 2906 | 2960 | } |
| 2907 | 2961 | |
| | @@ -3105,11 +3159,11 @@ |
| 3105 | 3159 | /* Draw either (1) the separator between the title line and the body |
| 3106 | 3160 | ** of the table, or (2) separators between individual rows of the table |
| 3107 | 3161 | ** body. isTitleDataSeparator will be true if we are doing (1). |
| 3108 | 3162 | */ |
| 3109 | 3163 | if( (i==0 || data.bMultiRow) && i+nColumn<data.n ){ |
| 3110 | | - int isTitleDataSeparator = (i==0 && p->spec.bTitles==QRF_Yes); |
| 3164 | + int isTitleDataSeparator = (i==0 && p->spec.bTitles>=QRF_Yes); |
| 3111 | 3165 | if( isTitleDataSeparator ){ |
| 3112 | 3166 | qrfLoadAlignment(&data, p); |
| 3113 | 3167 | } |
| 3114 | 3168 | switch( p->spec.eStyle ){ |
| 3115 | 3169 | case QRF_STYLE_Table: { |
| | @@ -3132,19 +3186,12 @@ |
| 3132 | 3186 | } |
| 3133 | 3187 | break; |
| 3134 | 3188 | } |
| 3135 | 3189 | case QRF_STYLE_Column: { |
| 3136 | 3190 | if( isTitleDataSeparator ){ |
| 3137 | | - for(j=0; j<nColumn; j++){ |
| 3138 | | - sqlite3_str_appendchar(p->pOut, data.a[j].w, '-'); |
| 3139 | | - if( j<nColumn-1 ){ |
| 3140 | | - sqlite3_str_append(p->pOut, colSep, szColSep); |
| 3141 | | - }else{ |
| 3142 | | - qrfRTrim(p->pOut); |
| 3143 | | - sqlite3_str_append(p->pOut, rowSep, szRowSep); |
| 3144 | | - } |
| 3145 | | - } |
| 3191 | + qrfColumnSeparator(p->pOut, &data, colSep, szColSep, |
| 3192 | + rowSep, szRowSep); |
| 3146 | 3193 | }else if( data.bMultiRow ){ |
| 3147 | 3194 | qrfRTrim(p->pOut); |
| 3148 | 3195 | sqlite3_str_append(p->pOut, "\n", 1); |
| 3149 | 3196 | } |
| 3150 | 3197 | break; |
| | @@ -3152,20 +3199,39 @@ |
| 3152 | 3199 | } |
| 3153 | 3200 | } |
| 3154 | 3201 | } |
| 3155 | 3202 | |
| 3156 | 3203 | /* Draw the line across the bottom of the table */ |
| 3157 | | - if( p->spec.bBorder!=QRF_No ){ |
| 3204 | + if( p->spec.bBorder!=QRF_No || p->nRow==0 ){ |
| 3158 | 3205 | switch( p->spec.eStyle ){ |
| 3159 | 3206 | case QRF_STYLE_Box: |
| 3160 | | - qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0); |
| 3207 | + if( p->nRow>0 ){ |
| 3208 | + qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0); |
| 3209 | + }else{ |
| 3210 | + qrfBoxSeparator(p->pOut, &data, DBL_12, DBL_124, DBL_14, 1); |
| 3211 | + } |
| 3161 | 3212 | break; |
| 3162 | 3213 | case QRF_STYLE_Table: |
| 3163 | 3214 | qrfRowSeparator(p->pOut, &data, '+'); |
| 3164 | 3215 | break; |
| 3216 | + case QRF_STYLE_Markdown: |
| 3217 | + if( p->nRow==0 ){ |
| 3218 | + qrfRowSeparator(p->pOut, &data, '|'); |
| 3219 | + } |
| 3220 | + break; |
| 3221 | + case QRF_STYLE_Column: |
| 3222 | + if( p->nRow==0 ){ |
| 3223 | + qrfColumnSeparator(p->pOut, &data, colSep, szColSep, |
| 3224 | + rowSep, szRowSep); |
| 3225 | + } |
| 3226 | + break; |
| 3165 | 3227 | } |
| 3166 | 3228 | } |
| 3229 | + if( p->spec.bRowCount==QRF_Yes ){ |
| 3230 | + sqlite3_int64 n = p->nRow; |
| 3231 | + sqlite3_str_appendf(p->pOut, "(%lld row%s)\n", n, n==1 ? "" : "s"); |
| 3232 | + } |
| 3167 | 3233 | qrfWrite(p); |
| 3168 | 3234 | |
| 3169 | 3235 | qrfColDataFree(&data); |
| 3170 | 3236 | return; |
| 3171 | 3237 | } |
| | @@ -3412,10 +3478,49 @@ |
| 3412 | 3478 | sqlite3_str_append(p->pOut, ":", 1); |
| 3413 | 3479 | qrfRenderValue(p, p->pOut, i); |
| 3414 | 3480 | } |
| 3415 | 3481 | qrfWrite(p); |
| 3416 | 3482 | } |
| 3483 | + |
| 3484 | +/* |
| 3485 | +** Render a title row (a row containing column names) if the spec |
| 3486 | +** calls for one and if it makes sense for the eStyle. Title rows |
| 3487 | +** do not make sense for some eStyles, such as QRF_STYLE_Off, |
| 3488 | +** QRF_STYLE_Count, QRF_STYLE_Json, and similar. |
| 3489 | +*/ |
| 3490 | +static void qrfSimpleTitle(Qrf *p){ |
| 3491 | + assert( p->nRow==0 ); |
| 3492 | + assert( p->spec.bTitles>=QRF_Yes ); |
| 3493 | + switch( p->spec.eStyle ){ |
| 3494 | + case QRF_STYLE_Html: { |
| 3495 | + int i; |
| 3496 | + sqlite3_str_append(p->pOut, "<TR>", 4); |
| 3497 | + for(i=0; i<p->nCol; i++){ |
| 3498 | + const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3499 | + sqlite3_str_append(p->pOut, "\n<TH>", 5); |
| 3500 | + qrfEncodeText(p, p->pOut, zCName); |
| 3501 | + } |
| 3502 | + sqlite3_str_append(p->pOut, "\n</TR>\n", 7); |
| 3503 | + break; |
| 3504 | + } |
| 3505 | + case QRF_STYLE_Quote: |
| 3506 | + case QRF_STYLE_List: { |
| 3507 | + int i; |
| 3508 | + int saved_eText = p->spec.eText; |
| 3509 | + p->spec.eText = p->spec.eTitle; |
| 3510 | + for(i=0; i<p->nCol; i++){ |
| 3511 | + const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3512 | + if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
| 3513 | + qrfEncodeText(p, p->pOut, zCName); |
| 3514 | + } |
| 3515 | + sqlite3_str_appendall(p->pOut, p->spec.zRowSep); |
| 3516 | + qrfWrite(p); |
| 3517 | + p->spec.eText = saved_eText; |
| 3518 | + break; |
| 3519 | + } |
| 3520 | + } |
| 3521 | +} |
| 3417 | 3522 | |
| 3418 | 3523 | /* |
| 3419 | 3524 | ** Render a single row of output for non-columnar styles - any |
| 3420 | 3525 | ** style that lets us render row by row as the content is received |
| 3421 | 3526 | ** from the query. |
| | @@ -3445,18 +3550,12 @@ |
| 3445 | 3550 | } |
| 3446 | 3551 | qrfOneJsonRow(p); |
| 3447 | 3552 | break; |
| 3448 | 3553 | } |
| 3449 | 3554 | case QRF_STYLE_Html: { |
| 3450 | | - if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){ |
| 3451 | | - sqlite3_str_append(p->pOut, "<TR>", 4); |
| 3452 | | - for(i=0; i<p->nCol; i++){ |
| 3453 | | - const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3454 | | - sqlite3_str_append(p->pOut, "\n<TH>", 5); |
| 3455 | | - qrfEncodeText(p, p->pOut, zCName); |
| 3456 | | - } |
| 3457 | | - sqlite3_str_append(p->pOut, "\n</TR>\n", 7); |
| 3555 | + if( p->nRow==0 && p->spec.bTitles>=QRF_Yes ){ |
| 3556 | + qrfSimpleTitle(p); |
| 3458 | 3557 | } |
| 3459 | 3558 | sqlite3_str_append(p->pOut, "<TR>", 4); |
| 3460 | 3559 | for(i=0; i<p->nCol; i++){ |
| 3461 | 3560 | sqlite3_str_append(p->pOut, "\n<TD>", 5); |
| 3462 | 3561 | qrfRenderValue(p, p->pOut, i); |
| | @@ -3476,11 +3575,11 @@ |
| 3476 | 3575 | if( qrf_need_quote(p->spec.zTableName) ){ |
| 3477 | 3576 | sqlite3_str_appendf(p->pOut,"INSERT INTO \"%w\"",p->spec.zTableName); |
| 3478 | 3577 | }else{ |
| 3479 | 3578 | sqlite3_str_appendf(p->pOut,"INSERT INTO %s",p->spec.zTableName); |
| 3480 | 3579 | } |
| 3481 | | - if( p->spec.bTitles==QRF_Yes ){ |
| 3580 | + if( p->spec.bTitles>=QRF_Yes ){ |
| 3482 | 3581 | for(i=0; i<p->nCol; i++){ |
| 3483 | 3582 | const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3484 | 3583 | if( qrf_need_quote(zCName) ){ |
| 3485 | 3584 | sqlite3_str_appendf(p->pOut, "%c\"%w\"", |
| 3486 | 3585 | i==0 ? '(' : ',', zCName); |
| | @@ -3576,21 +3675,12 @@ |
| 3576 | 3675 | if( zEqpLine[0]=='-' ) qrfEqpRender(p, 0); |
| 3577 | 3676 | qrfEqpAppend(p, iEqpId, iParentId, zEqpLine); |
| 3578 | 3677 | break; |
| 3579 | 3678 | } |
| 3580 | 3679 | default: { /* QRF_STYLE_List */ |
| 3581 | | - if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){ |
| 3582 | | - int saved_eText = p->spec.eText; |
| 3583 | | - p->spec.eText = p->spec.eTitle; |
| 3584 | | - for(i=0; i<p->nCol; i++){ |
| 3585 | | - const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3586 | | - if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
| 3587 | | - qrfEncodeText(p, p->pOut, zCName); |
| 3588 | | - } |
| 3589 | | - sqlite3_str_appendall(p->pOut, p->spec.zRowSep); |
| 3590 | | - qrfWrite(p); |
| 3591 | | - p->spec.eText = saved_eText; |
| 3680 | + if( p->nRow==0 && p->spec.bTitles>=QRF_Yes ){ |
| 3681 | + qrfSimpleTitle(p); |
| 3592 | 3682 | } |
| 3593 | 3683 | for(i=0; i<p->nCol; i++){ |
| 3594 | 3684 | if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
| 3595 | 3685 | qrfRenderValue(p, p->pOut, i); |
| 3596 | 3686 | } |
| | @@ -3612,11 +3702,11 @@ |
| 3612 | 3702 | char **pzErr /* Write errors here */ |
| 3613 | 3703 | ){ |
| 3614 | 3704 | size_t sz; /* Size of pSpec[], based on pSpec->iVersion */ |
| 3615 | 3705 | memset(p, 0, sizeof(*p)); |
| 3616 | 3706 | p->pzErr = pzErr; |
| 3617 | | - if( pSpec->iVersion>1 ){ |
| 3707 | + if( pSpec->iVersion>2 ){ |
| 3618 | 3708 | qrfError(p, SQLITE_ERROR, |
| 3619 | 3709 | "unusable sqlite3_qrf_spec.iVersion (%d)", |
| 3620 | 3710 | pSpec->iVersion); |
| 3621 | 3711 | return; |
| 3622 | 3712 | } |
| | @@ -3640,10 +3730,13 @@ |
| 3640 | 3730 | if( p->spec.eStyle>QRF_STYLE_Table ) p->spec.eStyle = QRF_Auto; |
| 3641 | 3731 | if( p->spec.eEsc>QRF_ESC_Symbol ) p->spec.eEsc = QRF_Auto; |
| 3642 | 3732 | if( p->spec.eText>QRF_TEXT_Relaxed ) p->spec.eText = QRF_Auto; |
| 3643 | 3733 | if( p->spec.eTitle>QRF_TEXT_Relaxed ) p->spec.eTitle = QRF_Auto; |
| 3644 | 3734 | if( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto; |
| 3735 | + if( pSpec->iVersion<=1 ){ |
| 3736 | + p->spec.bRowCount = 0; |
| 3737 | + } |
| 3645 | 3738 | qrf_reinit: |
| 3646 | 3739 | switch( p->spec.eStyle ){ |
| 3647 | 3740 | case QRF_Auto: { |
| 3648 | 3741 | switch( sqlite3_stmt_isexplain(pStmt) ){ |
| 3649 | 3742 | case 0: p->spec.eStyle = QRF_STYLE_Box; break; |
| | @@ -3898,10 +3991,34 @@ |
| 3898 | 3991 | default: { |
| 3899 | 3992 | /* Non-columnar modes where the output can occur after each row |
| 3900 | 3993 | ** of result is received */ |
| 3901 | 3994 | while( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 3902 | 3995 | qrfOneSimpleRow(&qrf); |
| 3996 | + } |
| 3997 | + if( qrf.nCol>0 && qrf.nRow==0 && qrf.spec.bTitles==QRF_Always ){ |
| 3998 | + qrfSimpleTitle(&qrf); |
| 3999 | + } |
| 4000 | + if( qrf.nCol>0 && qrf.spec.bRowCount==QRF_Yes ){ |
| 4001 | + const char *zPlural = qrf.nRow==1 ? "" : "s"; |
| 4002 | + switch( qrf.spec.eStyle ){ |
| 4003 | + case QRF_STYLE_Line: |
| 4004 | + if( qrf.nRow>0 ) sqlite3_str_append(qrf.pOut, "\n", 1); |
| 4005 | + /* Fall through */ |
| 4006 | + case QRF_STYLE_Csv: |
| 4007 | + case QRF_STYLE_List: |
| 4008 | + case QRF_STYLE_Quote: |
| 4009 | + sqlite3_str_appendf(qrf.pOut,"(%lld row%s)\n",qrf.nRow,zPlural); |
| 4010 | + break; |
| 4011 | + case QRF_STYLE_Html: |
| 4012 | + sqlite3_str_appendf(qrf.pOut,"<!-- %lld row%s -->\n", |
| 4013 | + qrf.nRow, zPlural); |
| 4014 | + break; |
| 4015 | + case QRF_STYLE_Insert: |
| 4016 | + sqlite3_str_appendf(qrf.pOut,"/* %lld row%s inserted */\n", |
| 4017 | + qrf.nRow, zPlural); |
| 4018 | + break; |
| 4019 | + } |
| 3903 | 4020 | } |
| 3904 | 4021 | break; |
| 3905 | 4022 | } |
| 3906 | 4023 | } |
| 3907 | 4024 | qrfResetStmt(&qrf); |
| | @@ -10602,11 +10719,15 @@ |
| 10602 | 10719 | sqlite3_result_error_nomem(ctx); |
| 10603 | 10720 | return SQLITE_NOMEM; |
| 10604 | 10721 | } |
| 10605 | 10722 | } |
| 10606 | 10723 | |
| 10607 | | - sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT); |
| 10724 | + if( n>0 ){ |
| 10725 | + sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT); |
| 10726 | + }else{ |
| 10727 | + sqlite3_result_null(ctx); |
| 10728 | + } |
| 10608 | 10729 | if( aBuf!=aStatic ) sqlite3_free(aBuf); |
| 10609 | 10730 | #endif |
| 10610 | 10731 | }else{ |
| 10611 | 10732 | readFileContents(ctx, pCur->zPath); |
| 10612 | 10733 | } |
| | @@ -17600,10 +17721,13 @@ |
| 17600 | 17721 | else if( (c>='A' && c<='Z') || (c>='a' && c<='z') ){ |
| 17601 | 17722 | while( (z[iRet]>='A' && z[iRet]<='Z') || (z[iRet]>='a' && z[iRet]<='z') ){ |
| 17602 | 17723 | iRet++; |
| 17603 | 17724 | } |
| 17604 | 17725 | } |
| 17726 | + else if( c==0 ){ |
| 17727 | + iRet = 0; |
| 17728 | + } |
| 17605 | 17729 | |
| 17606 | 17730 | return iRet; |
| 17607 | 17731 | } |
| 17608 | 17732 | |
| 17609 | 17733 | /* |
| | @@ -18039,10 +18163,27 @@ |
| 18039 | 18163 | intckFinalize(p, pStmt); |
| 18040 | 18164 | |
| 18041 | 18165 | if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 1"); |
| 18042 | 18166 | return zRet; |
| 18043 | 18167 | } |
| 18168 | + |
| 18169 | +/* |
| 18170 | +** Register or unregister special SQL functions implemented by intck. |
| 18171 | +** |
| 18172 | +** Normally the custom SQL functions used by intck are only available |
| 18173 | +** in between sqlite3_intck_open() and sqlite3_intck_close(). However, |
| 18174 | +** for testing and debugging, it is sometimes useful to make those |
| 18175 | +** functions available generally. This routine provides as a separate |
| 18176 | +** interface in order to provide that capability. |
| 18177 | +*/ |
| 18178 | +int sqlite3_intck_register(sqlite3 *db, int bCreate){ |
| 18179 | + int rc; |
| 18180 | + rc = sqlite3_create_function(db, "parse_create_index", |
| 18181 | + 2, SQLITE_UTF8, 0, bCreate ? intckParseCreateIndexFunc : 0, 0, 0 |
| 18182 | + ); |
| 18183 | + return rc; |
| 18184 | +} |
| 18044 | 18185 | |
| 18045 | 18186 | /* |
| 18046 | 18187 | ** Open a new integrity-check object. |
| 18047 | 18188 | */ |
| 18048 | 18189 | int sqlite3_intck_open( |
| | @@ -18061,13 +18202,11 @@ |
| 18061 | 18202 | }else{ |
| 18062 | 18203 | memset(pNew, 0, sizeof(*pNew)); |
| 18063 | 18204 | pNew->db = db; |
| 18064 | 18205 | pNew->zDb = (const char*)&pNew[1]; |
| 18065 | 18206 | memcpy(&pNew[1], zDb, nDb+1); |
| 18066 | | - rc = sqlite3_create_function(db, "parse_create_index", |
| 18067 | | - 2, SQLITE_UTF8, 0, intckParseCreateIndexFunc, 0, 0 |
| 18068 | | - ); |
| 18207 | + rc = sqlite3_intck_register(db, 1); |
| 18069 | 18208 | if( rc!=SQLITE_OK ){ |
| 18070 | 18209 | sqlite3_intck_close(pNew); |
| 18071 | 18210 | pNew = 0; |
| 18072 | 18211 | } |
| 18073 | 18212 | } |
| | @@ -18080,13 +18219,11 @@ |
| 18080 | 18219 | ** Free the integrity-check object. |
| 18081 | 18220 | */ |
| 18082 | 18221 | void sqlite3_intck_close(sqlite3_intck *p){ |
| 18083 | 18222 | if( p ){ |
| 18084 | 18223 | sqlite3_finalize(p->pCheck); |
| 18085 | | - sqlite3_create_function( |
| 18086 | | - p->db, "parse_create_index", 1, SQLITE_UTF8, 0, 0, 0, 0 |
| 18087 | | - ); |
| 18224 | + sqlite3_intck_register(p->db, 0); |
| 18088 | 18225 | sqlite3_free(p->zObj); |
| 18089 | 18226 | sqlite3_free(p->zKey); |
| 18090 | 18227 | sqlite3_free(p->zTestSql); |
| 18091 | 18228 | sqlite3_free(p->zErr); |
| 18092 | 18229 | sqlite3_free(p->zMessage); |
| | @@ -20370,10 +20507,122 @@ |
| 20370 | 20507 | 0, diskusedFunc, 0, 0); |
| 20371 | 20508 | return rc; |
| 20372 | 20509 | } |
| 20373 | 20510 | |
| 20374 | 20511 | /************************* End ext/misc/diskused.c ********************/ |
| 20512 | +/************************* Begin ext/misc/strdup.c ******************/ |
| 20513 | +/* |
| 20514 | +** 2026-07-04 |
| 20515 | +** |
| 20516 | +** The author disclaims copyright to this source code. In place of |
| 20517 | +** a legal notice, here is a blessing: |
| 20518 | +** |
| 20519 | +** May you do good and not evil. |
| 20520 | +** May you find forgiveness for yourself and forgive others. |
| 20521 | +** May you share freely, never taking more than you give. |
| 20522 | +** |
| 20523 | +****************************************************************************** |
| 20524 | +** |
| 20525 | +** Since this module was written (coincidentally) on the 250th anniversary |
| 20526 | +** of the signing of the Declaration of Independence of The United States, |
| 20527 | +** it seems fitting to quote from that document: |
| 20528 | +** |
| 20529 | +** We hold these truths to be self-evident, that all men are created |
| 20530 | +** equal, that they are endowed by their Creator with certain unalienable |
| 20531 | +** Rights, that among these are Life, Liberty and the pursuit of Happiness |
| 20532 | +** - That to secure these rights, Governments are instituted among Men, |
| 20533 | +** deriving their just powers from the consent of the governed,.... |
| 20534 | +** |
| 20535 | +** Two core ideas that (1) rights come from God and not from government, |
| 20536 | +** and that (2) there should not be special privileged classes of people |
| 20537 | +** were radical thoughts then, and are indeed disputed even today. Many |
| 20538 | +** people still hold that human rights derive from the beneficence of |
| 20539 | +** government and that certain classes of people have special rights and |
| 20540 | +** privileges not available to all. Yet, were it not for the crazy ideas |
| 20541 | +** espoused in the original Declaration of Independence, this software |
| 20542 | +** project, and indeed most of modern technology, would not exist. |
| 20543 | +** |
| 20544 | +** Therefore let us give thanks for the bold leadership and vision |
| 20545 | +** demonstrated by the authors of the American Revolution, 250 years ago |
| 20546 | +** this day. |
| 20547 | +** |
| 20548 | +****************************************************************************** |
| 20549 | +** |
| 20550 | +** Implementation of the strdup() SQL function. strdup() makes a copy |
| 20551 | +** of its argument (a string or a BLOB) into memory obtained directly |
| 20552 | +** from system malloc() (not from sqlite3_malloc()) and sized exactly |
| 20553 | +** to hold the string or BLOB. This is intended for testing purposes, |
| 20554 | +** particularly testing with ASAN. This function serves no practical |
| 20555 | +** purpose beyond testing and not not intended for production use. |
| 20556 | +** |
| 20557 | +** NULL, BLOB, and TEXT values come through as NULL, BLOB, and TEXT, |
| 20558 | +** respectively. Numeric values are rendered as strings and come out |
| 20559 | +** as text. |
| 20560 | +*/ |
| 20561 | +/* #include "sqlite3ext.h" */ |
| 20562 | +SQLITE_EXTENSION_INIT1 |
| 20563 | +#include <assert.h> |
| 20564 | +#include <string.h> |
| 20565 | +#include <stdlib.h> |
| 20566 | + |
| 20567 | +/* |
| 20568 | +** Make a copy of a string or BLOB in memory obtained from malloc(). |
| 20569 | +*/ |
| 20570 | +static void strdupfunc( |
| 20571 | + sqlite3_context *context, |
| 20572 | + int argc, |
| 20573 | + sqlite3_value **argv |
| 20574 | +){ |
| 20575 | + const unsigned char *zIn; |
| 20576 | + int nIn; |
| 20577 | + unsigned char *zOut; |
| 20578 | + |
| 20579 | + assert( argc==1 ); |
| 20580 | + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 20581 | + if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ |
| 20582 | + zIn = (const unsigned char*)sqlite3_value_blob(argv[0]); |
| 20583 | + nIn = sqlite3_value_bytes(argv[0]); |
| 20584 | + zOut = malloc( nIn==0 ? 1 : nIn ); |
| 20585 | + if( zOut==0 ){ |
| 20586 | + sqlite3_result_error_nomem(context); |
| 20587 | + return; |
| 20588 | + } |
| 20589 | + if( nIn>0 ) memcpy(zOut, zIn, nIn); |
| 20590 | + sqlite3_result_blob(context, zOut, nIn, free); |
| 20591 | + }else{ |
| 20592 | + zIn = (const unsigned char*)sqlite3_value_text(argv[0]); |
| 20593 | + if( zIn==0 ) return; |
| 20594 | + nIn = (int)strlen((char*)zIn); |
| 20595 | + zOut = malloc( nIn+1 ); |
| 20596 | + if( zOut==0 ){ |
| 20597 | + sqlite3_result_error_nomem(context); |
| 20598 | + return; |
| 20599 | + } |
| 20600 | + memcpy(zOut, zIn, nIn); |
| 20601 | + zOut[nIn] = 0; |
| 20602 | + sqlite3_result_text64(context, (char*)zOut, nIn, free, |
| 20603 | + SQLITE_UTF8_ZT); |
| 20604 | + } |
| 20605 | +} |
| 20606 | + |
| 20607 | +#ifdef _WIN32 |
| 20608 | + |
| 20609 | +#endif |
| 20610 | +int sqlite3_strdup_init( |
| 20611 | + sqlite3 *db, |
| 20612 | + char **pzErrMsg, |
| 20613 | + const sqlite3_api_routines *pApi |
| 20614 | +){ |
| 20615 | + int rc = SQLITE_OK; |
| 20616 | + SQLITE_EXTENSION_INIT2(pApi); |
| 20617 | + (void)pzErrMsg; /* Unused parameter */ |
| 20618 | + rc = sqlite3_create_function(db, "strdup", 1, SQLITE_UTF8, |
| 20619 | + 0, strdupfunc, 0, 0); |
| 20620 | + return rc; |
| 20621 | +} |
| 20622 | + |
| 20623 | +/************************* End ext/misc/strdup.c ********************/ |
| 20375 | 20624 | |
| 20376 | 20625 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 20377 | 20626 | #define SQLITE_SHELL_HAVE_RECOVER 1 |
| 20378 | 20627 | #else |
| 20379 | 20628 | #define SQLITE_SHELL_HAVE_RECOVER 0 |
| | @@ -24837,11 +25086,11 @@ |
| 24837 | 25086 | unsigned char eHdr; /* Default header encoding. */ |
| 24838 | 25087 | unsigned char eBlob; /* Default blob encoding. */ |
| 24839 | 25088 | unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */ |
| 24840 | 25089 | unsigned char eStyle; /* Underlying QRF style */ |
| 24841 | 25090 | unsigned char eCx; /* 0: other, 1: line, 2: columnar */ |
| 24842 | | - unsigned char mFlg; /* Flags. 1=border-off 2=split-column */ |
| 25091 | + unsigned char mFlg; /* Flags. 1=border-off 2=split-column 4=rowcount */ |
| 24843 | 25092 | }; |
| 24844 | 25093 | |
| 24845 | 25094 | /* String constants used by built-in modes */ |
| 24846 | 25095 | static const char *aModeStr[] = |
| 24847 | 25096 | /* 0 1 2 3 4 5 6 7 8 */ |
| | @@ -24850,45 +25099,45 @@ |
| 24850 | 25099 | /* 9 10 11 12 13 */ |
| 24851 | 25100 | |
| 24852 | 25101 | static const ModeInfo aModeInfo[] = { |
| 24853 | 25102 | /* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */ |
| 24854 | 25103 | { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 }, |
| 24855 | | - { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 }, |
| 25104 | + { "box", 0, 0, 9, 1, 1, 0, 3, 1, 2, 0 }, |
| 24856 | 25105 | { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 }, |
| 24857 | | - { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 }, |
| 25106 | + { "column", 0, 0, 9, 1, 1, 0, 3, 2, 2, 0 }, |
| 24858 | 25107 | { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, |
| 24859 | 25108 | { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 }, |
| 24860 | | - { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }, |
| 25109 | + { "html", 0, 0, 9, 4, 4, 0, 3, 7, 0, 0 }, |
| 24861 | 25110 | { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 }, |
| 24862 | 25111 | { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 }, |
| 24863 | 25112 | { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 }, |
| 24864 | 25113 | { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 }, |
| 24865 | 25114 | { "line", 13, 1, 9, 1, 1, 0, 0, 11, 1, 0 }, |
| 24866 | 25115 | { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 }, |
| 24867 | | - { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 }, |
| 25116 | + { "markdown", 0, 0, 9, 1, 1, 0, 3, 13, 2, 0 }, |
| 24868 | 25117 | { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 }, |
| 24869 | | - { "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 }, |
| 24870 | | - { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 }, |
| 25118 | + { "psql", 0, 0, 9, 1, 1, 0, 3, 19, 2, 5 }, |
| 25119 | + { "qbox", 0, 0, 10, 2, 1, 0, 3, 1, 2, 0 }, |
| 24871 | 25120 | { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 }, |
| 24872 | 25121 | { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 }, |
| 24873 | | - { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 }, |
| 25122 | + { "table", 0, 0, 9, 1, 1, 0, 3, 19, 2, 0 }, |
| 24874 | 25123 | { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 }, |
| 24875 | 25124 | { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 }, |
| 24876 | | - { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 } |
| 24877 | | -}; /* | / / | / / | | \ |
| 24878 | | - ** | / / | / / | | \_ 2: columnar |
| 24879 | | - ** Index into aModeStr[] | / / | | 1: line |
| 24880 | | - ** | / / | | 0: other |
| 24881 | | - ** | / / | \ |
| 24882 | | - ** text encoding |/ | show | \ |
| 24883 | | - ** v-------------------' | hdrs? | The QRF style |
| 24884 | | - ** 0: n/a blob | v-----' |
| 24885 | | - ** 1: plain v----------' 0: n/a |
| 24886 | | - ** 2: sql 0: auto 1: no |
| 24887 | | - ** 3: csv 1: as-text 2: yes |
| 24888 | | - ** 4: html 2: sql |
| 24889 | | - ** 5: c 3: hex |
| 25125 | + { "www", 0, 0, 9, 4, 4, 0, 3, 7, 0, 0 } |
| 25126 | +}; /* | / / | / / | | / \____ |
| 25127 | + ** | / / | / / | | / | |
| 25128 | + ** Index into aModeStr[] | / / | | 2: columnar | |
| 25129 | + ** | / / | | 1: line | |
| 25130 | + ** | / / | | 0: other | |
| 25131 | + ** text encoding |/ | show | | | |
| 25132 | + ** v-------------------' | hdrs? | The QRF style | |
| 25133 | + ** 0: n/a blob | v-----' / |
| 25134 | + ** 1: plain v----------' 0: n/a _________/ |
| 25135 | + ** 2: sql 0: auto 1: no | |
| 25136 | + ** 3: csv 1: as-text 2: yes 1: border-off |
| 25137 | + ** 4: html 2: sql 3: always 2: split-column |
| 25138 | + ** 5: c 3: hex 4: rowcount |
| 24890 | 25139 | ** 6: json 4: c |
| 24891 | 25140 | ** 5: json |
| 24892 | 25141 | ** 6: size |
| 24893 | 25142 | ******************************************************************/ |
| 24894 | 25143 | |
| | @@ -26316,11 +26565,11 @@ |
| 26316 | 26565 | free(p->spec.zColumnSep); |
| 26317 | 26566 | free(p->spec.zRowSep); |
| 26318 | 26567 | free(p->spec.zTableName); |
| 26319 | 26568 | free(p->spec.zNull); |
| 26320 | 26569 | memset(p, 0, sizeof(*p)); |
| 26321 | | - p->spec.iVersion = 1; |
| 26570 | + p->spec.iVersion = 2; |
| 26322 | 26571 | p->autoExplain = autoExplain; |
| 26323 | 26572 | } |
| 26324 | 26573 | |
| 26325 | 26574 | /* |
| 26326 | 26575 | ** Duplicate Mode pSrc into pDest. pDest is assumed to be |
| | @@ -26441,10 +26690,15 @@ |
| 26441 | 26690 | pM->spec.bSplitColumn = QRF_Yes; |
| 26442 | 26691 | pM->bAutoScreenWidth = 1; |
| 26443 | 26692 | }else{ |
| 26444 | 26693 | pM->spec.bSplitColumn = QRF_No; |
| 26445 | 26694 | } |
| 26695 | + if( pI->mFlg & 0x04 ){ |
| 26696 | + pM->spec.bRowCount = QRF_Yes; |
| 26697 | + }else{ |
| 26698 | + pM->spec.bRowCount = QRF_No; |
| 26699 | + } |
| 26446 | 26700 | }else if( eMode>=MODE_USER && eMode-MODE_USER<p->nSavedModes ){ |
| 26447 | 26701 | modeFree(&p->mode); |
| 26448 | 26702 | modeDup(&p->mode, &p->aSavedModes[eMode-MODE_USER].mode); |
| 26449 | 26703 | }else if( eMode==MODE_BATCH ){ |
| 26450 | 26704 | u8 mFlags = p->mode.mFlags; |
| | @@ -26470,11 +26724,11 @@ |
| 26470 | 26724 | /* |
| 26471 | 26725 | ** Set the mode to the default. It assumed that the mode has |
| 26472 | 26726 | ** already been freed and zeroed prior to calling this routine. |
| 26473 | 26727 | */ |
| 26474 | 26728 | static void modeDefault(ShellState *p){ |
| 26475 | | - p->mode.spec.iVersion = 1; |
| 26729 | + p->mode.spec.iVersion = 2; |
| 26476 | 26730 | p->mode.autoExplain = 1; |
| 26477 | 26731 | if( stdin_is_interactive || stdout_is_console ){ |
| 26478 | 26732 | modeChange(p, MODE_TTY); |
| 26479 | 26733 | }else{ |
| 26480 | 26734 | modeChange(p, MODE_BATCH); |
| | @@ -28836,10 +29090,11 @@ |
| 28836 | 29090 | " --quote ARG Enable/disable quoting of text. ARG can be\n" |
| 28837 | 29091 | " \"off\", \"on\", \"sql\", \"relaxed\", \"csv\", \"html\",\n" |
| 28838 | 29092 | " \"tcl\", or \"json\". \"off\" means show the text as-is.\n" |
| 28839 | 29093 | " \"on\" is an alias for \"sql\".\n" |
| 28840 | 29094 | " --reset Changes all mode settings back to their default.\n" |
| 29095 | +" --rowcount BOOLEAN Show \"(N rows)\" at the end of query results.\n" |
| 28841 | 29096 | " --rowsep STRING Use STRING as the row separator\n" |
| 28842 | 29097 | " --sw|--screenwidth N Declare the screen width of the output device\n" |
| 28843 | 29098 | " to be N characters. An attempt may be made to\n" |
| 28844 | 29099 | " wrap output text to fit within this limit. Zero\n" |
| 28845 | 29100 | " means \"no limit\". Or N can be \"auto\" to set the\n" |
| | @@ -28847,11 +29102,11 @@ |
| 28847 | 29102 | " --tablename NAME Set the name of the table for \"insert\" mode.\n" |
| 28848 | 29103 | " --tag NAME Save mode to the left as NAME.\n" |
| 28849 | 29104 | " --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.\n" |
| 28850 | 29105 | " --title ARG Whether or not to show column headers, and if so\n" |
| 28851 | 29106 | " how to encode them. ARG can be \"off\", \"on\",\n" |
| 28852 | | -" \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n" |
| 29107 | +" \"always\", \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n" |
| 28853 | 29108 | " --titlelimit N Limit the length of column titles to N characters.\n" |
| 28854 | 29109 | " -v|--verbose Verbose output\n" |
| 28855 | 29110 | " --widths LIST Set the columns widths for columnar modes. The\n" |
| 28856 | 29111 | " argument is a list of integers, one for each\n" |
| 28857 | 29112 | " column. A \"0\" width means use a dynamic width\n" |
| | @@ -29553,10 +29808,11 @@ |
| 29553 | 29808 | sqlite3_base85_init(p->db, 0, 0); |
| 29554 | 29809 | sqlite3_regexp_init(p->db, 0, 0); |
| 29555 | 29810 | sqlite3_ieee_init(p->db, 0, 0); |
| 29556 | 29811 | sqlite3_series_init(p->db, 0, 0); |
| 29557 | 29812 | sqlite3_diskused_init(p->db, 0, 0); |
| 29813 | + sqlite3_strdup_init(p->db, 0, 0); |
| 29558 | 29814 | #ifndef SQLITE_SHELL_FIDDLE |
| 29559 | 29815 | sqlite3_fileio_init(p->db, 0, 0); |
| 29560 | 29816 | sqlite3_completion_init(p->db, 0, 0); |
| 29561 | 29817 | #endif |
| 29562 | 29818 | #ifdef SQLITE_HAVE_ZLIB |
| | @@ -33007,14 +33263,16 @@ |
| 33007 | 33263 | /* |
| 33008 | 33264 | ** This function computes what to show the user about the configured |
| 33009 | 33265 | ** titles (or column-names). Output is an integer between 0 and 3: |
| 33010 | 33266 | ** |
| 33011 | 33267 | ** 0: The titles do not matter. Never show anything. |
| 33012 | | -** 1: Show "--titles off" |
| 33013 | | -** 2: Show "--titles on" |
| 33014 | | -** 3: Show "--title VALUE" where VALUE is an encoding method |
| 33268 | +** 1: Show "--title off" |
| 33269 | +** 2: Show "--title on" |
| 33270 | +** 3: Show "--title always" |
| 33271 | +** 4: Show "--title VALUE" where VALUE is an encoding method |
| 33015 | 33272 | ** to use, one of: plain sql csv html tcl json |
| 33273 | +** 5: Show "--title VALUE" as in 3, followed by "--title always" |
| 33016 | 33274 | ** |
| 33017 | 33275 | ** Inputs are: |
| 33018 | 33276 | ** |
| 33019 | 33277 | ** spec.bTitles (bT) Whether or not to show the titles |
| 33020 | 33278 | ** spec.eTitle (eT) The actual encoding to be used for titles |
| | @@ -33047,11 +33305,27 @@ |
| 33047 | 33305 | if( eT!=eH ) v >>= 32; /* Encoding disagree in upper 4-bytes */ |
| 33048 | 33306 | if( bT!=bH ) v >>= 16; /* ON/OFF disagree in upper 2-byte pairs */ |
| 33049 | 33307 | if( bT<2 ) v >>= 8; /* ON in even bytes, OFF in odd bytes (1st byte 0) */ |
| 33050 | 33308 | if( !bAll ) v >>= 4; /* bAll values are in the lower half-byte */ |
| 33051 | 33309 | |
| 33052 | | - return v & 3; /* Return the selected truth-table entry */ |
| 33310 | + v &= 3; |
| 33311 | + /* At this point v means: |
| 33312 | + ** 0: The titles do not matter. Never show anything. |
| 33313 | + ** 1: Show "--title off" |
| 33314 | + ** 2: Show "--title on" |
| 33315 | + ** 3: Show "--title VALUE" where VALUE is an encoding method |
| 33316 | + ** to use, one of: plain sql csv html tcl json |
| 33317 | + ** If bTitles is QRF_Always, convert 2->3, 3->5. If bTitles is not |
| 33318 | + ** QRF_Always, convert 3->4. |
| 33319 | + */ |
| 33320 | + if( p->mode.spec.bTitles==QRF_Always ){ |
| 33321 | + if( v==3 ) v = 5; |
| 33322 | + if( v==2 ) v = 3; |
| 33323 | + }else{ |
| 33324 | + if( v==3 ) v = 4; |
| 33325 | + } |
| 33326 | + return v; |
| 33053 | 33327 | } |
| 33054 | 33328 | |
| 33055 | 33329 | /* |
| 33056 | 33330 | ** DOT-COMMAND: .mode |
| 33057 | 33331 | ** |
| | @@ -33095,10 +33369,11 @@ |
| 33095 | 33369 | ** --quote ARG Enable/disable quoting of text. ARG can be |
| 33096 | 33370 | ** "off", "on", "sql", "relaxed", "csv", "html", |
| 33097 | 33371 | ** "tcl", or "json". "off" means show the text as-is. |
| 33098 | 33372 | ** "on" is an alias for "sql". |
| 33099 | 33373 | ** --reset Changes all mode settings back to their default. |
| 33374 | +** --rowcount BOOLEAN Show "(N rows)" at the end of query results. |
| 33100 | 33375 | ** --rowsep STRING Use STRING as the row separator |
| 33101 | 33376 | ** --sw|--screenwidth N Declare the screen width of the output device |
| 33102 | 33377 | ** to be N characters. An attempt may be made to |
| 33103 | 33378 | ** wrap output text to fit within this limit. Zero |
| 33104 | 33379 | ** means "no limit". Or N can be "auto" to set the |
| | @@ -33106,11 +33381,11 @@ |
| 33106 | 33381 | ** --tablename NAME Set the name of the table for "insert" mode. |
| 33107 | 33382 | ** --tag NAME Save mode to the left as NAME. |
| 33108 | 33383 | ** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON. |
| 33109 | 33384 | ** --title ARG Whether or not to show column headers, and if so |
| 33110 | 33385 | ** how to encode them. ARG can be "off", "on", |
| 33111 | | -** "sql", "csv", "html", "tcl", or "json". |
| 33386 | +** "always", "sql", "csv", "html", "tcl", or "json". |
| 33112 | 33387 | ** --titlelimit N Limit the length of column titles to N characters. |
| 33113 | 33388 | ** -v|--verbose Verbose output |
| 33114 | 33389 | ** --widths LIST Set the columns widths for columnar modes. The |
| 33115 | 33390 | ** argument is a list of integers, one for each |
| 33116 | 33391 | ** column. A "0" width means use a dynamic width |
| | @@ -33334,10 +33609,17 @@ |
| 33334 | 33609 | chng = 1; |
| 33335 | 33610 | }else if( optionMatch(z,"reset") ){ |
| 33336 | 33611 | int saved_eMode = p->mode.eMode; |
| 33337 | 33612 | modeFree(&p->mode); |
| 33338 | 33613 | modeChange(p, saved_eMode); |
| 33614 | + }else if( optionMatch(z,"rowcount") ){ |
| 33615 | + if( i+1>=nArg ){ |
| 33616 | + dotCmdError(p, i, "missing argument", 0); |
| 33617 | + return 1; |
| 33618 | + } |
| 33619 | + p->mode.spec.bRowCount = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; |
| 33620 | + chng = 1; |
| 33339 | 33621 | }else if( optionMatch(z,"screenwidth") || optionMatch(z,"sw") ){ |
| 33340 | 33622 | if( (++i)>=nArg ){ |
| 33341 | 33623 | dotCmdError(p, i-1, "missing argument", 0); |
| 33342 | 33624 | return 1; |
| 33343 | 33625 | } |
| | @@ -33387,25 +33669,63 @@ |
| 33387 | 33669 | return 1; |
| 33388 | 33670 | } |
| 33389 | 33671 | p->mode.spec.bTextJsonb = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; |
| 33390 | 33672 | chng = 1; |
| 33391 | 33673 | }else if( optionMatch(z,"titles") || optionMatch(z,"title") ){ |
| 33674 | + /* The --titles option controls two fields of the |
| 33675 | + ** sqlite3_qrf_spec object: eTitle and bTitles. |
| 33676 | + ** |
| 33677 | + ** --titles eTitle bTitles |
| 33678 | + ** -------- --------------- --------------------- |
| 33679 | + ** "off" (unchanged) QRF_Off |
| 33680 | + ** "on" (unchanged) QRF_On |
| 33681 | + ** "always" (unchanged) QRF_Always |
| 33682 | + ** "auto", (default) (default) |
| 33683 | + ** "plain" QRF_TEXT_Plain QRF_Always or QRF_On |
| 33684 | + ** "sql" QRF_TEXT_Sql QRF_Always or QRF_On |
| 33685 | + ** "csv" QRF_TEXT_Csv QRF_Always or QRF_On |
| 33686 | + ** "html" QRF_TEXT_Html QRF_Always or QRF_On |
| 33687 | + ** "tcl" QRF_TEXT_Tcl QRF_Always or QRF_On |
| 33688 | + ** "json" QRF_TEXT_Json QRF_Always or QRF_On |
| 33689 | + ** |
| 33690 | + ** This mapping is different from the -title option to the "format" |
| 33691 | + ** method of the TCL interface. In this mapping: |
| 33692 | + ** |
| 33693 | + ** * "off", "on", and "always" affect only bTitles and leave |
| 33694 | + ** eTitle unchanged |
| 33695 | + ** |
| 33696 | + ** * "auto" sets both bTitles and eTitle to the default value |
| 33697 | + ** in aModeInfo[] according to the current --style. |
| 33698 | + ** |
| 33699 | + ** * "plain" through "json" set eTitle but leave bTitles |
| 33700 | + ** unchanged if it is not QRF_Off. If bTitles is QRF_Off, |
| 33701 | + ** then it is changed QRF_Always. |
| 33702 | + */ |
| 33392 | 33703 | char *zErr = 0; |
| 33393 | 33704 | if( i+1>=nArg ){ |
| 33394 | 33705 | dotCmdError(p, i, "missing argument", 0); |
| 33395 | 33706 | return 1; |
| 33396 | 33707 | } |
| 33397 | 33708 | k = pickStr(azArg[++i],&zErr, |
| 33398 | | - "off","on","plain","sql","csv","html","tcl","json",""); |
| 33399 | | - /* 0 1 2 3 4 5 6 7 */ |
| 33709 | + "off","on","always","auto","plain","sql","csv","html","tcl","json",""); |
| 33710 | + /* 0 1 2 3 4 5 6 7 8 9 */ |
| 33400 | 33711 | if( k<0 ){ |
| 33401 | 33712 | dotCmdError(p, i, "bad --titles value","%z", zErr); |
| 33402 | 33713 | return 1; |
| 33403 | 33714 | } |
| 33404 | | - p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
| 33405 | 33715 | p->mode.mFlags &= ~MFLG_HDR; |
| 33406 | | - p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
| 33716 | + if( k<=2 ){ |
| 33717 | + p->mode.spec.bTitles = QRF_No+k; |
| 33718 | + }else if( k==3 ){ |
| 33719 | + p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 33720 | + p->mode.spec.bTitles = aModeInfo[p->mode.eMode].bHdr; |
| 33721 | + }else{ |
| 33722 | + p->mode.spec.eTitle = (k-4)+QRF_TEXT_Plain; |
| 33723 | + if( p->mode.spec.bTitles==QRF_No ){ |
| 33724 | + p->mode.spec.bTitles = QRF_Always; |
| 33725 | + } |
| 33726 | + } |
| 33407 | 33727 | chng = 1; |
| 33408 | 33728 | }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
| 33409 | 33729 | int nWidth = 0; |
| 33410 | 33730 | short int *aWidth; |
| 33411 | 33731 | const char *zW; |
| | @@ -33556,10 +33876,16 @@ |
| 33556 | 33876 | if( bAll |
| 33557 | 33877 | || (pI->eText!=p->mode.spec.eText && (pI->eText>1 || p->mode.spec.eText>1)) |
| 33558 | 33878 | ){ |
| 33559 | 33879 | sqlite3_str_appendf(pDesc," --quote %s",qrfQuoteNames[p->mode.spec.eText]); |
| 33560 | 33880 | } |
| 33881 | + if( bAll |
| 33882 | + || ((p->mode.spec.bRowCount==QRF_Yes) != ((pI->mFlg&4)!=0)) |
| 33883 | + ){ |
| 33884 | + sqlite3_str_appendf(pDesc," --rowcount %s", |
| 33885 | + p->mode.spec.bRowCount==QRF_Yes ? "on" : "off"); |
| 33886 | + } |
| 33561 | 33887 | zSetting = aModeStr[pI->eRSep]; |
| 33562 | 33888 | if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zRowSep)!=0) ){ |
| 33563 | 33889 | sqlite3_str_appendf(pDesc, " --rowsep "); |
| 33564 | 33890 | append_c_string(pDesc, p->mode.spec.zRowSep); |
| 33565 | 33891 | } |
| | @@ -33585,14 +33911,19 @@ |
| 33585 | 33911 | if( k==1 ){ |
| 33586 | 33912 | sqlite3_str_appendall(pDesc, " --titles off"); |
| 33587 | 33913 | }else if( k==2 ){ |
| 33588 | 33914 | sqlite3_str_appendall(pDesc, " --titles on"); |
| 33589 | 33915 | }else if( k==3 ){ |
| 33916 | + sqlite3_str_appendall(pDesc, " --titles always"); |
| 33917 | + }else if( k>=4 ){ |
| 33590 | 33918 | static const char *azTitle[] = |
| 33591 | 33919 | { "plain", "sql", "csv", "html", "tcl", "json"}; |
| 33592 | 33920 | sqlite3_str_appendf(pDesc, " --titles %s", |
| 33593 | 33921 | azTitle[p->mode.spec.eTitle-1]); |
| 33922 | + if( k==5 ){ |
| 33923 | + sqlite3_str_appendf(pDesc, " --titles always"); |
| 33924 | + } |
| 33594 | 33925 | } |
| 33595 | 33926 | if( p->mode.spec.nWidth>0 && (bAll || pI->eCx==2) ){ |
| 33596 | 33927 | int ii; |
| 33597 | 33928 | const char *zSep = " --widths "; |
| 33598 | 33929 | for(ii=0; ii<p->mode.spec.nWidth; ii++){ |
| | @@ -34910,15 +35241,19 @@ |
| 34910 | 35241 | } |
| 34911 | 35242 | }else |
| 34912 | 35243 | |
| 34913 | 35244 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 34914 | 35245 | if( nArg==2 ){ |
| 34915 | | - p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 35246 | + if( cli_strcmp(azArg[1],"always")==0 ){ |
| 35247 | + p->mode.spec.bTitles = QRF_Always; |
| 35248 | + }else{ |
| 35249 | + p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 35250 | + } |
| 34916 | 35251 | p->mode.mFlags |= MFLG_HDR; |
| 34917 | 35252 | p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 34918 | 35253 | }else{ |
| 34919 | | - eputz("Usage: .headers on|off\n"); |
| 35254 | + eputz("Usage: .headers off|on|always\n"); |
| 34920 | 35255 | rc = 1; |
| 34921 | 35256 | } |
| 34922 | 35257 | }else |
| 34923 | 35258 | |
| 34924 | 35259 | if( c=='h' && cli_strncmp(azArg[0], "help", n)==0 ){ |
| | @@ -35122,13 +35457,21 @@ |
| 35122 | 35457 | sqlite3_str_free(pSql); |
| 35123 | 35458 | }else |
| 35124 | 35459 | |
| 35125 | 35460 | if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){ |
| 35126 | 35461 | i64 iArg = 0; |
| 35462 | + open_db(p, 0); |
| 35127 | 35463 | if( nArg==2 ){ |
| 35128 | 35464 | iArg = integerValue(azArg[1]); |
| 35129 | | - if( iArg==0 ) iArg = -1; |
| 35465 | + if( iArg==0 ){ |
| 35466 | + if( cli_strcmp(azArg[1],"register")==0 ){ |
| 35467 | + sqlite3_intck_register(p->db, 1); |
| 35468 | + rc = 0; |
| 35469 | + goto meta_command_exit; |
| 35470 | + } |
| 35471 | + iArg = -1; |
| 35472 | + } |
| 35130 | 35473 | } |
| 35131 | 35474 | if( (nArg!=1 && nArg!=2) || iArg<0 ){ |
| 35132 | 35475 | cli_printf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); |
| 35133 | 35476 | rc = 1; |
| 35134 | 35477 | goto meta_command_exit; |
| | @@ -36502,11 +36845,12 @@ |
| 36502 | 36845 | azBool[(p->mode.mFlags & MFLG_ECHO)!=0]); |
| 36503 | 36846 | cli_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->mode.autoEQP&3]); |
| 36504 | 36847 | cli_printf(p->out, "%12.12s: %s\n","explain", |
| 36505 | 36848 | p->mode.autoExplain ? "auto" : "off"); |
| 36506 | 36849 | cli_printf(p->out, "%12.12s: %s\n","headers", |
| 36507 | | - azBool[p->mode.spec.bTitles==QRF_Yes]); |
| 36850 | + p->mode.spec.bTitles==QRF_Always ? "always" : |
| 36851 | + azBool[p->mode.spec.bTitles==QRF_Yes]); |
| 36508 | 36852 | if( p->mode.spec.eStyle==QRF_STYLE_Column |
| 36509 | 36853 | || p->mode.spec.eStyle==QRF_STYLE_Box |
| 36510 | 36854 | || p->mode.spec.eStyle==QRF_STYLE_Table |
| 36511 | 36855 | || p->mode.spec.eStyle==QRF_STYLE_Markdown |
| 36512 | 36856 | ){ |
| | @@ -38576,11 +38920,11 @@ |
| 38576 | 38920 | cmdline_option_value(argc,argv,++i)); |
| 38577 | 38921 | }else if( cli_strcmp(z,"-nullvalue")==0 ){ |
| 38578 | 38922 | modeSetStr(&data.mode.spec.zNull, |
| 38579 | 38923 | cmdline_option_value(argc,argv,++i)); |
| 38580 | 38924 | }else if( cli_strcmp(z,"-header")==0 ){ |
| 38581 | | - data.mode.spec.bTitles = QRF_Yes; |
| 38925 | + data.mode.spec.bTitles = QRF_Always; |
| 38582 | 38926 | data.mode.mFlags |= MFLG_HDR; |
| 38583 | 38927 | }else if( cli_strcmp(z,"-noheader")==0 ){ |
| 38584 | 38928 | data.mode.spec.bTitles = QRF_No; |
| 38585 | 38929 | data.mode.mFlags |= MFLG_HDR; |
| 38586 | 38930 | }else if( cli_strcmp(z,"-echo")==0 ){ |
| 38587 | 38931 | |