Fossil SCM
Add the .fullschema command to "fossil sqlite". This command shows the schema and the content of the sqlite_stat tables, all in one go. Useful when reporting problems with the query planner
Commit
4f62b6017b7e38b3d24ef7115a2c20195345d8e0
Parent
1b648ebacf84484…
2 files changed
+136
-29
+136
-29
+136
-29
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -62,10 +62,11 @@ | ||
| 62 | 62 | # define stifle_history(X) |
| 63 | 63 | #endif |
| 64 | 64 | |
| 65 | 65 | #if defined(_WIN32) || defined(WIN32) |
| 66 | 66 | # include <io.h> |
| 67 | +# include <fcntl.h> | |
| 67 | 68 | #define isatty(h) _isatty(h) |
| 68 | 69 | #ifndef access |
| 69 | 70 | # define access(f,m) _access((f),(m)) |
| 70 | 71 | #endif |
| 71 | 72 | #undef popen |
| @@ -456,10 +457,11 @@ | ||
| 456 | 457 | int mode; /* An output mode setting */ |
| 457 | 458 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 458 | 459 | int showHeader; /* True to show column names in List or Column mode */ |
| 459 | 460 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 460 | 461 | char separator[20]; /* Separator character for MODE_List */ |
| 462 | + char newline[20]; /* Record separator in MODE_Csv */ | |
| 461 | 463 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 462 | 464 | int actualWidth[100]; /* Actual width of each column */ |
| 463 | 465 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 464 | 466 | ** the database */ |
| 465 | 467 | struct previous_mode_data explainPrev; |
| @@ -657,11 +659,12 @@ | ||
| 657 | 659 | }; |
| 658 | 660 | |
| 659 | 661 | /* |
| 660 | 662 | ** Output a single term of CSV. Actually, p->separator is used for |
| 661 | 663 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 662 | -** the null value. Strings are quoted if necessary. | |
| 664 | +** the null value. Strings are quoted if necessary. The separator | |
| 665 | +** is only issued if bSep is true. | |
| 663 | 666 | */ |
| 664 | 667 | static void output_csv(struct callback_data *p, const char *z, int bSep){ |
| 665 | 668 | FILE *out = p->out; |
| 666 | 669 | if( z==0 ){ |
| 667 | 670 | fprintf(out,"%s",p->nullvalue); |
| @@ -853,21 +856,30 @@ | ||
| 853 | 856 | } |
| 854 | 857 | fprintf(p->out,"\n"); |
| 855 | 858 | break; |
| 856 | 859 | } |
| 857 | 860 | case MODE_Csv: { |
| 861 | +#if defined(WIN32) || defined(_WIN32) | |
| 862 | + fflush(p->out); | |
| 863 | + _setmode(_fileno(p->out), _O_BINARY); | |
| 864 | +#endif | |
| 858 | 865 | if( p->cnt++==0 && p->showHeader ){ |
| 859 | 866 | for(i=0; i<nArg; i++){ |
| 860 | 867 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 861 | 868 | } |
| 862 | - fprintf(p->out,"\n"); | |
| 869 | + fprintf(p->out,"%s",p->newline); | |
| 863 | 870 | } |
| 864 | - if( azArg==0 ) break; | |
| 865 | - for(i=0; i<nArg; i++){ | |
| 866 | - output_csv(p, azArg[i], i<nArg-1); | |
| 871 | + if( azArg>0 ){ | |
| 872 | + for(i=0; i<nArg; i++){ | |
| 873 | + output_csv(p, azArg[i], i<nArg-1); | |
| 874 | + } | |
| 875 | + fprintf(p->out,"%s",p->newline); | |
| 867 | 876 | } |
| 868 | - fprintf(p->out,"\n"); | |
| 877 | +#if defined(WIN32) || defined(_WIN32) | |
| 878 | + fflush(p->out); | |
| 879 | + _setmode(_fileno(p->out), _O_TEXT); | |
| 880 | +#endif | |
| 869 | 881 | break; |
| 870 | 882 | } |
| 871 | 883 | case MODE_Insert: { |
| 872 | 884 | p->cnt++; |
| 873 | 885 | if( azArg==0 ) break; |
| @@ -1150,12 +1162,14 @@ | ||
| 1150 | 1162 | fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
| 1151 | 1163 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 1152 | 1164 | fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
| 1153 | 1165 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
| 1154 | 1166 | fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
| 1155 | - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); | |
| 1156 | - fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); | |
| 1167 | + if( sqlite3_libversion_number()>=3008000 ){ | |
| 1168 | + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); | |
| 1169 | + fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); | |
| 1170 | + } | |
| 1157 | 1171 | } |
| 1158 | 1172 | |
| 1159 | 1173 | return 0; |
| 1160 | 1174 | } |
| 1161 | 1175 | |
| @@ -1617,11 +1631,12 @@ | ||
| 1617 | 1631 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| 1618 | 1632 | ".save FILE Write in-memory database into FILE\n" |
| 1619 | 1633 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1620 | 1634 | " If TABLE specified, only show tables matching\n" |
| 1621 | 1635 | " LIKE pattern TABLE.\n" |
| 1622 | - ".separator STRING Change separator used by output mode and .import\n" | |
| 1636 | + ".separator STRING ?NL? Change separator used by output mode and .import\n" | |
| 1637 | + " NL is the end-of-line mark for CSV\n" | |
| 1623 | 1638 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1624 | 1639 | ".show Show the current values for various settings\n" |
| 1625 | 1640 | ".stats on|off Turn stats on or off\n" |
| 1626 | 1641 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1627 | 1642 | ".tables ?TABLE? List names of tables\n" |
| @@ -1635,10 +1650,73 @@ | ||
| 1635 | 1650 | " Negative values right-justify\n" |
| 1636 | 1651 | ; |
| 1637 | 1652 | |
| 1638 | 1653 | /* Forward reference */ |
| 1639 | 1654 | static int process_input(struct callback_data *p, FILE *in); |
| 1655 | +/* | |
| 1656 | +** Implementation of the "readfile(X)" SQL function. The entire content | |
| 1657 | +** of the file named X is read and returned as a BLOB. NULL is returned | |
| 1658 | +** if the file does not exist or is unreadable. | |
| 1659 | +*/ | |
| 1660 | +static void readfileFunc( | |
| 1661 | + sqlite3_context *context, | |
| 1662 | + int argc, | |
| 1663 | + sqlite3_value **argv | |
| 1664 | +){ | |
| 1665 | + const char *zName; | |
| 1666 | + FILE *in; | |
| 1667 | + long nIn; | |
| 1668 | + void *pBuf; | |
| 1669 | + | |
| 1670 | + zName = (const char*)sqlite3_value_text(argv[0]); | |
| 1671 | + if( zName==0 ) return; | |
| 1672 | + in = fopen(zName, "rb"); | |
| 1673 | + if( in==0 ) return; | |
| 1674 | + fseek(in, 0, SEEK_END); | |
| 1675 | + nIn = ftell(in); | |
| 1676 | + rewind(in); | |
| 1677 | + pBuf = sqlite3_malloc( nIn ); | |
| 1678 | + if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ | |
| 1679 | + sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); | |
| 1680 | + }else{ | |
| 1681 | + sqlite3_free(pBuf); | |
| 1682 | + } | |
| 1683 | + fclose(in); | |
| 1684 | +} | |
| 1685 | + | |
| 1686 | +/* | |
| 1687 | +** Implementation of the "writefile(X,Y)" SQL function. The argument Y | |
| 1688 | +** is written into file X. The number of bytes written is returned. Or | |
| 1689 | +** NULL is returned if something goes wrong, such as being unable to open | |
| 1690 | +** file X for writing. | |
| 1691 | +*/ | |
| 1692 | +static void writefileFunc( | |
| 1693 | + sqlite3_context *context, | |
| 1694 | + int argc, | |
| 1695 | + sqlite3_value **argv | |
| 1696 | +){ | |
| 1697 | + FILE *out; | |
| 1698 | + const char *z; | |
| 1699 | + int n; | |
| 1700 | + sqlite3_int64 rc; | |
| 1701 | + const char *zFile; | |
| 1702 | + | |
| 1703 | + zFile = (const char*)sqlite3_value_text(argv[0]); | |
| 1704 | + if( zFile==0 ) return; | |
| 1705 | + out = fopen(zFile, "wb"); | |
| 1706 | + if( out==0 ) return; | |
| 1707 | + z = (const char*)sqlite3_value_blob(argv[1]); | |
| 1708 | + if( z==0 ){ | |
| 1709 | + n = 0; | |
| 1710 | + rc = 0; | |
| 1711 | + }else{ | |
| 1712 | + n = sqlite3_value_bytes(argv[1]); | |
| 1713 | + rc = fwrite(z, 1, n, out); | |
| 1714 | + } | |
| 1715 | + fclose(out); | |
| 1716 | + sqlite3_result_int64(context, rc); | |
| 1717 | +} | |
| 1640 | 1718 | |
| 1641 | 1719 | /* |
| 1642 | 1720 | ** Make sure the database is open. If it is not, then open it. If |
| 1643 | 1721 | ** the database fails to open, print an error message and exit. |
| 1644 | 1722 | */ |
| @@ -1658,10 +1736,14 @@ | ||
| 1658 | 1736 | exit(1); |
| 1659 | 1737 | } |
| 1660 | 1738 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1661 | 1739 | sqlite3_enable_load_extension(p->db, 1); |
| 1662 | 1740 | #endif |
| 1741 | + sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, | |
| 1742 | + readfileFunc, 0, 0); | |
| 1743 | + sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, | |
| 1744 | + writefileFunc, 0, 0); | |
| 1663 | 1745 | } |
| 1664 | 1746 | } |
| 1665 | 1747 | |
| 1666 | 1748 | /* |
| 1667 | 1749 | ** Do C-language style dequoting. |
| @@ -2414,10 +2496,11 @@ | ||
| 2414 | 2496 | }else |
| 2415 | 2497 | |
| 2416 | 2498 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 2417 | 2499 | struct callback_data data; |
| 2418 | 2500 | char *zErrMsg = 0; |
| 2501 | + int doStats = 0; | |
| 2419 | 2502 | if( nArg!=1 ){ |
| 2420 | 2503 | fprintf(stderr, "Usage: .fullschema\n"); |
| 2421 | 2504 | rc = 1; |
| 2422 | 2505 | goto meta_command_exit; |
| 2423 | 2506 | } |
| @@ -2432,25 +2515,37 @@ | ||
| 2432 | 2515 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " |
| 2433 | 2516 | "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" |
| 2434 | 2517 | "ORDER BY rowid", |
| 2435 | 2518 | callback, &data, &zErrMsg |
| 2436 | 2519 | ); |
| 2437 | - sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", | |
| 2438 | - callback, &data, &zErrMsg); | |
| 2439 | - data.mode = MODE_Insert; | |
| 2440 | - data.zDestTable = "sqlite_stat1"; | |
| 2441 | - shell_exec(p->db, "SELECT * FROM sqlite_stat1", | |
| 2442 | - shell_callback, &data,&zErrMsg); | |
| 2443 | - data.zDestTable = "sqlite_stat3"; | |
| 2444 | - shell_exec(p->db, "SELECT * FROM sqlite_stat3", | |
| 2445 | - shell_callback, &data,&zErrMsg); | |
| 2446 | - data.zDestTable = "sqlite_stat4"; | |
| 2447 | - shell_exec(p->db, "SELECT * FROM sqlite_stat4", | |
| 2448 | - shell_callback, &data, &zErrMsg); | |
| 2449 | - data.mode = MODE_Semi; | |
| 2450 | - shell_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", | |
| 2451 | - shell_callback, &data, &zErrMsg); | |
| 2520 | + if( rc==SQLITE_OK ){ | |
| 2521 | + sqlite3_stmt *pStmt; | |
| 2522 | + rc = sqlite3_prepare_v2(p->db, | |
| 2523 | + "SELECT rowid FROM sqlite_master" | |
| 2524 | + " WHERE name GLOB 'sqlite_stat[134]'", | |
| 2525 | + -1, &pStmt, 0); | |
| 2526 | + doStats = sqlite3_step(pStmt)==SQLITE_ROW; | |
| 2527 | + sqlite3_finalize(pStmt); | |
| 2528 | + } | |
| 2529 | + if( doStats==0 ){ | |
| 2530 | + fprintf(p->out, "/* No STAT tables available */\n"); | |
| 2531 | + }else{ | |
| 2532 | + fprintf(p->out, "ANALYZE sqlite_master;\n"); | |
| 2533 | + sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", | |
| 2534 | + callback, &data, &zErrMsg); | |
| 2535 | + data.mode = MODE_Insert; | |
| 2536 | + data.zDestTable = "sqlite_stat1"; | |
| 2537 | + shell_exec(p->db, "SELECT * FROM sqlite_stat1", | |
| 2538 | + shell_callback, &data,&zErrMsg); | |
| 2539 | + data.zDestTable = "sqlite_stat3"; | |
| 2540 | + shell_exec(p->db, "SELECT * FROM sqlite_stat3", | |
| 2541 | + shell_callback, &data,&zErrMsg); | |
| 2542 | + data.zDestTable = "sqlite_stat4"; | |
| 2543 | + shell_exec(p->db, "SELECT * FROM sqlite_stat4", | |
| 2544 | + shell_callback, &data, &zErrMsg); | |
| 2545 | + fprintf(p->out, "ANALYZE sqlite_master;\n"); | |
| 2546 | + } | |
| 2452 | 2547 | }else |
| 2453 | 2548 | |
| 2454 | 2549 | if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ |
| 2455 | 2550 | if( nArg==2 ){ |
| 2456 | 2551 | p->showHeader = booleanValue(azArg[1]); |
| @@ -2736,10 +2831,11 @@ | ||
| 2736 | 2831 | p->mode = MODE_Tcl; |
| 2737 | 2832 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2738 | 2833 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2739 | 2834 | p->mode = MODE_Csv; |
| 2740 | 2835 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2836 | + sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); | |
| 2741 | 2837 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2742 | 2838 | p->mode = MODE_List; |
| 2743 | 2839 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2744 | 2840 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2745 | 2841 | p->mode = MODE_Insert; |
| @@ -3014,17 +3110,20 @@ | ||
| 3014 | 3110 | } |
| 3015 | 3111 | }else |
| 3016 | 3112 | #endif |
| 3017 | 3113 | |
| 3018 | 3114 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3019 | - if( nArg==2 ){ | |
| 3020 | - sqlite3_snprintf(sizeof(p->separator), p->separator, | |
| 3021 | - "%.*s", (int)sizeof(p->separator)-1, azArg[1]); | |
| 3022 | - }else{ | |
| 3023 | - fprintf(stderr, "Usage: .separator STRING\n"); | |
| 3115 | + if( nArg<2 || nArg>3 ){ | |
| 3116 | + fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); | |
| 3024 | 3117 | rc = 1; |
| 3025 | 3118 | } |
| 3119 | + if( nArg>=2 ){ | |
| 3120 | + sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); | |
| 3121 | + } | |
| 3122 | + if( nArg>=3 ){ | |
| 3123 | + sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); | |
| 3124 | + } | |
| 3026 | 3125 | }else |
| 3027 | 3126 | |
| 3028 | 3127 | if( c=='s' |
| 3029 | 3128 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| 3030 | 3129 | ){ |
| @@ -3061,10 +3160,12 @@ | ||
| 3061 | 3160 | fprintf(p->out, "\n"); |
| 3062 | 3161 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3063 | 3162 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3064 | 3163 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3065 | 3164 | output_c_string(p->out, p->separator); |
| 3165 | + fprintf(p->out," "); | |
| 3166 | + output_c_string(p->out, p->newline); | |
| 3066 | 3167 | fprintf(p->out, "\n"); |
| 3067 | 3168 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3068 | 3169 | fprintf(p->out,"%9.9s: ","width"); |
| 3069 | 3170 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3070 | 3171 | fprintf(p->out,"%d ",p->colWidth[i]); |
| @@ -3677,10 +3778,11 @@ | ||
| 3677 | 3778 | " -list set output mode to 'list'\n" |
| 3678 | 3779 | " -mmap N default mmap size set to N\n" |
| 3679 | 3780 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3680 | 3781 | " -multiplex enable the multiplexor VFS\n" |
| 3681 | 3782 | #endif |
| 3783 | + " -newline SEP set newline character(s) for CSV\n" | |
| 3682 | 3784 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3683 | 3785 | " -separator SEP set output field separator. Default: '|'\n" |
| 3684 | 3786 | " -stats print memory stats before each finalize\n" |
| 3685 | 3787 | " -version show SQLite version\n" |
| 3686 | 3788 | " -vfs NAME use NAME as the default VFS\n" |
| @@ -3706,10 +3808,11 @@ | ||
| 3706 | 3808 | */ |
| 3707 | 3809 | static void main_init(struct callback_data *data) { |
| 3708 | 3810 | memset(data, 0, sizeof(*data)); |
| 3709 | 3811 | data->mode = MODE_List; |
| 3710 | 3812 | memcpy(data->separator,"|", 2); |
| 3813 | + memcpy(data->newline,"\r\n", 3); | |
| 3711 | 3814 | data->showHeader = 0; |
| 3712 | 3815 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3713 | 3816 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3714 | 3817 | sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); |
| 3715 | 3818 | sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); |
| @@ -3798,10 +3901,11 @@ | ||
| 3798 | 3901 | return 1; |
| 3799 | 3902 | } |
| 3800 | 3903 | if( z[1]=='-' ) z++; |
| 3801 | 3904 | if( strcmp(z,"-separator")==0 |
| 3802 | 3905 | || strcmp(z,"-nullvalue")==0 |
| 3906 | + || strcmp(z,"-newline")==0 | |
| 3803 | 3907 | || strcmp(z,"-cmd")==0 |
| 3804 | 3908 | ){ |
| 3805 | 3909 | (void)cmdline_option_value(argc, argv, ++i); |
| 3806 | 3910 | }else if( strcmp(z,"-init")==0 ){ |
| 3807 | 3911 | zInitFile = cmdline_option_value(argc, argv, ++i); |
| @@ -3907,10 +4011,13 @@ | ||
| 3907 | 4011 | data.mode = MODE_Csv; |
| 3908 | 4012 | memcpy(data.separator,",",2); |
| 3909 | 4013 | }else if( strcmp(z,"-separator")==0 ){ |
| 3910 | 4014 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 3911 | 4015 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4016 | + }else if( strcmp(z,"-newline")==0 ){ | |
| 4017 | + sqlite3_snprintf(sizeof(data.newline), data.newline, | |
| 4018 | + "%s",cmdline_option_value(argc,argv,++i)); | |
| 3912 | 4019 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 3913 | 4020 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 3914 | 4021 | "%s",cmdline_option_value(argc,argv,++i)); |
| 3915 | 4022 | }else if( strcmp(z,"-header")==0 ){ |
| 3916 | 4023 | data.showHeader = 1; |
| 3917 | 4024 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -62,10 +62,11 @@ | |
| 62 | # define stifle_history(X) |
| 63 | #endif |
| 64 | |
| 65 | #if defined(_WIN32) || defined(WIN32) |
| 66 | # include <io.h> |
| 67 | #define isatty(h) _isatty(h) |
| 68 | #ifndef access |
| 69 | # define access(f,m) _access((f),(m)) |
| 70 | #endif |
| 71 | #undef popen |
| @@ -456,10 +457,11 @@ | |
| 456 | int mode; /* An output mode setting */ |
| 457 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 458 | int showHeader; /* True to show column names in List or Column mode */ |
| 459 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 460 | char separator[20]; /* Separator character for MODE_List */ |
| 461 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 462 | int actualWidth[100]; /* Actual width of each column */ |
| 463 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 464 | ** the database */ |
| 465 | struct previous_mode_data explainPrev; |
| @@ -657,11 +659,12 @@ | |
| 657 | }; |
| 658 | |
| 659 | /* |
| 660 | ** Output a single term of CSV. Actually, p->separator is used for |
| 661 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 662 | ** the null value. Strings are quoted if necessary. |
| 663 | */ |
| 664 | static void output_csv(struct callback_data *p, const char *z, int bSep){ |
| 665 | FILE *out = p->out; |
| 666 | if( z==0 ){ |
| 667 | fprintf(out,"%s",p->nullvalue); |
| @@ -853,21 +856,30 @@ | |
| 853 | } |
| 854 | fprintf(p->out,"\n"); |
| 855 | break; |
| 856 | } |
| 857 | case MODE_Csv: { |
| 858 | if( p->cnt++==0 && p->showHeader ){ |
| 859 | for(i=0; i<nArg; i++){ |
| 860 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 861 | } |
| 862 | fprintf(p->out,"\n"); |
| 863 | } |
| 864 | if( azArg==0 ) break; |
| 865 | for(i=0; i<nArg; i++){ |
| 866 | output_csv(p, azArg[i], i<nArg-1); |
| 867 | } |
| 868 | fprintf(p->out,"\n"); |
| 869 | break; |
| 870 | } |
| 871 | case MODE_Insert: { |
| 872 | p->cnt++; |
| 873 | if( azArg==0 ) break; |
| @@ -1150,12 +1162,14 @@ | |
| 1150 | fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
| 1151 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 1152 | fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
| 1153 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
| 1154 | fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
| 1155 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 1156 | fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
| 1157 | } |
| 1158 | |
| 1159 | return 0; |
| 1160 | } |
| 1161 | |
| @@ -1617,11 +1631,12 @@ | |
| 1617 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| 1618 | ".save FILE Write in-memory database into FILE\n" |
| 1619 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1620 | " If TABLE specified, only show tables matching\n" |
| 1621 | " LIKE pattern TABLE.\n" |
| 1622 | ".separator STRING Change separator used by output mode and .import\n" |
| 1623 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1624 | ".show Show the current values for various settings\n" |
| 1625 | ".stats on|off Turn stats on or off\n" |
| 1626 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1627 | ".tables ?TABLE? List names of tables\n" |
| @@ -1635,10 +1650,73 @@ | |
| 1635 | " Negative values right-justify\n" |
| 1636 | ; |
| 1637 | |
| 1638 | /* Forward reference */ |
| 1639 | static int process_input(struct callback_data *p, FILE *in); |
| 1640 | |
| 1641 | /* |
| 1642 | ** Make sure the database is open. If it is not, then open it. If |
| 1643 | ** the database fails to open, print an error message and exit. |
| 1644 | */ |
| @@ -1658,10 +1736,14 @@ | |
| 1658 | exit(1); |
| 1659 | } |
| 1660 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1661 | sqlite3_enable_load_extension(p->db, 1); |
| 1662 | #endif |
| 1663 | } |
| 1664 | } |
| 1665 | |
| 1666 | /* |
| 1667 | ** Do C-language style dequoting. |
| @@ -2414,10 +2496,11 @@ | |
| 2414 | }else |
| 2415 | |
| 2416 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 2417 | struct callback_data data; |
| 2418 | char *zErrMsg = 0; |
| 2419 | if( nArg!=1 ){ |
| 2420 | fprintf(stderr, "Usage: .fullschema\n"); |
| 2421 | rc = 1; |
| 2422 | goto meta_command_exit; |
| 2423 | } |
| @@ -2432,25 +2515,37 @@ | |
| 2432 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " |
| 2433 | "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" |
| 2434 | "ORDER BY rowid", |
| 2435 | callback, &data, &zErrMsg |
| 2436 | ); |
| 2437 | sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", |
| 2438 | callback, &data, &zErrMsg); |
| 2439 | data.mode = MODE_Insert; |
| 2440 | data.zDestTable = "sqlite_stat1"; |
| 2441 | shell_exec(p->db, "SELECT * FROM sqlite_stat1", |
| 2442 | shell_callback, &data,&zErrMsg); |
| 2443 | data.zDestTable = "sqlite_stat3"; |
| 2444 | shell_exec(p->db, "SELECT * FROM sqlite_stat3", |
| 2445 | shell_callback, &data,&zErrMsg); |
| 2446 | data.zDestTable = "sqlite_stat4"; |
| 2447 | shell_exec(p->db, "SELECT * FROM sqlite_stat4", |
| 2448 | shell_callback, &data, &zErrMsg); |
| 2449 | data.mode = MODE_Semi; |
| 2450 | shell_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", |
| 2451 | shell_callback, &data, &zErrMsg); |
| 2452 | }else |
| 2453 | |
| 2454 | if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ |
| 2455 | if( nArg==2 ){ |
| 2456 | p->showHeader = booleanValue(azArg[1]); |
| @@ -2736,10 +2831,11 @@ | |
| 2736 | p->mode = MODE_Tcl; |
| 2737 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2738 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2739 | p->mode = MODE_Csv; |
| 2740 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2741 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2742 | p->mode = MODE_List; |
| 2743 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2744 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2745 | p->mode = MODE_Insert; |
| @@ -3014,17 +3110,20 @@ | |
| 3014 | } |
| 3015 | }else |
| 3016 | #endif |
| 3017 | |
| 3018 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3019 | if( nArg==2 ){ |
| 3020 | sqlite3_snprintf(sizeof(p->separator), p->separator, |
| 3021 | "%.*s", (int)sizeof(p->separator)-1, azArg[1]); |
| 3022 | }else{ |
| 3023 | fprintf(stderr, "Usage: .separator STRING\n"); |
| 3024 | rc = 1; |
| 3025 | } |
| 3026 | }else |
| 3027 | |
| 3028 | if( c=='s' |
| 3029 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| 3030 | ){ |
| @@ -3061,10 +3160,12 @@ | |
| 3061 | fprintf(p->out, "\n"); |
| 3062 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3063 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3064 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3065 | output_c_string(p->out, p->separator); |
| 3066 | fprintf(p->out, "\n"); |
| 3067 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3068 | fprintf(p->out,"%9.9s: ","width"); |
| 3069 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3070 | fprintf(p->out,"%d ",p->colWidth[i]); |
| @@ -3677,10 +3778,11 @@ | |
| 3677 | " -list set output mode to 'list'\n" |
| 3678 | " -mmap N default mmap size set to N\n" |
| 3679 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3680 | " -multiplex enable the multiplexor VFS\n" |
| 3681 | #endif |
| 3682 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3683 | " -separator SEP set output field separator. Default: '|'\n" |
| 3684 | " -stats print memory stats before each finalize\n" |
| 3685 | " -version show SQLite version\n" |
| 3686 | " -vfs NAME use NAME as the default VFS\n" |
| @@ -3706,10 +3808,11 @@ | |
| 3706 | */ |
| 3707 | static void main_init(struct callback_data *data) { |
| 3708 | memset(data, 0, sizeof(*data)); |
| 3709 | data->mode = MODE_List; |
| 3710 | memcpy(data->separator,"|", 2); |
| 3711 | data->showHeader = 0; |
| 3712 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3713 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3714 | sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); |
| 3715 | sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); |
| @@ -3798,10 +3901,11 @@ | |
| 3798 | return 1; |
| 3799 | } |
| 3800 | if( z[1]=='-' ) z++; |
| 3801 | if( strcmp(z,"-separator")==0 |
| 3802 | || strcmp(z,"-nullvalue")==0 |
| 3803 | || strcmp(z,"-cmd")==0 |
| 3804 | ){ |
| 3805 | (void)cmdline_option_value(argc, argv, ++i); |
| 3806 | }else if( strcmp(z,"-init")==0 ){ |
| 3807 | zInitFile = cmdline_option_value(argc, argv, ++i); |
| @@ -3907,10 +4011,13 @@ | |
| 3907 | data.mode = MODE_Csv; |
| 3908 | memcpy(data.separator,",",2); |
| 3909 | }else if( strcmp(z,"-separator")==0 ){ |
| 3910 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 3911 | "%s",cmdline_option_value(argc,argv,++i)); |
| 3912 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 3913 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 3914 | "%s",cmdline_option_value(argc,argv,++i)); |
| 3915 | }else if( strcmp(z,"-header")==0 ){ |
| 3916 | data.showHeader = 1; |
| 3917 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -62,10 +62,11 @@ | |
| 62 | # define stifle_history(X) |
| 63 | #endif |
| 64 | |
| 65 | #if defined(_WIN32) || defined(WIN32) |
| 66 | # include <io.h> |
| 67 | # include <fcntl.h> |
| 68 | #define isatty(h) _isatty(h) |
| 69 | #ifndef access |
| 70 | # define access(f,m) _access((f),(m)) |
| 71 | #endif |
| 72 | #undef popen |
| @@ -456,10 +457,11 @@ | |
| 457 | int mode; /* An output mode setting */ |
| 458 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 459 | int showHeader; /* True to show column names in List or Column mode */ |
| 460 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 461 | char separator[20]; /* Separator character for MODE_List */ |
| 462 | char newline[20]; /* Record separator in MODE_Csv */ |
| 463 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 464 | int actualWidth[100]; /* Actual width of each column */ |
| 465 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 466 | ** the database */ |
| 467 | struct previous_mode_data explainPrev; |
| @@ -657,11 +659,12 @@ | |
| 659 | }; |
| 660 | |
| 661 | /* |
| 662 | ** Output a single term of CSV. Actually, p->separator is used for |
| 663 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 664 | ** the null value. Strings are quoted if necessary. The separator |
| 665 | ** is only issued if bSep is true. |
| 666 | */ |
| 667 | static void output_csv(struct callback_data *p, const char *z, int bSep){ |
| 668 | FILE *out = p->out; |
| 669 | if( z==0 ){ |
| 670 | fprintf(out,"%s",p->nullvalue); |
| @@ -853,21 +856,30 @@ | |
| 856 | } |
| 857 | fprintf(p->out,"\n"); |
| 858 | break; |
| 859 | } |
| 860 | case MODE_Csv: { |
| 861 | #if defined(WIN32) || defined(_WIN32) |
| 862 | fflush(p->out); |
| 863 | _setmode(_fileno(p->out), _O_BINARY); |
| 864 | #endif |
| 865 | if( p->cnt++==0 && p->showHeader ){ |
| 866 | for(i=0; i<nArg; i++){ |
| 867 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 868 | } |
| 869 | fprintf(p->out,"%s",p->newline); |
| 870 | } |
| 871 | if( azArg>0 ){ |
| 872 | for(i=0; i<nArg; i++){ |
| 873 | output_csv(p, azArg[i], i<nArg-1); |
| 874 | } |
| 875 | fprintf(p->out,"%s",p->newline); |
| 876 | } |
| 877 | #if defined(WIN32) || defined(_WIN32) |
| 878 | fflush(p->out); |
| 879 | _setmode(_fileno(p->out), _O_TEXT); |
| 880 | #endif |
| 881 | break; |
| 882 | } |
| 883 | case MODE_Insert: { |
| 884 | p->cnt++; |
| 885 | if( azArg==0 ) break; |
| @@ -1150,12 +1162,14 @@ | |
| 1162 | fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
| 1163 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 1164 | fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
| 1165 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
| 1166 | fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
| 1167 | if( sqlite3_libversion_number()>=3008000 ){ |
| 1168 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 1169 | fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
| 1170 | } |
| 1171 | } |
| 1172 | |
| 1173 | return 0; |
| 1174 | } |
| 1175 | |
| @@ -1617,11 +1631,12 @@ | |
| 1631 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| 1632 | ".save FILE Write in-memory database into FILE\n" |
| 1633 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1634 | " If TABLE specified, only show tables matching\n" |
| 1635 | " LIKE pattern TABLE.\n" |
| 1636 | ".separator STRING ?NL? Change separator used by output mode and .import\n" |
| 1637 | " NL is the end-of-line mark for CSV\n" |
| 1638 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1639 | ".show Show the current values for various settings\n" |
| 1640 | ".stats on|off Turn stats on or off\n" |
| 1641 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1642 | ".tables ?TABLE? List names of tables\n" |
| @@ -1635,10 +1650,73 @@ | |
| 1650 | " Negative values right-justify\n" |
| 1651 | ; |
| 1652 | |
| 1653 | /* Forward reference */ |
| 1654 | static int process_input(struct callback_data *p, FILE *in); |
| 1655 | /* |
| 1656 | ** Implementation of the "readfile(X)" SQL function. The entire content |
| 1657 | ** of the file named X is read and returned as a BLOB. NULL is returned |
| 1658 | ** if the file does not exist or is unreadable. |
| 1659 | */ |
| 1660 | static void readfileFunc( |
| 1661 | sqlite3_context *context, |
| 1662 | int argc, |
| 1663 | sqlite3_value **argv |
| 1664 | ){ |
| 1665 | const char *zName; |
| 1666 | FILE *in; |
| 1667 | long nIn; |
| 1668 | void *pBuf; |
| 1669 | |
| 1670 | zName = (const char*)sqlite3_value_text(argv[0]); |
| 1671 | if( zName==0 ) return; |
| 1672 | in = fopen(zName, "rb"); |
| 1673 | if( in==0 ) return; |
| 1674 | fseek(in, 0, SEEK_END); |
| 1675 | nIn = ftell(in); |
| 1676 | rewind(in); |
| 1677 | pBuf = sqlite3_malloc( nIn ); |
| 1678 | if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ |
| 1679 | sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); |
| 1680 | }else{ |
| 1681 | sqlite3_free(pBuf); |
| 1682 | } |
| 1683 | fclose(in); |
| 1684 | } |
| 1685 | |
| 1686 | /* |
| 1687 | ** Implementation of the "writefile(X,Y)" SQL function. The argument Y |
| 1688 | ** is written into file X. The number of bytes written is returned. Or |
| 1689 | ** NULL is returned if something goes wrong, such as being unable to open |
| 1690 | ** file X for writing. |
| 1691 | */ |
| 1692 | static void writefileFunc( |
| 1693 | sqlite3_context *context, |
| 1694 | int argc, |
| 1695 | sqlite3_value **argv |
| 1696 | ){ |
| 1697 | FILE *out; |
| 1698 | const char *z; |
| 1699 | int n; |
| 1700 | sqlite3_int64 rc; |
| 1701 | const char *zFile; |
| 1702 | |
| 1703 | zFile = (const char*)sqlite3_value_text(argv[0]); |
| 1704 | if( zFile==0 ) return; |
| 1705 | out = fopen(zFile, "wb"); |
| 1706 | if( out==0 ) return; |
| 1707 | z = (const char*)sqlite3_value_blob(argv[1]); |
| 1708 | if( z==0 ){ |
| 1709 | n = 0; |
| 1710 | rc = 0; |
| 1711 | }else{ |
| 1712 | n = sqlite3_value_bytes(argv[1]); |
| 1713 | rc = fwrite(z, 1, n, out); |
| 1714 | } |
| 1715 | fclose(out); |
| 1716 | sqlite3_result_int64(context, rc); |
| 1717 | } |
| 1718 | |
| 1719 | /* |
| 1720 | ** Make sure the database is open. If it is not, then open it. If |
| 1721 | ** the database fails to open, print an error message and exit. |
| 1722 | */ |
| @@ -1658,10 +1736,14 @@ | |
| 1736 | exit(1); |
| 1737 | } |
| 1738 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1739 | sqlite3_enable_load_extension(p->db, 1); |
| 1740 | #endif |
| 1741 | sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, |
| 1742 | readfileFunc, 0, 0); |
| 1743 | sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, |
| 1744 | writefileFunc, 0, 0); |
| 1745 | } |
| 1746 | } |
| 1747 | |
| 1748 | /* |
| 1749 | ** Do C-language style dequoting. |
| @@ -2414,10 +2496,11 @@ | |
| 2496 | }else |
| 2497 | |
| 2498 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 2499 | struct callback_data data; |
| 2500 | char *zErrMsg = 0; |
| 2501 | int doStats = 0; |
| 2502 | if( nArg!=1 ){ |
| 2503 | fprintf(stderr, "Usage: .fullschema\n"); |
| 2504 | rc = 1; |
| 2505 | goto meta_command_exit; |
| 2506 | } |
| @@ -2432,25 +2515,37 @@ | |
| 2515 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " |
| 2516 | "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" |
| 2517 | "ORDER BY rowid", |
| 2518 | callback, &data, &zErrMsg |
| 2519 | ); |
| 2520 | if( rc==SQLITE_OK ){ |
| 2521 | sqlite3_stmt *pStmt; |
| 2522 | rc = sqlite3_prepare_v2(p->db, |
| 2523 | "SELECT rowid FROM sqlite_master" |
| 2524 | " WHERE name GLOB 'sqlite_stat[134]'", |
| 2525 | -1, &pStmt, 0); |
| 2526 | doStats = sqlite3_step(pStmt)==SQLITE_ROW; |
| 2527 | sqlite3_finalize(pStmt); |
| 2528 | } |
| 2529 | if( doStats==0 ){ |
| 2530 | fprintf(p->out, "/* No STAT tables available */\n"); |
| 2531 | }else{ |
| 2532 | fprintf(p->out, "ANALYZE sqlite_master;\n"); |
| 2533 | sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", |
| 2534 | callback, &data, &zErrMsg); |
| 2535 | data.mode = MODE_Insert; |
| 2536 | data.zDestTable = "sqlite_stat1"; |
| 2537 | shell_exec(p->db, "SELECT * FROM sqlite_stat1", |
| 2538 | shell_callback, &data,&zErrMsg); |
| 2539 | data.zDestTable = "sqlite_stat3"; |
| 2540 | shell_exec(p->db, "SELECT * FROM sqlite_stat3", |
| 2541 | shell_callback, &data,&zErrMsg); |
| 2542 | data.zDestTable = "sqlite_stat4"; |
| 2543 | shell_exec(p->db, "SELECT * FROM sqlite_stat4", |
| 2544 | shell_callback, &data, &zErrMsg); |
| 2545 | fprintf(p->out, "ANALYZE sqlite_master;\n"); |
| 2546 | } |
| 2547 | }else |
| 2548 | |
| 2549 | if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ |
| 2550 | if( nArg==2 ){ |
| 2551 | p->showHeader = booleanValue(azArg[1]); |
| @@ -2736,10 +2831,11 @@ | |
| 2831 | p->mode = MODE_Tcl; |
| 2832 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2833 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2834 | p->mode = MODE_Csv; |
| 2835 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2836 | sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); |
| 2837 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2838 | p->mode = MODE_List; |
| 2839 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2840 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2841 | p->mode = MODE_Insert; |
| @@ -3014,17 +3110,20 @@ | |
| 3110 | } |
| 3111 | }else |
| 3112 | #endif |
| 3113 | |
| 3114 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3115 | if( nArg<2 || nArg>3 ){ |
| 3116 | fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); |
| 3117 | rc = 1; |
| 3118 | } |
| 3119 | if( nArg>=2 ){ |
| 3120 | sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); |
| 3121 | } |
| 3122 | if( nArg>=3 ){ |
| 3123 | sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); |
| 3124 | } |
| 3125 | }else |
| 3126 | |
| 3127 | if( c=='s' |
| 3128 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| 3129 | ){ |
| @@ -3061,10 +3160,12 @@ | |
| 3160 | fprintf(p->out, "\n"); |
| 3161 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3162 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3163 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3164 | output_c_string(p->out, p->separator); |
| 3165 | fprintf(p->out," "); |
| 3166 | output_c_string(p->out, p->newline); |
| 3167 | fprintf(p->out, "\n"); |
| 3168 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3169 | fprintf(p->out,"%9.9s: ","width"); |
| 3170 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3171 | fprintf(p->out,"%d ",p->colWidth[i]); |
| @@ -3677,10 +3778,11 @@ | |
| 3778 | " -list set output mode to 'list'\n" |
| 3779 | " -mmap N default mmap size set to N\n" |
| 3780 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3781 | " -multiplex enable the multiplexor VFS\n" |
| 3782 | #endif |
| 3783 | " -newline SEP set newline character(s) for CSV\n" |
| 3784 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3785 | " -separator SEP set output field separator. Default: '|'\n" |
| 3786 | " -stats print memory stats before each finalize\n" |
| 3787 | " -version show SQLite version\n" |
| 3788 | " -vfs NAME use NAME as the default VFS\n" |
| @@ -3706,10 +3808,11 @@ | |
| 3808 | */ |
| 3809 | static void main_init(struct callback_data *data) { |
| 3810 | memset(data, 0, sizeof(*data)); |
| 3811 | data->mode = MODE_List; |
| 3812 | memcpy(data->separator,"|", 2); |
| 3813 | memcpy(data->newline,"\r\n", 3); |
| 3814 | data->showHeader = 0; |
| 3815 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3816 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3817 | sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); |
| 3818 | sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); |
| @@ -3798,10 +3901,11 @@ | |
| 3901 | return 1; |
| 3902 | } |
| 3903 | if( z[1]=='-' ) z++; |
| 3904 | if( strcmp(z,"-separator")==0 |
| 3905 | || strcmp(z,"-nullvalue")==0 |
| 3906 | || strcmp(z,"-newline")==0 |
| 3907 | || strcmp(z,"-cmd")==0 |
| 3908 | ){ |
| 3909 | (void)cmdline_option_value(argc, argv, ++i); |
| 3910 | }else if( strcmp(z,"-init")==0 ){ |
| 3911 | zInitFile = cmdline_option_value(argc, argv, ++i); |
| @@ -3907,10 +4011,13 @@ | |
| 4011 | data.mode = MODE_Csv; |
| 4012 | memcpy(data.separator,",",2); |
| 4013 | }else if( strcmp(z,"-separator")==0 ){ |
| 4014 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 4015 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4016 | }else if( strcmp(z,"-newline")==0 ){ |
| 4017 | sqlite3_snprintf(sizeof(data.newline), data.newline, |
| 4018 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4019 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 4020 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 4021 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4022 | }else if( strcmp(z,"-header")==0 ){ |
| 4023 | data.showHeader = 1; |
| 4024 |
+136
-29
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -62,10 +62,11 @@ | ||
| 62 | 62 | # define stifle_history(X) |
| 63 | 63 | #endif |
| 64 | 64 | |
| 65 | 65 | #if defined(_WIN32) || defined(WIN32) |
| 66 | 66 | # include <io.h> |
| 67 | +# include <fcntl.h> | |
| 67 | 68 | #define isatty(h) _isatty(h) |
| 68 | 69 | #ifndef access |
| 69 | 70 | # define access(f,m) _access((f),(m)) |
| 70 | 71 | #endif |
| 71 | 72 | #undef popen |
| @@ -456,10 +457,11 @@ | ||
| 456 | 457 | int mode; /* An output mode setting */ |
| 457 | 458 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 458 | 459 | int showHeader; /* True to show column names in List or Column mode */ |
| 459 | 460 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 460 | 461 | char separator[20]; /* Separator character for MODE_List */ |
| 462 | + char newline[20]; /* Record separator in MODE_Csv */ | |
| 461 | 463 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 462 | 464 | int actualWidth[100]; /* Actual width of each column */ |
| 463 | 465 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 464 | 466 | ** the database */ |
| 465 | 467 | struct previous_mode_data explainPrev; |
| @@ -657,11 +659,12 @@ | ||
| 657 | 659 | }; |
| 658 | 660 | |
| 659 | 661 | /* |
| 660 | 662 | ** Output a single term of CSV. Actually, p->separator is used for |
| 661 | 663 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 662 | -** the null value. Strings are quoted if necessary. | |
| 664 | +** the null value. Strings are quoted if necessary. The separator | |
| 665 | +** is only issued if bSep is true. | |
| 663 | 666 | */ |
| 664 | 667 | static void output_csv(struct callback_data *p, const char *z, int bSep){ |
| 665 | 668 | FILE *out = p->out; |
| 666 | 669 | if( z==0 ){ |
| 667 | 670 | fprintf(out,"%s",p->nullvalue); |
| @@ -853,21 +856,30 @@ | ||
| 853 | 856 | } |
| 854 | 857 | fprintf(p->out,"\n"); |
| 855 | 858 | break; |
| 856 | 859 | } |
| 857 | 860 | case MODE_Csv: { |
| 861 | +#if defined(WIN32) || defined(_WIN32) | |
| 862 | + fflush(p->out); | |
| 863 | + _setmode(_fileno(p->out), _O_BINARY); | |
| 864 | +#endif | |
| 858 | 865 | if( p->cnt++==0 && p->showHeader ){ |
| 859 | 866 | for(i=0; i<nArg; i++){ |
| 860 | 867 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 861 | 868 | } |
| 862 | - fprintf(p->out,"\n"); | |
| 869 | + fprintf(p->out,"%s",p->newline); | |
| 863 | 870 | } |
| 864 | - if( azArg==0 ) break; | |
| 865 | - for(i=0; i<nArg; i++){ | |
| 866 | - output_csv(p, azArg[i], i<nArg-1); | |
| 871 | + if( azArg>0 ){ | |
| 872 | + for(i=0; i<nArg; i++){ | |
| 873 | + output_csv(p, azArg[i], i<nArg-1); | |
| 874 | + } | |
| 875 | + fprintf(p->out,"%s",p->newline); | |
| 867 | 876 | } |
| 868 | - fprintf(p->out,"\n"); | |
| 877 | +#if defined(WIN32) || defined(_WIN32) | |
| 878 | + fflush(p->out); | |
| 879 | + _setmode(_fileno(p->out), _O_TEXT); | |
| 880 | +#endif | |
| 869 | 881 | break; |
| 870 | 882 | } |
| 871 | 883 | case MODE_Insert: { |
| 872 | 884 | p->cnt++; |
| 873 | 885 | if( azArg==0 ) break; |
| @@ -1150,12 +1162,14 @@ | ||
| 1150 | 1162 | fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
| 1151 | 1163 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 1152 | 1164 | fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
| 1153 | 1165 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
| 1154 | 1166 | fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
| 1155 | - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); | |
| 1156 | - fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); | |
| 1167 | + if( sqlite3_libversion_number()>=3008000 ){ | |
| 1168 | + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); | |
| 1169 | + fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); | |
| 1170 | + } | |
| 1157 | 1171 | } |
| 1158 | 1172 | |
| 1159 | 1173 | return 0; |
| 1160 | 1174 | } |
| 1161 | 1175 | |
| @@ -1617,11 +1631,12 @@ | ||
| 1617 | 1631 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| 1618 | 1632 | ".save FILE Write in-memory database into FILE\n" |
| 1619 | 1633 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1620 | 1634 | " If TABLE specified, only show tables matching\n" |
| 1621 | 1635 | " LIKE pattern TABLE.\n" |
| 1622 | - ".separator STRING Change separator used by output mode and .import\n" | |
| 1636 | + ".separator STRING ?NL? Change separator used by output mode and .import\n" | |
| 1637 | + " NL is the end-of-line mark for CSV\n" | |
| 1623 | 1638 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1624 | 1639 | ".show Show the current values for various settings\n" |
| 1625 | 1640 | ".stats on|off Turn stats on or off\n" |
| 1626 | 1641 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1627 | 1642 | ".tables ?TABLE? List names of tables\n" |
| @@ -1635,10 +1650,73 @@ | ||
| 1635 | 1650 | " Negative values right-justify\n" |
| 1636 | 1651 | ; |
| 1637 | 1652 | |
| 1638 | 1653 | /* Forward reference */ |
| 1639 | 1654 | static int process_input(struct callback_data *p, FILE *in); |
| 1655 | +/* | |
| 1656 | +** Implementation of the "readfile(X)" SQL function. The entire content | |
| 1657 | +** of the file named X is read and returned as a BLOB. NULL is returned | |
| 1658 | +** if the file does not exist or is unreadable. | |
| 1659 | +*/ | |
| 1660 | +static void readfileFunc( | |
| 1661 | + sqlite3_context *context, | |
| 1662 | + int argc, | |
| 1663 | + sqlite3_value **argv | |
| 1664 | +){ | |
| 1665 | + const char *zName; | |
| 1666 | + FILE *in; | |
| 1667 | + long nIn; | |
| 1668 | + void *pBuf; | |
| 1669 | + | |
| 1670 | + zName = (const char*)sqlite3_value_text(argv[0]); | |
| 1671 | + if( zName==0 ) return; | |
| 1672 | + in = fopen(zName, "rb"); | |
| 1673 | + if( in==0 ) return; | |
| 1674 | + fseek(in, 0, SEEK_END); | |
| 1675 | + nIn = ftell(in); | |
| 1676 | + rewind(in); | |
| 1677 | + pBuf = sqlite3_malloc( nIn ); | |
| 1678 | + if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ | |
| 1679 | + sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); | |
| 1680 | + }else{ | |
| 1681 | + sqlite3_free(pBuf); | |
| 1682 | + } | |
| 1683 | + fclose(in); | |
| 1684 | +} | |
| 1685 | + | |
| 1686 | +/* | |
| 1687 | +** Implementation of the "writefile(X,Y)" SQL function. The argument Y | |
| 1688 | +** is written into file X. The number of bytes written is returned. Or | |
| 1689 | +** NULL is returned if something goes wrong, such as being unable to open | |
| 1690 | +** file X for writing. | |
| 1691 | +*/ | |
| 1692 | +static void writefileFunc( | |
| 1693 | + sqlite3_context *context, | |
| 1694 | + int argc, | |
| 1695 | + sqlite3_value **argv | |
| 1696 | +){ | |
| 1697 | + FILE *out; | |
| 1698 | + const char *z; | |
| 1699 | + int n; | |
| 1700 | + sqlite3_int64 rc; | |
| 1701 | + const char *zFile; | |
| 1702 | + | |
| 1703 | + zFile = (const char*)sqlite3_value_text(argv[0]); | |
| 1704 | + if( zFile==0 ) return; | |
| 1705 | + out = fopen(zFile, "wb"); | |
| 1706 | + if( out==0 ) return; | |
| 1707 | + z = (const char*)sqlite3_value_blob(argv[1]); | |
| 1708 | + if( z==0 ){ | |
| 1709 | + n = 0; | |
| 1710 | + rc = 0; | |
| 1711 | + }else{ | |
| 1712 | + n = sqlite3_value_bytes(argv[1]); | |
| 1713 | + rc = fwrite(z, 1, n, out); | |
| 1714 | + } | |
| 1715 | + fclose(out); | |
| 1716 | + sqlite3_result_int64(context, rc); | |
| 1717 | +} | |
| 1640 | 1718 | |
| 1641 | 1719 | /* |
| 1642 | 1720 | ** Make sure the database is open. If it is not, then open it. If |
| 1643 | 1721 | ** the database fails to open, print an error message and exit. |
| 1644 | 1722 | */ |
| @@ -1658,10 +1736,14 @@ | ||
| 1658 | 1736 | exit(1); |
| 1659 | 1737 | } |
| 1660 | 1738 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1661 | 1739 | sqlite3_enable_load_extension(p->db, 1); |
| 1662 | 1740 | #endif |
| 1741 | + sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, | |
| 1742 | + readfileFunc, 0, 0); | |
| 1743 | + sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, | |
| 1744 | + writefileFunc, 0, 0); | |
| 1663 | 1745 | } |
| 1664 | 1746 | } |
| 1665 | 1747 | |
| 1666 | 1748 | /* |
| 1667 | 1749 | ** Do C-language style dequoting. |
| @@ -2414,10 +2496,11 @@ | ||
| 2414 | 2496 | }else |
| 2415 | 2497 | |
| 2416 | 2498 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 2417 | 2499 | struct callback_data data; |
| 2418 | 2500 | char *zErrMsg = 0; |
| 2501 | + int doStats = 0; | |
| 2419 | 2502 | if( nArg!=1 ){ |
| 2420 | 2503 | fprintf(stderr, "Usage: .fullschema\n"); |
| 2421 | 2504 | rc = 1; |
| 2422 | 2505 | goto meta_command_exit; |
| 2423 | 2506 | } |
| @@ -2432,25 +2515,37 @@ | ||
| 2432 | 2515 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " |
| 2433 | 2516 | "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" |
| 2434 | 2517 | "ORDER BY rowid", |
| 2435 | 2518 | callback, &data, &zErrMsg |
| 2436 | 2519 | ); |
| 2437 | - sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", | |
| 2438 | - callback, &data, &zErrMsg); | |
| 2439 | - data.mode = MODE_Insert; | |
| 2440 | - data.zDestTable = "sqlite_stat1"; | |
| 2441 | - shell_exec(p->db, "SELECT * FROM sqlite_stat1", | |
| 2442 | - shell_callback, &data,&zErrMsg); | |
| 2443 | - data.zDestTable = "sqlite_stat3"; | |
| 2444 | - shell_exec(p->db, "SELECT * FROM sqlite_stat3", | |
| 2445 | - shell_callback, &data,&zErrMsg); | |
| 2446 | - data.zDestTable = "sqlite_stat4"; | |
| 2447 | - shell_exec(p->db, "SELECT * FROM sqlite_stat4", | |
| 2448 | - shell_callback, &data, &zErrMsg); | |
| 2449 | - data.mode = MODE_Semi; | |
| 2450 | - shell_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", | |
| 2451 | - shell_callback, &data, &zErrMsg); | |
| 2520 | + if( rc==SQLITE_OK ){ | |
| 2521 | + sqlite3_stmt *pStmt; | |
| 2522 | + rc = sqlite3_prepare_v2(p->db, | |
| 2523 | + "SELECT rowid FROM sqlite_master" | |
| 2524 | + " WHERE name GLOB 'sqlite_stat[134]'", | |
| 2525 | + -1, &pStmt, 0); | |
| 2526 | + doStats = sqlite3_step(pStmt)==SQLITE_ROW; | |
| 2527 | + sqlite3_finalize(pStmt); | |
| 2528 | + } | |
| 2529 | + if( doStats==0 ){ | |
| 2530 | + fprintf(p->out, "/* No STAT tables available */\n"); | |
| 2531 | + }else{ | |
| 2532 | + fprintf(p->out, "ANALYZE sqlite_master;\n"); | |
| 2533 | + sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", | |
| 2534 | + callback, &data, &zErrMsg); | |
| 2535 | + data.mode = MODE_Insert; | |
| 2536 | + data.zDestTable = "sqlite_stat1"; | |
| 2537 | + shell_exec(p->db, "SELECT * FROM sqlite_stat1", | |
| 2538 | + shell_callback, &data,&zErrMsg); | |
| 2539 | + data.zDestTable = "sqlite_stat3"; | |
| 2540 | + shell_exec(p->db, "SELECT * FROM sqlite_stat3", | |
| 2541 | + shell_callback, &data,&zErrMsg); | |
| 2542 | + data.zDestTable = "sqlite_stat4"; | |
| 2543 | + shell_exec(p->db, "SELECT * FROM sqlite_stat4", | |
| 2544 | + shell_callback, &data, &zErrMsg); | |
| 2545 | + fprintf(p->out, "ANALYZE sqlite_master;\n"); | |
| 2546 | + } | |
| 2452 | 2547 | }else |
| 2453 | 2548 | |
| 2454 | 2549 | if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ |
| 2455 | 2550 | if( nArg==2 ){ |
| 2456 | 2551 | p->showHeader = booleanValue(azArg[1]); |
| @@ -2736,10 +2831,11 @@ | ||
| 2736 | 2831 | p->mode = MODE_Tcl; |
| 2737 | 2832 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2738 | 2833 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2739 | 2834 | p->mode = MODE_Csv; |
| 2740 | 2835 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2836 | + sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); | |
| 2741 | 2837 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2742 | 2838 | p->mode = MODE_List; |
| 2743 | 2839 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2744 | 2840 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2745 | 2841 | p->mode = MODE_Insert; |
| @@ -3014,17 +3110,20 @@ | ||
| 3014 | 3110 | } |
| 3015 | 3111 | }else |
| 3016 | 3112 | #endif |
| 3017 | 3113 | |
| 3018 | 3114 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3019 | - if( nArg==2 ){ | |
| 3020 | - sqlite3_snprintf(sizeof(p->separator), p->separator, | |
| 3021 | - "%.*s", (int)sizeof(p->separator)-1, azArg[1]); | |
| 3022 | - }else{ | |
| 3023 | - fprintf(stderr, "Usage: .separator STRING\n"); | |
| 3115 | + if( nArg<2 || nArg>3 ){ | |
| 3116 | + fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); | |
| 3024 | 3117 | rc = 1; |
| 3025 | 3118 | } |
| 3119 | + if( nArg>=2 ){ | |
| 3120 | + sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); | |
| 3121 | + } | |
| 3122 | + if( nArg>=3 ){ | |
| 3123 | + sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); | |
| 3124 | + } | |
| 3026 | 3125 | }else |
| 3027 | 3126 | |
| 3028 | 3127 | if( c=='s' |
| 3029 | 3128 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| 3030 | 3129 | ){ |
| @@ -3061,10 +3160,12 @@ | ||
| 3061 | 3160 | fprintf(p->out, "\n"); |
| 3062 | 3161 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3063 | 3162 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3064 | 3163 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3065 | 3164 | output_c_string(p->out, p->separator); |
| 3165 | + fprintf(p->out," "); | |
| 3166 | + output_c_string(p->out, p->newline); | |
| 3066 | 3167 | fprintf(p->out, "\n"); |
| 3067 | 3168 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3068 | 3169 | fprintf(p->out,"%9.9s: ","width"); |
| 3069 | 3170 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3070 | 3171 | fprintf(p->out,"%d ",p->colWidth[i]); |
| @@ -3677,10 +3778,11 @@ | ||
| 3677 | 3778 | " -list set output mode to 'list'\n" |
| 3678 | 3779 | " -mmap N default mmap size set to N\n" |
| 3679 | 3780 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3680 | 3781 | " -multiplex enable the multiplexor VFS\n" |
| 3681 | 3782 | #endif |
| 3783 | + " -newline SEP set newline character(s) for CSV\n" | |
| 3682 | 3784 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3683 | 3785 | " -separator SEP set output field separator. Default: '|'\n" |
| 3684 | 3786 | " -stats print memory stats before each finalize\n" |
| 3685 | 3787 | " -version show SQLite version\n" |
| 3686 | 3788 | " -vfs NAME use NAME as the default VFS\n" |
| @@ -3706,10 +3808,11 @@ | ||
| 3706 | 3808 | */ |
| 3707 | 3809 | static void main_init(struct callback_data *data) { |
| 3708 | 3810 | memset(data, 0, sizeof(*data)); |
| 3709 | 3811 | data->mode = MODE_List; |
| 3710 | 3812 | memcpy(data->separator,"|", 2); |
| 3813 | + memcpy(data->newline,"\r\n", 3); | |
| 3711 | 3814 | data->showHeader = 0; |
| 3712 | 3815 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3713 | 3816 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3714 | 3817 | sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); |
| 3715 | 3818 | sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); |
| @@ -3798,10 +3901,11 @@ | ||
| 3798 | 3901 | return 1; |
| 3799 | 3902 | } |
| 3800 | 3903 | if( z[1]=='-' ) z++; |
| 3801 | 3904 | if( strcmp(z,"-separator")==0 |
| 3802 | 3905 | || strcmp(z,"-nullvalue")==0 |
| 3906 | + || strcmp(z,"-newline")==0 | |
| 3803 | 3907 | || strcmp(z,"-cmd")==0 |
| 3804 | 3908 | ){ |
| 3805 | 3909 | (void)cmdline_option_value(argc, argv, ++i); |
| 3806 | 3910 | }else if( strcmp(z,"-init")==0 ){ |
| 3807 | 3911 | zInitFile = cmdline_option_value(argc, argv, ++i); |
| @@ -3907,10 +4011,13 @@ | ||
| 3907 | 4011 | data.mode = MODE_Csv; |
| 3908 | 4012 | memcpy(data.separator,",",2); |
| 3909 | 4013 | }else if( strcmp(z,"-separator")==0 ){ |
| 3910 | 4014 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 3911 | 4015 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4016 | + }else if( strcmp(z,"-newline")==0 ){ | |
| 4017 | + sqlite3_snprintf(sizeof(data.newline), data.newline, | |
| 4018 | + "%s",cmdline_option_value(argc,argv,++i)); | |
| 3912 | 4019 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 3913 | 4020 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 3914 | 4021 | "%s",cmdline_option_value(argc,argv,++i)); |
| 3915 | 4022 | }else if( strcmp(z,"-header")==0 ){ |
| 3916 | 4023 | data.showHeader = 1; |
| 3917 | 4024 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -62,10 +62,11 @@ | |
| 62 | # define stifle_history(X) |
| 63 | #endif |
| 64 | |
| 65 | #if defined(_WIN32) || defined(WIN32) |
| 66 | # include <io.h> |
| 67 | #define isatty(h) _isatty(h) |
| 68 | #ifndef access |
| 69 | # define access(f,m) _access((f),(m)) |
| 70 | #endif |
| 71 | #undef popen |
| @@ -456,10 +457,11 @@ | |
| 456 | int mode; /* An output mode setting */ |
| 457 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 458 | int showHeader; /* True to show column names in List or Column mode */ |
| 459 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 460 | char separator[20]; /* Separator character for MODE_List */ |
| 461 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 462 | int actualWidth[100]; /* Actual width of each column */ |
| 463 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 464 | ** the database */ |
| 465 | struct previous_mode_data explainPrev; |
| @@ -657,11 +659,12 @@ | |
| 657 | }; |
| 658 | |
| 659 | /* |
| 660 | ** Output a single term of CSV. Actually, p->separator is used for |
| 661 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 662 | ** the null value. Strings are quoted if necessary. |
| 663 | */ |
| 664 | static void output_csv(struct callback_data *p, const char *z, int bSep){ |
| 665 | FILE *out = p->out; |
| 666 | if( z==0 ){ |
| 667 | fprintf(out,"%s",p->nullvalue); |
| @@ -853,21 +856,30 @@ | |
| 853 | } |
| 854 | fprintf(p->out,"\n"); |
| 855 | break; |
| 856 | } |
| 857 | case MODE_Csv: { |
| 858 | if( p->cnt++==0 && p->showHeader ){ |
| 859 | for(i=0; i<nArg; i++){ |
| 860 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 861 | } |
| 862 | fprintf(p->out,"\n"); |
| 863 | } |
| 864 | if( azArg==0 ) break; |
| 865 | for(i=0; i<nArg; i++){ |
| 866 | output_csv(p, azArg[i], i<nArg-1); |
| 867 | } |
| 868 | fprintf(p->out,"\n"); |
| 869 | break; |
| 870 | } |
| 871 | case MODE_Insert: { |
| 872 | p->cnt++; |
| 873 | if( azArg==0 ) break; |
| @@ -1150,12 +1162,14 @@ | |
| 1150 | fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
| 1151 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 1152 | fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
| 1153 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
| 1154 | fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
| 1155 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 1156 | fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
| 1157 | } |
| 1158 | |
| 1159 | return 0; |
| 1160 | } |
| 1161 | |
| @@ -1617,11 +1631,12 @@ | |
| 1617 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| 1618 | ".save FILE Write in-memory database into FILE\n" |
| 1619 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1620 | " If TABLE specified, only show tables matching\n" |
| 1621 | " LIKE pattern TABLE.\n" |
| 1622 | ".separator STRING Change separator used by output mode and .import\n" |
| 1623 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1624 | ".show Show the current values for various settings\n" |
| 1625 | ".stats on|off Turn stats on or off\n" |
| 1626 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1627 | ".tables ?TABLE? List names of tables\n" |
| @@ -1635,10 +1650,73 @@ | |
| 1635 | " Negative values right-justify\n" |
| 1636 | ; |
| 1637 | |
| 1638 | /* Forward reference */ |
| 1639 | static int process_input(struct callback_data *p, FILE *in); |
| 1640 | |
| 1641 | /* |
| 1642 | ** Make sure the database is open. If it is not, then open it. If |
| 1643 | ** the database fails to open, print an error message and exit. |
| 1644 | */ |
| @@ -1658,10 +1736,14 @@ | |
| 1658 | exit(1); |
| 1659 | } |
| 1660 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1661 | sqlite3_enable_load_extension(p->db, 1); |
| 1662 | #endif |
| 1663 | } |
| 1664 | } |
| 1665 | |
| 1666 | /* |
| 1667 | ** Do C-language style dequoting. |
| @@ -2414,10 +2496,11 @@ | |
| 2414 | }else |
| 2415 | |
| 2416 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 2417 | struct callback_data data; |
| 2418 | char *zErrMsg = 0; |
| 2419 | if( nArg!=1 ){ |
| 2420 | fprintf(stderr, "Usage: .fullschema\n"); |
| 2421 | rc = 1; |
| 2422 | goto meta_command_exit; |
| 2423 | } |
| @@ -2432,25 +2515,37 @@ | |
| 2432 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " |
| 2433 | "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" |
| 2434 | "ORDER BY rowid", |
| 2435 | callback, &data, &zErrMsg |
| 2436 | ); |
| 2437 | sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", |
| 2438 | callback, &data, &zErrMsg); |
| 2439 | data.mode = MODE_Insert; |
| 2440 | data.zDestTable = "sqlite_stat1"; |
| 2441 | shell_exec(p->db, "SELECT * FROM sqlite_stat1", |
| 2442 | shell_callback, &data,&zErrMsg); |
| 2443 | data.zDestTable = "sqlite_stat3"; |
| 2444 | shell_exec(p->db, "SELECT * FROM sqlite_stat3", |
| 2445 | shell_callback, &data,&zErrMsg); |
| 2446 | data.zDestTable = "sqlite_stat4"; |
| 2447 | shell_exec(p->db, "SELECT * FROM sqlite_stat4", |
| 2448 | shell_callback, &data, &zErrMsg); |
| 2449 | data.mode = MODE_Semi; |
| 2450 | shell_exec(p->db, "SELECT 'ANALYZE sqlite_master;'", |
| 2451 | shell_callback, &data, &zErrMsg); |
| 2452 | }else |
| 2453 | |
| 2454 | if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ |
| 2455 | if( nArg==2 ){ |
| 2456 | p->showHeader = booleanValue(azArg[1]); |
| @@ -2736,10 +2831,11 @@ | |
| 2736 | p->mode = MODE_Tcl; |
| 2737 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2738 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2739 | p->mode = MODE_Csv; |
| 2740 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2741 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2742 | p->mode = MODE_List; |
| 2743 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2744 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2745 | p->mode = MODE_Insert; |
| @@ -3014,17 +3110,20 @@ | |
| 3014 | } |
| 3015 | }else |
| 3016 | #endif |
| 3017 | |
| 3018 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3019 | if( nArg==2 ){ |
| 3020 | sqlite3_snprintf(sizeof(p->separator), p->separator, |
| 3021 | "%.*s", (int)sizeof(p->separator)-1, azArg[1]); |
| 3022 | }else{ |
| 3023 | fprintf(stderr, "Usage: .separator STRING\n"); |
| 3024 | rc = 1; |
| 3025 | } |
| 3026 | }else |
| 3027 | |
| 3028 | if( c=='s' |
| 3029 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| 3030 | ){ |
| @@ -3061,10 +3160,12 @@ | |
| 3061 | fprintf(p->out, "\n"); |
| 3062 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3063 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3064 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3065 | output_c_string(p->out, p->separator); |
| 3066 | fprintf(p->out, "\n"); |
| 3067 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3068 | fprintf(p->out,"%9.9s: ","width"); |
| 3069 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3070 | fprintf(p->out,"%d ",p->colWidth[i]); |
| @@ -3677,10 +3778,11 @@ | |
| 3677 | " -list set output mode to 'list'\n" |
| 3678 | " -mmap N default mmap size set to N\n" |
| 3679 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3680 | " -multiplex enable the multiplexor VFS\n" |
| 3681 | #endif |
| 3682 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3683 | " -separator SEP set output field separator. Default: '|'\n" |
| 3684 | " -stats print memory stats before each finalize\n" |
| 3685 | " -version show SQLite version\n" |
| 3686 | " -vfs NAME use NAME as the default VFS\n" |
| @@ -3706,10 +3808,11 @@ | |
| 3706 | */ |
| 3707 | static void main_init(struct callback_data *data) { |
| 3708 | memset(data, 0, sizeof(*data)); |
| 3709 | data->mode = MODE_List; |
| 3710 | memcpy(data->separator,"|", 2); |
| 3711 | data->showHeader = 0; |
| 3712 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3713 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3714 | sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); |
| 3715 | sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); |
| @@ -3798,10 +3901,11 @@ | |
| 3798 | return 1; |
| 3799 | } |
| 3800 | if( z[1]=='-' ) z++; |
| 3801 | if( strcmp(z,"-separator")==0 |
| 3802 | || strcmp(z,"-nullvalue")==0 |
| 3803 | || strcmp(z,"-cmd")==0 |
| 3804 | ){ |
| 3805 | (void)cmdline_option_value(argc, argv, ++i); |
| 3806 | }else if( strcmp(z,"-init")==0 ){ |
| 3807 | zInitFile = cmdline_option_value(argc, argv, ++i); |
| @@ -3907,10 +4011,13 @@ | |
| 3907 | data.mode = MODE_Csv; |
| 3908 | memcpy(data.separator,",",2); |
| 3909 | }else if( strcmp(z,"-separator")==0 ){ |
| 3910 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 3911 | "%s",cmdline_option_value(argc,argv,++i)); |
| 3912 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 3913 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 3914 | "%s",cmdline_option_value(argc,argv,++i)); |
| 3915 | }else if( strcmp(z,"-header")==0 ){ |
| 3916 | data.showHeader = 1; |
| 3917 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -62,10 +62,11 @@ | |
| 62 | # define stifle_history(X) |
| 63 | #endif |
| 64 | |
| 65 | #if defined(_WIN32) || defined(WIN32) |
| 66 | # include <io.h> |
| 67 | # include <fcntl.h> |
| 68 | #define isatty(h) _isatty(h) |
| 69 | #ifndef access |
| 70 | # define access(f,m) _access((f),(m)) |
| 71 | #endif |
| 72 | #undef popen |
| @@ -456,10 +457,11 @@ | |
| 457 | int mode; /* An output mode setting */ |
| 458 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 459 | int showHeader; /* True to show column names in List or Column mode */ |
| 460 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 461 | char separator[20]; /* Separator character for MODE_List */ |
| 462 | char newline[20]; /* Record separator in MODE_Csv */ |
| 463 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 464 | int actualWidth[100]; /* Actual width of each column */ |
| 465 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 466 | ** the database */ |
| 467 | struct previous_mode_data explainPrev; |
| @@ -657,11 +659,12 @@ | |
| 659 | }; |
| 660 | |
| 661 | /* |
| 662 | ** Output a single term of CSV. Actually, p->separator is used for |
| 663 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 664 | ** the null value. Strings are quoted if necessary. The separator |
| 665 | ** is only issued if bSep is true. |
| 666 | */ |
| 667 | static void output_csv(struct callback_data *p, const char *z, int bSep){ |
| 668 | FILE *out = p->out; |
| 669 | if( z==0 ){ |
| 670 | fprintf(out,"%s",p->nullvalue); |
| @@ -853,21 +856,30 @@ | |
| 856 | } |
| 857 | fprintf(p->out,"\n"); |
| 858 | break; |
| 859 | } |
| 860 | case MODE_Csv: { |
| 861 | #if defined(WIN32) || defined(_WIN32) |
| 862 | fflush(p->out); |
| 863 | _setmode(_fileno(p->out), _O_BINARY); |
| 864 | #endif |
| 865 | if( p->cnt++==0 && p->showHeader ){ |
| 866 | for(i=0; i<nArg; i++){ |
| 867 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 868 | } |
| 869 | fprintf(p->out,"%s",p->newline); |
| 870 | } |
| 871 | if( azArg>0 ){ |
| 872 | for(i=0; i<nArg; i++){ |
| 873 | output_csv(p, azArg[i], i<nArg-1); |
| 874 | } |
| 875 | fprintf(p->out,"%s",p->newline); |
| 876 | } |
| 877 | #if defined(WIN32) || defined(_WIN32) |
| 878 | fflush(p->out); |
| 879 | _setmode(_fileno(p->out), _O_TEXT); |
| 880 | #endif |
| 881 | break; |
| 882 | } |
| 883 | case MODE_Insert: { |
| 884 | p->cnt++; |
| 885 | if( azArg==0 ) break; |
| @@ -1150,12 +1162,14 @@ | |
| 1162 | fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
| 1163 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 1164 | fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
| 1165 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
| 1166 | fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
| 1167 | if( sqlite3_libversion_number()>=3008000 ){ |
| 1168 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 1169 | fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
| 1170 | } |
| 1171 | } |
| 1172 | |
| 1173 | return 0; |
| 1174 | } |
| 1175 | |
| @@ -1617,11 +1631,12 @@ | |
| 1631 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| 1632 | ".save FILE Write in-memory database into FILE\n" |
| 1633 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1634 | " If TABLE specified, only show tables matching\n" |
| 1635 | " LIKE pattern TABLE.\n" |
| 1636 | ".separator STRING ?NL? Change separator used by output mode and .import\n" |
| 1637 | " NL is the end-of-line mark for CSV\n" |
| 1638 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1639 | ".show Show the current values for various settings\n" |
| 1640 | ".stats on|off Turn stats on or off\n" |
| 1641 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1642 | ".tables ?TABLE? List names of tables\n" |
| @@ -1635,10 +1650,73 @@ | |
| 1650 | " Negative values right-justify\n" |
| 1651 | ; |
| 1652 | |
| 1653 | /* Forward reference */ |
| 1654 | static int process_input(struct callback_data *p, FILE *in); |
| 1655 | /* |
| 1656 | ** Implementation of the "readfile(X)" SQL function. The entire content |
| 1657 | ** of the file named X is read and returned as a BLOB. NULL is returned |
| 1658 | ** if the file does not exist or is unreadable. |
| 1659 | */ |
| 1660 | static void readfileFunc( |
| 1661 | sqlite3_context *context, |
| 1662 | int argc, |
| 1663 | sqlite3_value **argv |
| 1664 | ){ |
| 1665 | const char *zName; |
| 1666 | FILE *in; |
| 1667 | long nIn; |
| 1668 | void *pBuf; |
| 1669 | |
| 1670 | zName = (const char*)sqlite3_value_text(argv[0]); |
| 1671 | if( zName==0 ) return; |
| 1672 | in = fopen(zName, "rb"); |
| 1673 | if( in==0 ) return; |
| 1674 | fseek(in, 0, SEEK_END); |
| 1675 | nIn = ftell(in); |
| 1676 | rewind(in); |
| 1677 | pBuf = sqlite3_malloc( nIn ); |
| 1678 | if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ |
| 1679 | sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); |
| 1680 | }else{ |
| 1681 | sqlite3_free(pBuf); |
| 1682 | } |
| 1683 | fclose(in); |
| 1684 | } |
| 1685 | |
| 1686 | /* |
| 1687 | ** Implementation of the "writefile(X,Y)" SQL function. The argument Y |
| 1688 | ** is written into file X. The number of bytes written is returned. Or |
| 1689 | ** NULL is returned if something goes wrong, such as being unable to open |
| 1690 | ** file X for writing. |
| 1691 | */ |
| 1692 | static void writefileFunc( |
| 1693 | sqlite3_context *context, |
| 1694 | int argc, |
| 1695 | sqlite3_value **argv |
| 1696 | ){ |
| 1697 | FILE *out; |
| 1698 | const char *z; |
| 1699 | int n; |
| 1700 | sqlite3_int64 rc; |
| 1701 | const char *zFile; |
| 1702 | |
| 1703 | zFile = (const char*)sqlite3_value_text(argv[0]); |
| 1704 | if( zFile==0 ) return; |
| 1705 | out = fopen(zFile, "wb"); |
| 1706 | if( out==0 ) return; |
| 1707 | z = (const char*)sqlite3_value_blob(argv[1]); |
| 1708 | if( z==0 ){ |
| 1709 | n = 0; |
| 1710 | rc = 0; |
| 1711 | }else{ |
| 1712 | n = sqlite3_value_bytes(argv[1]); |
| 1713 | rc = fwrite(z, 1, n, out); |
| 1714 | } |
| 1715 | fclose(out); |
| 1716 | sqlite3_result_int64(context, rc); |
| 1717 | } |
| 1718 | |
| 1719 | /* |
| 1720 | ** Make sure the database is open. If it is not, then open it. If |
| 1721 | ** the database fails to open, print an error message and exit. |
| 1722 | */ |
| @@ -1658,10 +1736,14 @@ | |
| 1736 | exit(1); |
| 1737 | } |
| 1738 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1739 | sqlite3_enable_load_extension(p->db, 1); |
| 1740 | #endif |
| 1741 | sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, |
| 1742 | readfileFunc, 0, 0); |
| 1743 | sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, |
| 1744 | writefileFunc, 0, 0); |
| 1745 | } |
| 1746 | } |
| 1747 | |
| 1748 | /* |
| 1749 | ** Do C-language style dequoting. |
| @@ -2414,10 +2496,11 @@ | |
| 2496 | }else |
| 2497 | |
| 2498 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 2499 | struct callback_data data; |
| 2500 | char *zErrMsg = 0; |
| 2501 | int doStats = 0; |
| 2502 | if( nArg!=1 ){ |
| 2503 | fprintf(stderr, "Usage: .fullschema\n"); |
| 2504 | rc = 1; |
| 2505 | goto meta_command_exit; |
| 2506 | } |
| @@ -2432,25 +2515,37 @@ | |
| 2515 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " |
| 2516 | "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" |
| 2517 | "ORDER BY rowid", |
| 2518 | callback, &data, &zErrMsg |
| 2519 | ); |
| 2520 | if( rc==SQLITE_OK ){ |
| 2521 | sqlite3_stmt *pStmt; |
| 2522 | rc = sqlite3_prepare_v2(p->db, |
| 2523 | "SELECT rowid FROM sqlite_master" |
| 2524 | " WHERE name GLOB 'sqlite_stat[134]'", |
| 2525 | -1, &pStmt, 0); |
| 2526 | doStats = sqlite3_step(pStmt)==SQLITE_ROW; |
| 2527 | sqlite3_finalize(pStmt); |
| 2528 | } |
| 2529 | if( doStats==0 ){ |
| 2530 | fprintf(p->out, "/* No STAT tables available */\n"); |
| 2531 | }else{ |
| 2532 | fprintf(p->out, "ANALYZE sqlite_master;\n"); |
| 2533 | sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", |
| 2534 | callback, &data, &zErrMsg); |
| 2535 | data.mode = MODE_Insert; |
| 2536 | data.zDestTable = "sqlite_stat1"; |
| 2537 | shell_exec(p->db, "SELECT * FROM sqlite_stat1", |
| 2538 | shell_callback, &data,&zErrMsg); |
| 2539 | data.zDestTable = "sqlite_stat3"; |
| 2540 | shell_exec(p->db, "SELECT * FROM sqlite_stat3", |
| 2541 | shell_callback, &data,&zErrMsg); |
| 2542 | data.zDestTable = "sqlite_stat4"; |
| 2543 | shell_exec(p->db, "SELECT * FROM sqlite_stat4", |
| 2544 | shell_callback, &data, &zErrMsg); |
| 2545 | fprintf(p->out, "ANALYZE sqlite_master;\n"); |
| 2546 | } |
| 2547 | }else |
| 2548 | |
| 2549 | if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ |
| 2550 | if( nArg==2 ){ |
| 2551 | p->showHeader = booleanValue(azArg[1]); |
| @@ -2736,10 +2831,11 @@ | |
| 2831 | p->mode = MODE_Tcl; |
| 2832 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2833 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2834 | p->mode = MODE_Csv; |
| 2835 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2836 | sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); |
| 2837 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2838 | p->mode = MODE_List; |
| 2839 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2840 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2841 | p->mode = MODE_Insert; |
| @@ -3014,17 +3110,20 @@ | |
| 3110 | } |
| 3111 | }else |
| 3112 | #endif |
| 3113 | |
| 3114 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3115 | if( nArg<2 || nArg>3 ){ |
| 3116 | fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); |
| 3117 | rc = 1; |
| 3118 | } |
| 3119 | if( nArg>=2 ){ |
| 3120 | sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); |
| 3121 | } |
| 3122 | if( nArg>=3 ){ |
| 3123 | sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); |
| 3124 | } |
| 3125 | }else |
| 3126 | |
| 3127 | if( c=='s' |
| 3128 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| 3129 | ){ |
| @@ -3061,10 +3160,12 @@ | |
| 3160 | fprintf(p->out, "\n"); |
| 3161 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3162 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3163 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3164 | output_c_string(p->out, p->separator); |
| 3165 | fprintf(p->out," "); |
| 3166 | output_c_string(p->out, p->newline); |
| 3167 | fprintf(p->out, "\n"); |
| 3168 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3169 | fprintf(p->out,"%9.9s: ","width"); |
| 3170 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3171 | fprintf(p->out,"%d ",p->colWidth[i]); |
| @@ -3677,10 +3778,11 @@ | |
| 3778 | " -list set output mode to 'list'\n" |
| 3779 | " -mmap N default mmap size set to N\n" |
| 3780 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3781 | " -multiplex enable the multiplexor VFS\n" |
| 3782 | #endif |
| 3783 | " -newline SEP set newline character(s) for CSV\n" |
| 3784 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3785 | " -separator SEP set output field separator. Default: '|'\n" |
| 3786 | " -stats print memory stats before each finalize\n" |
| 3787 | " -version show SQLite version\n" |
| 3788 | " -vfs NAME use NAME as the default VFS\n" |
| @@ -3706,10 +3808,11 @@ | |
| 3808 | */ |
| 3809 | static void main_init(struct callback_data *data) { |
| 3810 | memset(data, 0, sizeof(*data)); |
| 3811 | data->mode = MODE_List; |
| 3812 | memcpy(data->separator,"|", 2); |
| 3813 | memcpy(data->newline,"\r\n", 3); |
| 3814 | data->showHeader = 0; |
| 3815 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3816 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3817 | sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); |
| 3818 | sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); |
| @@ -3798,10 +3901,11 @@ | |
| 3901 | return 1; |
| 3902 | } |
| 3903 | if( z[1]=='-' ) z++; |
| 3904 | if( strcmp(z,"-separator")==0 |
| 3905 | || strcmp(z,"-nullvalue")==0 |
| 3906 | || strcmp(z,"-newline")==0 |
| 3907 | || strcmp(z,"-cmd")==0 |
| 3908 | ){ |
| 3909 | (void)cmdline_option_value(argc, argv, ++i); |
| 3910 | }else if( strcmp(z,"-init")==0 ){ |
| 3911 | zInitFile = cmdline_option_value(argc, argv, ++i); |
| @@ -3907,10 +4011,13 @@ | |
| 4011 | data.mode = MODE_Csv; |
| 4012 | memcpy(data.separator,",",2); |
| 4013 | }else if( strcmp(z,"-separator")==0 ){ |
| 4014 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 4015 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4016 | }else if( strcmp(z,"-newline")==0 ){ |
| 4017 | sqlite3_snprintf(sizeof(data.newline), data.newline, |
| 4018 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4019 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 4020 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 4021 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4022 | }else if( strcmp(z,"-header")==0 ){ |
| 4023 | data.showHeader = 1; |
| 4024 |