=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/sqlite3/Attic/shell.c,v retrieving revision 1.1.1.10 retrieving revision 1.1.1.11 diff -c -r1.1.1.10 -r1.1.1.11 *** src/usr.bin/sqlite3/Attic/shell.c 2015/03/16 00:08:48 1.1.1.10 --- src/usr.bin/sqlite3/Attic/shell.c 2015/04/04 23:26:28 1.1.1.11 *************** *** 18,23 **** --- 18,30 ---- #endif /* + ** If requested, include the SQLite compiler options file for MSVC. + */ + #if defined(INCLUDE_MSVC_H) + #include "msvc.h" + #endif + + /* ** Enable large-file support for fopen() and friends on unix. */ #ifndef SQLITE_DISABLE_LFS *************** *** 48,70 **** # include #endif ! #if defined(HAVE_READLINE) && HAVE_READLINE!=0 # include # include - #else - # undef HAVE_READLINE #endif ! #if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE) ! # define HAVE_READLINE 1 # include #endif ! #if !defined(HAVE_READLINE) ! # define add_history(X) ! # define read_history(X) ! # define write_history(X) ! # define stifle_history(X) #endif #if defined(_WIN32) || defined(WIN32) # include # include --- 55,96 ---- # include #endif ! #if HAVE_READLINE # include # include #endif ! ! #if HAVE_EDITLINE # include #endif ! ! #if HAVE_EDITLINE || HAVE_READLINE ! ! # define shell_add_history(X) add_history(X) ! # define shell_read_history(X) read_history(X) ! # define shell_write_history(X) write_history(X) ! # define shell_stifle_history(X) stifle_history(X) ! # define shell_readline(X) readline(X) ! ! #elif HAVE_LINENOISE ! ! # include "linenoise.h" ! # define shell_add_history(X) linenoiseHistoryAdd(X) ! # define shell_read_history(X) linenoiseHistoryLoad(X) ! # define shell_write_history(X) linenoiseHistorySave(X) ! # define shell_stifle_history(X) linenoiseHistorySetMaxLen(X) ! # define shell_readline(X) linenoise(X) ! ! #else ! ! # define shell_read_history(X) ! # define shell_write_history(X) ! # define shell_stifle_history(X) ! ! # define SHELL_USE_LOCAL_GETLINE 1 #endif + #if defined(_WIN32) || defined(WIN32) # include # include *************** *** 172,178 **** static FILETIME ftKernelBegin; static FILETIME ftUserBegin; static sqlite3_int64 ftWallBegin; ! typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); static GETPROCTIMES getProcessTimesAddr = NULL; /* --- 198,205 ---- static FILETIME ftKernelBegin; static FILETIME ftUserBegin; static sqlite3_int64 ftWallBegin; ! typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, ! LPFILETIME, LPFILETIME); static GETPROCTIMES getProcessTimesAddr = NULL; /* *************** *** 183,197 **** if( getProcessTimesAddr ){ return 1; } else { ! /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions. ! ** See if the version we are running on has it, and if it does, save off ! ** a pointer to it and the current process handle. */ hProcess = GetCurrentProcess(); if( hProcess ){ HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); if( NULL != hinstLib ){ ! getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); if( NULL != getProcessTimesAddr ){ return 1; } --- 210,225 ---- if( getProcessTimesAddr ){ return 1; } else { ! /* GetProcessTimes() isn't supported in WIN95 and some other Windows ! ** versions. See if the version we are running on has it, and if it ! ** does, save off a pointer to it and the current process handle. */ hProcess = GetCurrentProcess(); if( hProcess ){ HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); if( NULL != hinstLib ){ ! getProcessTimesAddr = ! (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); if( NULL != getProcessTimesAddr ){ return 1; } *************** *** 208,214 **** static void beginTimer(void){ if( enableTimer && getProcessTimesAddr ){ FILETIME ftCreation, ftExit; ! getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin); ftWallBegin = timeOfDay(); } } --- 236,243 ---- static void beginTimer(void){ if( enableTimer && getProcessTimesAddr ){ FILETIME ftCreation, ftExit; ! getProcessTimesAddr(hProcess,&ftCreation,&ftExit, ! &ftKernelBegin,&ftUserBegin); ftWallBegin = timeOfDay(); } } *************** *** 227,233 **** if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); ! getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd); printf("Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), --- 256,262 ---- if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); ! getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); printf("Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), *************** *** 422,435 **** zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; ! #if defined(HAVE_READLINE) ! free(zPrior); ! zResult = readline(zPrompt); ! if( zResult && *zResult ) add_history(zResult); ! #else printf("%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); #endif } return zResult; --- 451,464 ---- zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; ! #if SHELL_USE_LOCAL_GETLINE printf("%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); + #else + free(zPrior); + zResult = shell_readline(zPrompt); + if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; *************** *** 457,462 **** --- 486,492 ---- int echoOn; /* True to echo input commands */ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ int statsOn; /* True to display memory stats before each finalize */ + int scanstatsOn; /* True to display scan stats before each finalize */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ *************** *** 467,477 **** int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ char *zDestTable; /* Name of destination table when MODE_Insert */ ! char separator[20]; /* Separator character for MODE_List */ ! char newline[20]; /* Record separator in MODE_Csv */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ ! char nullvalue[20]; /* The text to print when a NULL comes back from ** the database */ SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ --- 497,507 ---- int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ char *zDestTable; /* Name of destination table when MODE_Insert */ ! char colSeparator[20]; /* Column separator character for several modes */ ! char rowSeparator[20]; /* Row separator character for MODE_Ascii */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ ! char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ *************** *** 504,509 **** --- 534,540 ---- #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 7 /* Quote strings, numbers are plain */ #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ + #define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ static const char *modeDescr[] = { "line", *************** *** 515,523 **** --- 546,568 ---- "tcl", "csv", "explain", + "ascii", }; /* + ** These are the column/row/line separators used by the various + ** import/export modes. + */ + #define SEP_Column "|" + #define SEP_Row "\n" + #define SEP_Tab "\t" + #define SEP_Space " " + #define SEP_Comma "," + #define SEP_CrLf "\r\n" + #define SEP_Unit "\x1F" + #define SEP_Record "\x1E" + + /* ** Number of elements in an array */ #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) *************** *** 673,694 **** }; /* ! ** Output a single term of CSV. Actually, p->separator is used for ! ** the separator, which may or may not be a comma. p->nullvalue is ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ ! fprintf(out,"%s",p->nullvalue); }else{ int i; ! int nSep = strlen30(p->separator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] ! || (z[i]==p->separator[0] && ! (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ i = 0; break; } --- 718,739 ---- }; /* ! ** Output a single term of CSV. Actually, p->colSeparator is used for ! ** the separator, which may or may not be a comma. p->nullValue is ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ ! fprintf(out,"%s",p->nullValue); }else{ int i; ! int nSep = strlen30(p->colSeparator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] ! || (z[i]==p->colSeparator[0] && ! (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ i = 0; break; } *************** *** 705,711 **** } } if( bSep ){ ! fprintf(p->out, "%s", p->separator); } } --- 750,756 ---- } } if( bSep ){ ! fprintf(p->out, "%s", p->colSeparator); } } *************** *** 725,731 **** ** This is the callback routine that the shell ** invokes for each row of a query result. */ ! static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){ int i; ShellState *p = (ShellState*)pArg; --- 770,782 ---- ** This is the callback routine that the shell ** invokes for each row of a query result. */ ! static int shell_callback( ! void *pArg, ! int nArg, /* Number of result columns */ ! char **azArg, /* Text of each result column */ ! char **azCol, /* Column names */ ! int *aiType /* Column types */ ! ){ int i; ShellState *p = (ShellState*)pArg; *************** *** 737,746 **** int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } ! if( p->cnt++>0 ) fprintf(p->out,"\n"); for(i=0; iout,"%*s = %s\n", w, azCol[i], ! azArg[i] ? azArg[i] : p->nullvalue); } break; } --- 788,797 ---- int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } ! if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); for(i=0; iout,"%*s = %s%s", w, azCol[i], ! azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } *************** *** 757,763 **** if( w==0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; ! n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); if( wactualWidth) ){ --- 808,814 ---- if( w==0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; ! n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); if( wactualWidth) ){ *************** *** 765,773 **** } if( p->showHeader ){ if( w<0 ){ ! fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); }else{ ! fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); } } } --- 816,826 ---- } if( p->showHeader ){ if( w<0 ){ ! fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], ! i==nArg-1 ? p->rowSeparator : " "); }else{ ! fprintf(p->out,"%-*.*s%s",w,w,azCol[i], ! i==nArg-1 ? p->rowSeparator : " "); } } } *************** *** 782,788 **** } fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" "----------------------------------------------------------", ! i==nArg-1 ? "\n": " "); } } } --- 835,841 ---- } fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" "----------------------------------------------------------", ! i==nArg-1 ? p->rowSeparator : " "); } } } *************** *** 805,814 **** } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, ! azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); }else{ fprintf(p->out,"%-*.*s%s",w,w, ! azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); } } break; --- 858,869 ---- } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, ! azArg[i] ? azArg[i] : p->nullValue, ! i==nArg-1 ? p->rowSeparator : " "); }else{ fprintf(p->out,"%-*.*s%s",w,w, ! azArg[i] ? azArg[i] : p->nullValue, ! i==nArg-1 ? p->rowSeparator : " "); } } break; *************** *** 817,836 **** case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); } } if( azArg==0 ) break; for(i=0; inullvalue; fprintf(p->out, "%s", z); if( iout, "%s", p->separator); }else if( p->mode==MODE_Semi ){ ! fprintf(p->out, ";\n"); }else{ ! fprintf(p->out, "\n"); } } break; --- 872,892 ---- case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s%s",azCol[i], ! i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; inullValue; fprintf(p->out, "%s", z); if( iout, "%s", p->colSeparator); }else if( p->mode==MODE_Semi ){ ! fprintf(p->out, ";%s", p->rowSeparator); }else{ ! fprintf(p->out, "%s", p->rowSeparator); } } break; *************** *** 849,855 **** fprintf(p->out,""); for(i=0; iout,""); ! output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); fprintf(p->out,"\n"); } fprintf(p->out,"\n"); --- 905,911 ---- fprintf(p->out,""); for(i=0; iout,""); ! output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); fprintf(p->out,"\n"); } fprintf(p->out,"\n"); *************** *** 859,874 **** if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); ! if(iout, "%s", p->separator); } ! fprintf(p->out,"\n"); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullvalue); ! if(iout, "%s", p->separator); } ! fprintf(p->out,"\n"); break; } case MODE_Csv: { --- 915,930 ---- if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); ! if(iout, "%s", p->colSeparator); } ! fprintf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue); ! if(iout, "%s", p->colSeparator); } ! fprintf(p->out, "%s", p->rowSeparator); break; } case MODE_Csv: { *************** *** 880,892 **** for(i=0; iout,"%s",p->newline); } ! if( azArg>0 ){ for(i=0; iout,"%s",p->newline); } #if defined(WIN32) || defined(_WIN32) fflush(p->out); --- 936,948 ---- for(i=0; iout, "%s", p->rowSeparator); } ! if( nArg>0 ){ for(i=0; iout, "%s", p->rowSeparator); } #if defined(WIN32) || defined(_WIN32) fflush(p->out); *************** *** 923,928 **** --- 979,1000 ---- fprintf(p->out,");\n"); break; } + case MODE_Ascii: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); + fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); + } + fprintf(p->out, "%s", p->rowSeparator); + } + if( azArg==0 ) break; + for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); + fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + } + fprintf(p->out, "%s", p->rowSeparator); + break; + } } return 0; } *************** *** 1104,1160 **** iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr); if( pArg->shellFlgs & SHFLG_Pagecache ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr); } iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); if( pArg->shellFlgs & SHFLG_Scratch ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr); } iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Largest Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", iHiwtr); #ifdef YYTRACKMAXSTACKDEPTH iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr); #endif } if( pArg && pArg->out && db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr); ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); fprintf(pArg->out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; --- 1176,1252 ---- iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, ! "Memory Used: %d (max %d) bytes\n", ! iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", ! iCur, iHiwtr); if( pArg->shellFlgs & SHFLG_Pagecache ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, ! "Number of Pcache Pages Used: %d (max %d) pages\n", ! iCur, iHiwtr); } iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, ! "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", ! iCur, iHiwtr); if( pArg->shellFlgs & SHFLG_Scratch ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", ! iCur, iHiwtr); } iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, ! "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", ! iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Largest Allocation: %d bytes\n", ! iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", ! iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", ! iHiwtr); #ifdef YYTRACKMAXSTACKDEPTH iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", ! iCur, iHiwtr); #endif } if( pArg && pArg->out && db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, ! &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", ! iCur, iHiwtr); ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, ! &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr); ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, ! &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); ! sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, ! &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Pager Heap Usage: %d bytes\n",iCur); ! iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); fprintf(pArg->out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; *************** *** 1165,1182 **** fprintf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); } if( pArg && pArg->out && db && pArg->pStmt ){ ! iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); fprintf(pArg->out, "Sort Operations: %d\n", iCur); ! iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); --- 1257,1275 ---- fprintf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Schema Heap Usage: %d bytes\n",iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); ! fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n",iCur); } if( pArg && pArg->out && db && pArg->pStmt ){ ! iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, ! bReset); fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); fprintf(pArg->out, "Sort Operations: %d\n", iCur); ! iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); *************** *** 1186,1191 **** --- 1279,1329 ---- } /* + ** Display scan stats. + */ + static void display_scanstats( + sqlite3 *db, /* Database to query */ + ShellState *pArg /* Pointer to ShellState */ + ){ + #ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int i, k, n, mx; + fprintf(pArg->out, "-------- scanstats --------\n"); + mx = 0; + for(k=0; k<=mx; k++){ + double rEstLoop = 1.0; + for(i=n=0; 1; i++){ + sqlite3_stmt *p = pArg->pStmt; + sqlite3_int64 nLoop, nVisit; + double rEst; + int iSid; + const char *zExplain; + if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ + break; + } + sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid); + if( iSid>mx ) mx = iSid; + if( iSid!=k ) continue; + if( n==0 ){ + rEstLoop = (double)nLoop; + if( k>0 ) fprintf(pArg->out, "-------- subquery %d -------\n", k); + } + n++; + sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); + sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); + sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); + fprintf(pArg->out, "Loop %2d: %s\n", n, zExplain); + rEstLoop *= rEst; + fprintf(pArg->out, + " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", + nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst + ); + } + } + fprintf(pArg->out, "---------------------------\n"); + #endif + } + + /* ** Parameter azArray points to a zero-terminated array of strings. zStr ** points to a single nul-terminated string. Return non-zero if zStr ** is equal, according to strcmp(), to any of the strings in the array. *************** *** 1226,1232 **** const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", "NextIfOpen", "PrevIfOpen", 0 }; ! const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this --- 1364,1371 ---- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", "NextIfOpen", "PrevIfOpen", 0 }; ! const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", ! "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this *************** *** 1339,1345 **** /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP ){ sqlite3_stmt *pExplain; ! char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", sqlite3_sql(pStmt)); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ --- 1478,1485 ---- /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP ){ sqlite3_stmt *pExplain; ! char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", ! sqlite3_sql(pStmt)); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ *************** *** 1423,1428 **** --- 1563,1573 ---- display_stats(db, pArg, 0); } + /* print loop-counters if required */ + if( pArg && pArg->scanstatsOn ){ + display_scanstats(db, pArg); + } + /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ *************** *** 1615,1626 **** #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" ! " list Values delimited by .separator string\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" --- 1760,1772 ---- #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" + " ascii Columns/rows delimited by 0x1F and 0x1E\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML
code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" ! " list Values delimited by .separator strings\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" *************** *** 1633,1643 **** ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".save FILE Write in-memory database into FILE\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" ! ".separator STRING ?NL? Change separator used by output mode and .import\n" ! " NL is the end-of-line mark for CSV\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats on|off Turn stats on or off\n" --- 1779,1790 ---- ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".save FILE Write in-memory database into FILE\n" + ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" ! ".separator COL ?ROW? Change the column separator and optionally the row\n" ! " separator for both the output mode and .import\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats on|off Turn stats on or off\n" *************** *** 1918,1927 **** } /* ! ** An object used to read a CSV file */ ! typedef struct CSVReader CSVReader; ! struct CSVReader { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ --- 2065,2074 ---- } /* ! ** An object used to read a CSV and other files for import. */ ! typedef struct ImportCtx ImportCtx; ! struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ *************** *** 1929,1939 **** int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int cTerm; /* Character that terminated the most recent field */ ! int cSeparator; /* The separator character. (Usually ",") */ }; /* Append a single byte to z[] */ ! static void csv_append_char(CSVReader *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc(p->z, p->nAlloc); --- 2076,2087 ---- int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int cTerm; /* Character that terminated the most recent field */ ! int cColSep; /* The column separator character. (Usually ",") */ ! int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ ! static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc(p->z, p->nAlloc); *************** *** 1951,1965 **** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc(). ! ** + Use p->cSep as the separator. The default is ",". ** + Keep track of the line number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ ! static char *csv_read_one_field(CSVReader *p){ ! int c, pc, ppc; ! int cSep = p->cSeparator; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ --- 2099,2115 ---- ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc(). ! ** + Use p->cSep as the column separator. The default is ",". ! ** + Use p->rSep as the row separator. The default is "\n". ** + Keep track of the line number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ ! static char *csv_read_one_field(ImportCtx *p){ ! int c; ! int cSep = p->cColSep; ! int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ *************** *** 1967,1978 **** return 0; } if( c=='"' ){ int startLine = p->nLine; int cQuote = c; pc = ppc = 0; while( 1 ){ c = fgetc(p->in); ! if( c=='\n' ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ pc = 0; --- 2117,2129 ---- return 0; } if( c=='"' ){ + int pc, ppc; int startLine = p->nLine; int cQuote = c; pc = ppc = 0; while( 1 ){ c = fgetc(p->in); ! if( c==rSep ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ pc = 0; *************** *** 1980,1987 **** } } if( (c==cSep && pc==cQuote) ! || (c=='\n' && pc==cQuote) ! || (c=='\n' && pc=='\r' && ppc==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); --- 2131,2138 ---- } } if( (c==cSep && pc==cQuote) ! || (c==rSep && pc==cQuote) ! || (c==rSep && pc=='\r' && ppc==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); *************** *** 1995,2013 **** if( c==EOF ){ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); ! p->cTerm = EOF; break; } ! csv_append_char(p, c); ppc = pc; pc = c; } }else{ ! while( c!=EOF && c!=cSep && c!='\n' ){ ! csv_append_char(p, c); c = fgetc(p->in); } ! if( c=='\n' ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } --- 2146,2164 ---- if( c==EOF ){ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); ! p->cTerm = c; break; } ! import_append_char(p, c); ppc = pc; pc = c; } }else{ ! while( c!=EOF && c!=cSep && c!=rSep ){ ! import_append_char(p, c); c = fgetc(p->in); } ! if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } *************** *** 2017,2022 **** --- 2168,2207 ---- return p->z; } + /* Read a single field of ASCII delimited text. + ** + ** + Input comes from p->in. + ** + Store results in p->z of length p->n. Space to hold p->z comes + ** from sqlite3_malloc(). + ** + Use p->cSep as the column separator. The default is "\x1F". + ** + Use p->rSep as the row separator. The default is "\x1E". + ** + Keep track of the row number in p->nLine. + ** + Store the character that terminates the field in p->cTerm. Store + ** EOF on end-of-file. + ** + Report syntax errors on stderr + */ + static char *ascii_read_one_field(ImportCtx *p){ + int c; + int cSep = p->cColSep; + int rSep = p->cRowSep; + p->n = 0; + c = fgetc(p->in); + if( c==EOF || seenInterrupt ){ + p->cTerm = EOF; + return 0; + } + while( c!=EOF && c!=cSep && c!=rSep ){ + import_append_char(p, c); + c = fgetc(p->in); + } + if( c==rSep ){ + p->nLine++; + } + p->cTerm = c; + if( p->z ) p->z[p->n] = 0; + return p->z; + } + /* ** Try to transfer data for table zTable. If an error is seen while ** moving forward, try to go backwards. The backwards movement won't *************** *** 2571,2579 **** int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ ! int nSep; /* Number of bytes in p->separator[] */ char *zSql; /* An SQL statement */ ! CSVReader sCsv; /* Reader context */ int (*xCloser)(FILE*); /* Procedure to close th3 connection */ if( nArg!=3 ){ --- 2756,2765 ---- int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ ! int nSep; /* Number of bytes in p->colSeparator[] */ char *zSql; /* An SQL statement */ ! ImportCtx sCtx; /* Reader context */ ! char *(*xRead)(ImportCtx*); /* Procedure to read one value */ int (*xCloser)(FILE*); /* Procedure to close th3 connection */ if( nArg!=3 ){ *************** *** 2583,2637 **** zFile = azArg[1]; zTable = azArg[2]; seenInterrupt = 0; ! memset(&sCsv, 0, sizeof(sCsv)); open_db(p, 0); ! nSep = strlen30(p->separator); if( nSep==0 ){ ! fprintf(stderr, "Error: non-null separator required for import\n"); return 1; } if( nSep>1 ){ ! fprintf(stderr, "Error: multi-character separators not allowed" " for import\n"); return 1; } ! sCsv.zFile = zFile; ! sCsv.nLine = 1; ! if( sCsv.zFile[0]=='|' ){ ! sCsv.in = popen(sCsv.zFile+1, "r"); ! sCsv.zFile = ""; xCloser = pclose; }else{ ! sCsv.in = fopen(sCsv.zFile, "rb"); xCloser = fclose; } ! if( sCsv.in==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } ! sCsv.cSeparator = p->separator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); ! xCloser(sCsv.in); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); ! csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; ! while( csv_read_one_field(&sCsv) ){ ! zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); cSep = ','; ! if( sCsv.cTerm!=sCsv.cSeparator ) break; } if( cSep=='(' ){ sqlite3_free(zCreate); ! sqlite3_free(sCsv.z); ! xCloser(sCsv.in); ! fprintf(stderr,"%s: empty file\n", sCsv.zFile); return 1; } zCreate = sqlite3_mprintf("%z\n)", zCreate); --- 2769,2847 ---- zFile = azArg[1]; zTable = azArg[2]; seenInterrupt = 0; ! memset(&sCtx, 0, sizeof(sCtx)); open_db(p, 0); ! nSep = strlen30(p->colSeparator); if( nSep==0 ){ ! fprintf(stderr, "Error: non-null column separator required for import\n"); return 1; } if( nSep>1 ){ ! fprintf(stderr, "Error: multi-character column separators not allowed" " for import\n"); return 1; } ! nSep = strlen30(p->rowSeparator); ! if( nSep==0 ){ ! fprintf(stderr, "Error: non-null row separator required for import\n"); ! return 1; ! } ! if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ ! /* When importing CSV (only), if the row separator is set to the ! ** default output row separator, change it to the default input ! ** row separator. This avoids having to maintain different input ! ** and output row separators. */ ! sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); ! nSep = strlen30(p->rowSeparator); ! } ! if( nSep>1 ){ ! fprintf(stderr, "Error: multi-character row separators not allowed" ! " for import\n"); ! return 1; ! } ! sCtx.zFile = zFile; ! sCtx.nLine = 1; ! if( sCtx.zFile[0]=='|' ){ ! sCtx.in = popen(sCtx.zFile+1, "r"); ! sCtx.zFile = ""; xCloser = pclose; }else{ ! sCtx.in = fopen(sCtx.zFile, "rb"); xCloser = fclose; } ! if( p->mode==MODE_Ascii ){ ! xRead = ascii_read_one_field; ! }else{ ! xRead = csv_read_one_field; ! } ! if( sCtx.in==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } ! sCtx.cColSep = p->colSeparator[0]; ! sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); ! xCloser(sCtx.in); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); ! import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; ! while( xRead(&sCtx) ){ ! zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); cSep = ','; ! if( sCtx.cTerm!=sCtx.cColSep ) break; } if( cSep=='(' ){ sqlite3_free(zCreate); ! sqlite3_free(sCtx.z); ! xCloser(sCtx.in); ! fprintf(stderr,"%s: empty file\n", sCtx.zFile); return 1; } zCreate = sqlite3_mprintf("%z\n)", zCreate); *************** *** 2640,2647 **** if( rc ){ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(db)); ! sqlite3_free(sCsv.z); ! xCloser(sCsv.in); return 1; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); --- 2850,2857 ---- if( rc ){ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(db)); ! sqlite3_free(sCtx.z); ! xCloser(sCtx.in); return 1; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); *************** *** 2650,2656 **** if( rc ){ if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); ! xCloser(sCsv.in); return 1; } nCol = sqlite3_column_count(pStmt); --- 2860,2866 ---- if( rc ){ if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); ! xCloser(sCtx.in); return 1; } nCol = sqlite3_column_count(pStmt); *************** *** 2660,2666 **** zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); ! xCloser(sCsv.in); return 1; } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); --- 2870,2876 ---- zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); ! xCloser(sCtx.in); return 1; } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); *************** *** 2676,2721 **** if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); if (pStmt) sqlite3_finalize(pStmt); ! xCloser(sCsv.in); return 1; } needCommit = sqlite3_get_autocommit(db); if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); do{ ! int startLine = sCsv.nLine; for(i=0; i=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ ! fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, sqlite3_errmsg(db)); } } ! }while( sCsv.cTerm!=EOF ); ! xCloser(sCsv.in); ! sqlite3_free(sCsv.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); }else --- 2886,2941 ---- if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); if (pStmt) sqlite3_finalize(pStmt); ! xCloser(sCtx.in); return 1; } needCommit = sqlite3_get_autocommit(db); if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); do{ ! int startLine = sCtx.nLine; for(i=0; imode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); ! if( i=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ ! fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(db)); } } ! }while( sCtx.cTerm!=EOF ); ! xCloser(sCtx.in); ! sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); }else *************** *** 2833,2860 **** p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; ! sqlite3_snprintf(sizeof(p->separator), p->separator, " "); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; ! sqlite3_snprintf(sizeof(p->separator), p->separator, ","); ! sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; ! sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); }else { fprintf(stderr,"Error: mode should be one of: " ! "column csv html insert line list tabs tcl\n"); rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ ! sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, ! "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); }else{ fprintf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; --- 3053,3084 ---- p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; ! sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; ! sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); ! sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; ! sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); + }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ + p->mode = MODE_Ascii; + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else { fprintf(stderr,"Error: mode should be one of: " ! "ascii column csv html insert line list tabs tcl\n"); rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ ! sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, ! "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ fprintf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; *************** *** 3014,3019 **** --- 3238,3256 ---- sqlite3_close(pSrc); }else + + if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ + if( nArg==2 ){ + p->scanstatsOn = booleanValue(azArg[1]); + #ifndef SQLITE_ENABLE_STMT_SCANSTATUS + fprintf(stderr, "Warning: .scanstats not available in this build.\n"); + #endif + }else{ + fprintf(stderr, "Usage: .scanstats on|off\n"); + rc = 1; + } + }else + if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ ShellState data; char *zErrMsg = 0; *************** *** 3126,3139 **** if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ ! fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); rc = 1; } if( nArg>=2 ){ ! sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); } if( nArg>=3 ){ ! sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); } }else --- 3363,3378 ---- if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ ! fprintf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ ! sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, ! "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ ! sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, ! "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else *************** *** 3164,3186 **** rc = 1; goto meta_command_exit; } ! fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); ! fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); ! fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); ! fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); ! fprintf(p->out,"%9.9s: ", "nullvalue"); ! output_c_string(p->out, p->nullvalue); fprintf(p->out, "\n"); ! fprintf(p->out,"%9.9s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); ! fprintf(p->out,"%9.9s: ", "separator"); ! output_c_string(p->out, p->separator); ! fprintf(p->out," "); ! output_c_string(p->out, p->newline); fprintf(p->out, "\n"); ! fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); ! fprintf(p->out,"%9.9s: ","width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { fprintf(p->out,"%d ",p->colWidth[i]); } --- 3403,3426 ---- rc = 1; goto meta_command_exit; } ! fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); ! fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); ! fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); ! fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); ! fprintf(p->out,"%12.12s: ", "nullvalue"); ! output_c_string(p->out, p->nullValue); fprintf(p->out, "\n"); ! fprintf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); ! fprintf(p->out,"%12.12s: ", "colseparator"); ! output_c_string(p->out, p->colSeparator); fprintf(p->out, "\n"); ! fprintf(p->out,"%12.12s: ", "rowseparator"); ! output_c_string(p->out, p->rowSeparator); ! fprintf(p->out, "\n"); ! fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off"); ! fprintf(p->out,"%12.12s: ","width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { fprintf(p->out,"%d ",p->colWidth[i]); } *************** *** 3271,3277 **** for(i=0; iout, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); } fprintf(p->out, "\n"); } --- 3511,3517 ---- for(i=0; iout, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } fprintf(p->out, "\n"); } *************** *** 3741,3747 **** static char *home_dir = NULL; if( home_dir ) return home_dir; ! #if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) { struct passwd *pwent; uid_t uid = getuid(); --- 3981,3988 ---- static char *home_dir = NULL; if( home_dir ) return home_dir; ! #if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \ ! && !defined(__RTP__) && !defined(_WRS_KERNEL) { struct passwd *pwent; uid_t uid = getuid(); *************** *** 3840,3845 **** --- 4081,4087 ---- ** Show available command line options */ static const char zOptions[] = + " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" *************** *** 3861,3871 **** #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif ! " -newline SEP set newline character(s) for CSV\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" ! " -separator SEP set output field separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" --- 4103,4113 ---- #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif ! " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" ! " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" *************** *** 3892,3899 **** static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; ! memcpy(data->separator,"|", 2); ! memcpy(data->newline,"\r\n", 3); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); --- 4134,4141 ---- static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; ! memcpy(data->colSeparator,SEP_Column, 2); ! memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); *************** *** 3940,3949 **** char *zErrMsg = 0; ShellState data; const char *zInitFile = 0; - char *zFirstCmd = 0; int i; int rc = 0; int warnInmemoryDb = 0; #if USE_SYSTEM_SQLITE+0!=1 if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){ --- 4182,4193 ---- char *zErrMsg = 0; ShellState data; const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; + int readStdin = 1; + int nCmd = 0; + char **azCmd = 0; #if USE_SYSTEM_SQLITE+0!=1 if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){ *************** *** 3963,3968 **** --- 4207,4224 ---- signal(SIGINT, interrupt_handler); #endif + #ifdef SQLITE_SHELL_DBNAME_PROC + { + /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name + ** of a C-function that will provide the name of the database file. Use + ** this compile-time option to embed this shell program in larger + ** applications. */ + extern void SQLITE_SHELL_DBNAME_PROC(const char**); + SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename); + warnInmemoryDb = 0; + } + #endif + /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, *************** *** 3974,3988 **** if( z[0]!='-' ){ if( data.zDbFilename==0 ){ data.zDbFilename = z; ! continue; } - if( zFirstCmd==0 ){ - zFirstCmd = z; - continue; - } - fprintf(stderr,"%s: Error: too many options: \"%s\"\n", Argv0, argv[i]); - fprintf(stderr,"Use -help for a list of options.\n"); - return 1; } if( z[1]=='-' ) z++; if( strcmp(z,"-separator")==0 --- 4230,4247 ---- if( z[0]!='-' ){ if( data.zDbFilename==0 ){ data.zDbFilename = z; ! }else{ ! /* Excesss arguments are interpreted as SQL (or dot-commands) and ! ** mean that nothing is read from stdin */ ! readStdin = 0; ! nCmd++; ! azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd); ! if( azCmd==0 ){ ! fprintf(stderr, "out of memory\n"); ! exit(1); ! } ! azCmd[nCmd-1] = z; } } if( z[1]=='-' ) z++; if( strcmp(z,"-separator")==0 *************** *** 4073,4083 **** fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; #endif - #ifdef SQLITE_SHELL_DBNAME_PROC - { extern void SQLITE_SHELL_DBNAME_PROC(const char**); - SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename); - warnInmemoryDb = 0; } - #endif } data.out = stdout; --- 4332,4337 ---- *************** *** 4120,4134 **** data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; ! memcpy(data.separator,",",2); }else if( strcmp(z,"-separator")==0 ){ ! sqlite3_snprintf(sizeof(data.separator), data.separator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-newline")==0 ){ ! sqlite3_snprintf(sizeof(data.newline), data.newline, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ ! sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; --- 4374,4394 ---- data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; ! memcpy(data.colSeparator,",",2); ! }else if( strcmp(z,"-ascii")==0 ){ ! data.mode = MODE_Ascii; ! sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, ! SEP_Unit); ! sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, ! SEP_Record); }else if( strcmp(z,"-separator")==0 ){ ! sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-newline")==0 ){ ! sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ ! sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; *************** *** 4140,4145 **** --- 4400,4407 ---- data.autoEQP = 1; }else if( strcmp(z,"-stats")==0 ){ data.statsOn = 1; + }else if( strcmp(z,"-scanstats")==0 ){ + data.scanstatsOn = 1; }else if( strcmp(z,"-bail")==0 ){ bail_on_error = 1; }else if( strcmp(z,"-version")==0 ){ *************** *** 4172,4177 **** --- 4434,4443 ---- }else if( strcmp(z,"-help")==0 ){ usage(1); }else if( strcmp(z,"-cmd")==0 ){ + /* Run commands that follow -cmd first and separately from commands + ** that simply appear on the command-line. This seems goofy. It would + ** be better if all commands ran in the order that they appear. But + ** we retain the goofy behavior for historical compatibility. */ if( i==argc-1 ) break; z = cmdline_option_value(argc,argv,++i); if( z[0]=='.' ){ *************** *** 4195,4217 **** } } ! if( zFirstCmd ){ ! /* Run just the command that follows the database name */ ! if( zFirstCmd[0]=='.' ){ ! rc = do_meta_command(zFirstCmd, &data); ! if( rc==2 ) rc = 0; ! }else{ ! open_db(&data, 0); ! rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg); ! if( zErrMsg!=0 ){ ! fprintf(stderr,"Error: %s\n", zErrMsg); ! return rc!=0 ? rc : 1; ! }else if( rc!=0 ){ ! fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd); ! return rc; } } }else{ /* Run commands received from standard input */ --- 4461,4488 ---- } } ! if( !readStdin ){ ! /* Run all arguments that do not begin with '-' as if they were separate ! ** command-line inputs, except for the argToSkip argument which contains ! ** the database filename. */ ! for(i=0; i