version 1.10, 2015/03/16 00:09:33 |
version 1.11, 2015/04/04 23:26:54 |
|
|
#endif |
#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. |
** Enable large-file support for fopen() and friends on unix. |
*/ |
*/ |
#ifndef SQLITE_DISABLE_LFS |
#ifndef SQLITE_DISABLE_LFS |
|
|
# include <sys/types.h> |
# include <sys/types.h> |
#endif |
#endif |
|
|
#if defined(HAVE_READLINE) && HAVE_READLINE!=0 |
#if HAVE_READLINE |
# include <readline/readline.h> |
# include <readline/readline.h> |
# include <readline/history.h> |
# include <readline/history.h> |
#else |
|
# undef HAVE_READLINE |
|
#endif |
#endif |
#if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE) |
|
# define HAVE_READLINE 1 |
#if HAVE_EDITLINE |
# include <editline/readline.h> |
# include <editline/readline.h> |
#endif |
#endif |
#if !defined(HAVE_READLINE) |
|
# define add_history(X) |
#if HAVE_EDITLINE || HAVE_READLINE |
# define read_history(X) |
|
# define write_history(X) |
# define shell_add_history(X) add_history(X) |
# define stifle_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 |
#endif |
|
|
|
|
#if defined(_WIN32) || defined(WIN32) |
#if defined(_WIN32) || defined(WIN32) |
# include <io.h> |
# include <io.h> |
# include <fcntl.h> |
# include <fcntl.h> |
|
|
static FILETIME ftKernelBegin; |
static FILETIME ftKernelBegin; |
static FILETIME ftUserBegin; |
static FILETIME ftUserBegin; |
static sqlite3_int64 ftWallBegin; |
static sqlite3_int64 ftWallBegin; |
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); |
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, |
|
LPFILETIME, LPFILETIME); |
static GETPROCTIMES getProcessTimesAddr = NULL; |
static GETPROCTIMES getProcessTimesAddr = NULL; |
|
|
/* |
/* |
|
|
if( getProcessTimesAddr ){ |
if( getProcessTimesAddr ){ |
return 1; |
return 1; |
} else { |
} else { |
/* GetProcessTimes() isn't supported in WIN95 and some other Windows versions. |
/* GetProcessTimes() isn't supported in WIN95 and some other Windows |
** See if the version we are running on has it, and if it does, save off |
** versions. See if the version we are running on has it, and if it |
** a pointer to it and the current process handle. |
** does, save off a pointer to it and the current process handle. |
*/ |
*/ |
hProcess = GetCurrentProcess(); |
hProcess = GetCurrentProcess(); |
if( hProcess ){ |
if( hProcess ){ |
HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); |
HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); |
if( NULL != hinstLib ){ |
if( NULL != hinstLib ){ |
getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); |
getProcessTimesAddr = |
|
(GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); |
if( NULL != getProcessTimesAddr ){ |
if( NULL != getProcessTimesAddr ){ |
return 1; |
return 1; |
} |
} |
|
|
static void beginTimer(void){ |
static void beginTimer(void){ |
if( enableTimer && getProcessTimesAddr ){ |
if( enableTimer && getProcessTimesAddr ){ |
FILETIME ftCreation, ftExit; |
FILETIME ftCreation, ftExit; |
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin); |
getProcessTimesAddr(hProcess,&ftCreation,&ftExit, |
|
&ftKernelBegin,&ftUserBegin); |
ftWallBegin = timeOfDay(); |
ftWallBegin = timeOfDay(); |
} |
} |
} |
} |
|
|
if( enableTimer && getProcessTimesAddr){ |
if( enableTimer && getProcessTimesAddr){ |
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
sqlite3_int64 ftWallEnd = timeOfDay(); |
sqlite3_int64 ftWallEnd = timeOfDay(); |
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd); |
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
printf("Run Time: real %.3f user %f sys %f\n", |
printf("Run Time: real %.3f user %f sys %f\n", |
(ftWallEnd - ftWallBegin)*0.001, |
(ftWallEnd - ftWallBegin)*0.001, |
timeDiff(&ftUserBegin, &ftUserEnd), |
timeDiff(&ftUserBegin, &ftUserEnd), |
|
|
zResult = local_getline(zPrior, in); |
zResult = local_getline(zPrior, in); |
}else{ |
}else{ |
zPrompt = isContinuation ? continuePrompt : mainPrompt; |
zPrompt = isContinuation ? continuePrompt : mainPrompt; |
#if defined(HAVE_READLINE) |
#if SHELL_USE_LOCAL_GETLINE |
free(zPrior); |
|
zResult = readline(zPrompt); |
|
if( zResult && *zResult ) add_history(zResult); |
|
#else |
|
printf("%s", zPrompt); |
printf("%s", zPrompt); |
fflush(stdout); |
fflush(stdout); |
zResult = local_getline(zPrior, stdin); |
zResult = local_getline(zPrior, stdin); |
|
#else |
|
free(zPrior); |
|
zResult = shell_readline(zPrompt); |
|
if( zResult && *zResult ) shell_add_history(zResult); |
#endif |
#endif |
} |
} |
return zResult; |
return zResult; |
|
|
int echoOn; /* True to echo input commands */ |
int echoOn; /* True to echo input commands */ |
int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ |
int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ |
int statsOn; /* True to display memory stats before each finalize */ |
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 outCount; /* Revert to stdout when reaching zero */ |
int cnt; /* Number of records displayed so far */ |
int cnt; /* Number of records displayed so far */ |
FILE *out; /* Write results here */ |
FILE *out; /* Write results here */ |
|
|
int showHeader; /* True to show column names in List or Column mode */ |
int showHeader; /* True to show column names in List or Column mode */ |
unsigned shellFlgs; /* Various flags */ |
unsigned shellFlgs; /* Various flags */ |
char *zDestTable; /* Name of destination table when MODE_Insert */ |
char *zDestTable; /* Name of destination table when MODE_Insert */ |
char separator[20]; /* Separator character for MODE_List */ |
char colSeparator[20]; /* Column separator character for several modes */ |
char newline[20]; /* Record separator in MODE_Csv */ |
char rowSeparator[20]; /* Row separator character for MODE_Ascii */ |
int colWidth[100]; /* Requested width of each column when in column mode*/ |
int colWidth[100]; /* Requested width of each column when in column mode*/ |
int actualWidth[100]; /* Actual width of each column */ |
int actualWidth[100]; /* Actual width of each column */ |
char nullvalue[20]; /* The text to print when a NULL comes back from |
char nullValue[20]; /* The text to print when a NULL comes back from |
** the database */ |
** the database */ |
SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ |
SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ |
char outfile[FILENAME_MAX]; /* Filename for *out */ |
char outfile[FILENAME_MAX]; /* Filename for *out */ |
|
|
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ |
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ |
#define MODE_Csv 7 /* Quote strings, numbers are plain */ |
#define MODE_Csv 7 /* Quote strings, numbers are plain */ |
#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ |
#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[] = { |
static const char *modeDescr[] = { |
"line", |
"line", |
|
|
"tcl", |
"tcl", |
"csv", |
"csv", |
"explain", |
"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 |
** Number of elements in an array |
*/ |
*/ |
#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
|
|
}; |
}; |
|
|
/* |
/* |
** Output a single term of CSV. Actually, p->separator is used for |
** 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 separator, which may or may not be a comma. p->nullValue is |
** the null value. Strings are quoted if necessary. The separator |
** the null value. Strings are quoted if necessary. The separator |
** is only issued if bSep is true. |
** is only issued if bSep is true. |
*/ |
*/ |
static void output_csv(ShellState *p, const char *z, int bSep){ |
static void output_csv(ShellState *p, const char *z, int bSep){ |
FILE *out = p->out; |
FILE *out = p->out; |
if( z==0 ){ |
if( z==0 ){ |
fprintf(out,"%s",p->nullvalue); |
fprintf(out,"%s",p->nullValue); |
}else{ |
}else{ |
int i; |
int i; |
int nSep = strlen30(p->separator); |
int nSep = strlen30(p->colSeparator); |
for(i=0; z[i]; i++){ |
for(i=0; z[i]; i++){ |
if( needCsvQuote[((unsigned char*)z)[i]] |
if( needCsvQuote[((unsigned char*)z)[i]] |
|| (z[i]==p->separator[0] && |
|| (z[i]==p->colSeparator[0] && |
(nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ |
(nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ |
i = 0; |
i = 0; |
break; |
break; |
} |
} |
|
|
} |
} |
} |
} |
if( bSep ){ |
if( bSep ){ |
fprintf(p->out, "%s", p->separator); |
fprintf(p->out, "%s", p->colSeparator); |
} |
} |
} |
} |
|
|
|
|
** This is the callback routine that the shell |
** This is the callback routine that the shell |
** invokes for each row of a query result. |
** invokes for each row of a query result. |
*/ |
*/ |
static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){ |
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; |
int i; |
ShellState *p = (ShellState*)pArg; |
ShellState *p = (ShellState*)pArg; |
|
|
|
|
int len = strlen30(azCol[i] ? azCol[i] : ""); |
int len = strlen30(azCol[i] ? azCol[i] : ""); |
if( len>w ) w = len; |
if( len>w ) w = len; |
} |
} |
if( p->cnt++>0 ) fprintf(p->out,"\n"); |
if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
fprintf(p->out,"%*s = %s\n", w, azCol[i], |
fprintf(p->out,"%*s = %s%s", w, azCol[i], |
azArg[i] ? azArg[i] : p->nullvalue); |
azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
} |
} |
break; |
break; |
} |
} |
|
|
if( w==0 ){ |
if( w==0 ){ |
w = strlen30(azCol[i] ? azCol[i] : ""); |
w = strlen30(azCol[i] ? azCol[i] : ""); |
if( w<10 ) w = 10; |
if( w<10 ) w = 10; |
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); |
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); |
if( w<n ) w = n; |
if( w<n ) w = n; |
} |
} |
if( i<ArraySize(p->actualWidth) ){ |
if( i<ArraySize(p->actualWidth) ){ |
|
|
} |
} |
if( p->showHeader ){ |
if( p->showHeader ){ |
if( w<0 ){ |
if( w<0 ){ |
fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); |
fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], |
|
i==nArg-1 ? p->rowSeparator : " "); |
}else{ |
}else{ |
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); |
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], |
|
i==nArg-1 ? p->rowSeparator : " "); |
} |
} |
} |
} |
} |
} |
|
|
} |
} |
fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" |
fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" |
"----------------------------------------------------------", |
"----------------------------------------------------------", |
i==nArg-1 ? "\n": " "); |
i==nArg-1 ? p->rowSeparator : " "); |
} |
} |
} |
} |
} |
} |
|
|
} |
} |
if( w<0 ){ |
if( w<0 ){ |
fprintf(p->out,"%*.*s%s",-w,-w, |
fprintf(p->out,"%*.*s%s",-w,-w, |
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); |
azArg[i] ? azArg[i] : p->nullValue, |
|
i==nArg-1 ? p->rowSeparator : " "); |
}else{ |
}else{ |
fprintf(p->out,"%-*.*s%s",w,w, |
fprintf(p->out,"%-*.*s%s",w,w, |
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); |
azArg[i] ? azArg[i] : p->nullValue, |
|
i==nArg-1 ? p->rowSeparator : " "); |
} |
} |
} |
} |
break; |
break; |
|
|
case MODE_List: { |
case MODE_List: { |
if( p->cnt++==0 && p->showHeader ){ |
if( p->cnt++==0 && p->showHeader ){ |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); |
fprintf(p->out,"%s%s",azCol[i], |
|
i==nArg-1 ? p->rowSeparator : p->colSeparator); |
} |
} |
} |
} |
if( azArg==0 ) break; |
if( azArg==0 ) break; |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
char *z = azArg[i]; |
char *z = azArg[i]; |
if( z==0 ) z = p->nullvalue; |
if( z==0 ) z = p->nullValue; |
fprintf(p->out, "%s", z); |
fprintf(p->out, "%s", z); |
if( i<nArg-1 ){ |
if( i<nArg-1 ){ |
fprintf(p->out, "%s", p->separator); |
fprintf(p->out, "%s", p->colSeparator); |
}else if( p->mode==MODE_Semi ){ |
}else if( p->mode==MODE_Semi ){ |
fprintf(p->out, ";\n"); |
fprintf(p->out, ";%s", p->rowSeparator); |
}else{ |
}else{ |
fprintf(p->out, "\n"); |
fprintf(p->out, "%s", p->rowSeparator); |
} |
} |
} |
} |
break; |
break; |
|
|
fprintf(p->out,"<TR>"); |
fprintf(p->out,"<TR>"); |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
fprintf(p->out,"<TD>"); |
fprintf(p->out,"<TD>"); |
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); |
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
fprintf(p->out,"</TD>\n"); |
fprintf(p->out,"</TD>\n"); |
} |
} |
fprintf(p->out,"</TR>\n"); |
fprintf(p->out,"</TR>\n"); |
|
|
if( p->cnt++==0 && p->showHeader ){ |
if( p->cnt++==0 && p->showHeader ){ |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
output_c_string(p->out,azCol[i] ? azCol[i] : ""); |
output_c_string(p->out,azCol[i] ? azCol[i] : ""); |
if(i<nArg-1) fprintf(p->out, "%s", p->separator); |
if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); |
} |
} |
fprintf(p->out,"\n"); |
fprintf(p->out, "%s", p->rowSeparator); |
} |
} |
if( azArg==0 ) break; |
if( azArg==0 ) break; |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); |
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
if(i<nArg-1) fprintf(p->out, "%s", p->separator); |
if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); |
} |
} |
fprintf(p->out,"\n"); |
fprintf(p->out, "%s", p->rowSeparator); |
break; |
break; |
} |
} |
case MODE_Csv: { |
case MODE_Csv: { |
|
|
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
} |
} |
fprintf(p->out,"%s",p->newline); |
fprintf(p->out, "%s", p->rowSeparator); |
} |
} |
if( azArg>0 ){ |
if( nArg>0 ){ |
for(i=0; i<nArg; i++){ |
for(i=0; i<nArg; i++){ |
output_csv(p, azArg[i], i<nArg-1); |
output_csv(p, azArg[i], i<nArg-1); |
} |
} |
fprintf(p->out,"%s",p->newline); |
fprintf(p->out, "%s", p->rowSeparator); |
} |
} |
#if defined(WIN32) || defined(_WIN32) |
#if defined(WIN32) || defined(_WIN32) |
fflush(p->out); |
fflush(p->out); |
|
|
fprintf(p->out,");\n"); |
fprintf(p->out,");\n"); |
break; |
break; |
} |
} |
|
case MODE_Ascii: { |
|
if( p->cnt++==0 && p->showHeader ){ |
|
for(i=0; i<nArg; i++){ |
|
if( i>0 ) 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; i<nArg; i++){ |
|
if( i>0 ) 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; |
return 0; |
} |
} |
|
|
|
|
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr); |
fprintf(pArg->out, |
|
"Memory Used: %d (max %d) bytes\n", |
|
iCur, iHiwtr); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr); |
fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", |
|
iCur, iHiwtr); |
if( pArg->shellFlgs & SHFLG_Pagecache ){ |
if( pArg->shellFlgs & SHFLG_Pagecache ){ |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr); |
fprintf(pArg->out, |
|
"Number of Pcache Pages Used: %d (max %d) pages\n", |
|
iCur, iHiwtr); |
} |
} |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); |
fprintf(pArg->out, |
|
"Number of Pcache Overflow Bytes: %d (max %d) bytes\n", |
|
iCur, iHiwtr); |
if( pArg->shellFlgs & SHFLG_Scratch ){ |
if( pArg->shellFlgs & SHFLG_Scratch ){ |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr); |
fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", |
|
iCur, iHiwtr); |
} |
} |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); |
fprintf(pArg->out, |
|
"Number of Scratch Overflow Bytes: %d (max %d) bytes\n", |
|
iCur, iHiwtr); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Largest Allocation: %d bytes\n", iHiwtr); |
fprintf(pArg->out, "Largest Allocation: %d bytes\n", |
|
iHiwtr); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", iHiwtr); |
fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", |
|
iHiwtr); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", iHiwtr); |
fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", |
|
iHiwtr); |
#ifdef YYTRACKMAXSTACKDEPTH |
#ifdef YYTRACKMAXSTACKDEPTH |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset); |
sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr); |
fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", |
|
iCur, iHiwtr); |
#endif |
#endif |
} |
} |
|
|
if( pArg && pArg->out && db ){ |
if( pArg && pArg->out && db ){ |
if( pArg->shellFlgs & SHFLG_Lookaside ){ |
if( pArg->shellFlgs & SHFLG_Lookaside ){ |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, |
fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); |
&iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &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); |
fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr); |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, |
|
&iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); |
fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, |
|
&iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); |
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); |
} |
} |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; |
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n",iCur); |
|
iHiwtr = iCur = -1; |
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); |
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); |
fprintf(pArg->out, "Page cache hits: %d\n", iCur); |
fprintf(pArg->out, "Page cache hits: %d\n", iCur); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
|
|
fprintf(pArg->out, "Page cache writes: %d\n", iCur); |
fprintf(pArg->out, "Page cache writes: %d\n", iCur); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); |
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n",iCur); |
iHiwtr = iCur = -1; |
iHiwtr = iCur = -1; |
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); |
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); |
fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); |
fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n",iCur); |
} |
} |
|
|
if( pArg && pArg->out && db && pArg->pStmt ){ |
if( pArg && pArg->out && db && pArg->pStmt ){ |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, |
|
bReset); |
fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); |
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
|
|
} |
} |
|
|
/* |
/* |
|
** 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 |
** Parameter azArray points to a zero-terminated array of strings. zStr |
** points to a single nul-terminated string. Return non-zero if 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. |
** is equal, according to strcmp(), to any of the strings in the array. |
|
|
|
|
const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", |
const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", |
"NextIfOpen", "PrevIfOpen", 0 }; |
"NextIfOpen", "PrevIfOpen", 0 }; |
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 }; |
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", |
|
"Rewind", 0 }; |
const char *azGoto[] = { "Goto", 0 }; |
const char *azGoto[] = { "Goto", 0 }; |
|
|
/* Try to figure out if this is really an EXPLAIN statement. If this |
/* Try to figure out if this is really an EXPLAIN statement. If this |
|
|
/* Show the EXPLAIN QUERY PLAN if .eqp is on */ |
/* Show the EXPLAIN QUERY PLAN if .eqp is on */ |
if( pArg && pArg->autoEQP ){ |
if( pArg && pArg->autoEQP ){ |
sqlite3_stmt *pExplain; |
sqlite3_stmt *pExplain; |
char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", sqlite3_sql(pStmt)); |
char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", |
|
sqlite3_sql(pStmt)); |
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); |
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); |
if( rc==SQLITE_OK ){ |
if( rc==SQLITE_OK ){ |
while( sqlite3_step(pExplain)==SQLITE_ROW ){ |
while( sqlite3_step(pExplain)==SQLITE_ROW ){ |
|
|
display_stats(db, pArg, 0); |
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 |
/* Finalize the statement just executed. If this fails, save a |
** copy of the error message. Otherwise, set zSql to point to the |
** copy of the error message. Otherwise, set zSql to point to the |
** next statement to execute. */ |
** next statement to execute. */ |
|
|
#endif |
#endif |
".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" |
".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" |
".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" |
" csv Comma-separated values\n" |
" column Left-aligned columns. (See .width)\n" |
" column Left-aligned columns. (See .width)\n" |
" html HTML <table> code\n" |
" html HTML <table> code\n" |
" insert SQL insert statements for TABLE\n" |
" insert SQL insert statements for TABLE\n" |
" line One value per line\n" |
" line One value per line\n" |
" list Values delimited by .separator string\n" |
" list Values delimited by .separator strings\n" |
" tabs Tab-separated values\n" |
" tabs Tab-separated values\n" |
" tcl TCL list elements\n" |
" tcl TCL list elements\n" |
".nullvalue STRING Use STRING in place of NULL values\n" |
".nullvalue STRING Use STRING in place of NULL values\n" |
|
|
".read FILENAME Execute SQL in FILENAME\n" |
".read FILENAME Execute SQL in FILENAME\n" |
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
".save FILE Write in-memory database into 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" |
".schema ?TABLE? Show the CREATE statements\n" |
" If TABLE specified, only show tables matching\n" |
" If TABLE specified, only show tables matching\n" |
" LIKE pattern TABLE.\n" |
" LIKE pattern TABLE.\n" |
".separator STRING ?NL? Change separator used by output mode and .import\n" |
".separator COL ?ROW? Change the column separator and optionally the row\n" |
" NL is the end-of-line mark for CSV\n" |
" separator for both the output mode and .import\n" |
".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
".show Show the current values for various settings\n" |
".show Show the current values for various settings\n" |
".stats on|off Turn stats on or off\n" |
".stats on|off Turn stats on or off\n" |
|
|
} |
} |
|
|
/* |
/* |
** An object used to read a CSV file |
** An object used to read a CSV and other files for import. |
*/ |
*/ |
typedef struct CSVReader CSVReader; |
typedef struct ImportCtx ImportCtx; |
struct CSVReader { |
struct ImportCtx { |
const char *zFile; /* Name of the input file */ |
const char *zFile; /* Name of the input file */ |
FILE *in; /* Read the CSV text from this input stream */ |
FILE *in; /* Read the CSV text from this input stream */ |
char *z; /* Accumulated text for a field */ |
char *z; /* Accumulated text for a field */ |
|
|
int nAlloc; /* Space allocated for z[] */ |
int nAlloc; /* Space allocated for z[] */ |
int nLine; /* Current line number */ |
int nLine; /* Current line number */ |
int cTerm; /* Character that terminated the most recent field */ |
int cTerm; /* Character that terminated the most recent field */ |
int cSeparator; /* The separator character. (Usually ",") */ |
int cColSep; /* The column separator character. (Usually ",") */ |
|
int cRowSep; /* The row separator character. (Usually "\n") */ |
}; |
}; |
|
|
/* Append a single byte to z[] */ |
/* Append a single byte to z[] */ |
static void csv_append_char(CSVReader *p, int c){ |
static void import_append_char(ImportCtx *p, int c){ |
if( p->n+1>=p->nAlloc ){ |
if( p->n+1>=p->nAlloc ){ |
p->nAlloc += p->nAlloc + 100; |
p->nAlloc += p->nAlloc + 100; |
p->z = sqlite3_realloc(p->z, p->nAlloc); |
p->z = sqlite3_realloc(p->z, p->nAlloc); |
|
|
** + Input comes from p->in. |
** + Input comes from p->in. |
** + Store results in p->z of length p->n. Space to hold p->z comes |
** + Store results in p->z of length p->n. Space to hold p->z comes |
** from sqlite3_malloc(). |
** from sqlite3_malloc(). |
** + Use p->cSep as the separator. The default is ",". |
** + 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. |
** + Keep track of the line number in p->nLine. |
** + Store the character that terminates the field in p->cTerm. Store |
** + Store the character that terminates the field in p->cTerm. Store |
** EOF on end-of-file. |
** EOF on end-of-file. |
** + Report syntax errors on stderr |
** + Report syntax errors on stderr |
*/ |
*/ |
static char *csv_read_one_field(CSVReader *p){ |
static char *csv_read_one_field(ImportCtx *p){ |
int c, pc, ppc; |
int c; |
int cSep = p->cSeparator; |
int cSep = p->cColSep; |
|
int rSep = p->cRowSep; |
p->n = 0; |
p->n = 0; |
c = fgetc(p->in); |
c = fgetc(p->in); |
if( c==EOF || seenInterrupt ){ |
if( c==EOF || seenInterrupt ){ |
|
|
return 0; |
return 0; |
} |
} |
if( c=='"' ){ |
if( c=='"' ){ |
|
int pc, ppc; |
int startLine = p->nLine; |
int startLine = p->nLine; |
int cQuote = c; |
int cQuote = c; |
pc = ppc = 0; |
pc = ppc = 0; |
while( 1 ){ |
while( 1 ){ |
c = fgetc(p->in); |
c = fgetc(p->in); |
if( c=='\n' ) p->nLine++; |
if( c==rSep ) p->nLine++; |
if( c==cQuote ){ |
if( c==cQuote ){ |
if( pc==cQuote ){ |
if( pc==cQuote ){ |
pc = 0; |
pc = 0; |
|
|
} |
} |
} |
} |
if( (c==cSep && pc==cQuote) |
if( (c==cSep && pc==cQuote) |
|| (c=='\n' && pc==cQuote) |
|| (c==rSep && pc==cQuote) |
|| (c=='\n' && pc=='\r' && ppc==cQuote) |
|| (c==rSep && pc=='\r' && ppc==cQuote) |
|| (c==EOF && pc==cQuote) |
|| (c==EOF && pc==cQuote) |
){ |
){ |
do{ p->n--; }while( p->z[p->n]!=cQuote ); |
do{ p->n--; }while( p->z[p->n]!=cQuote ); |
|
|
if( c==EOF ){ |
if( c==EOF ){ |
fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", |
fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", |
p->zFile, startLine, cQuote); |
p->zFile, startLine, cQuote); |
p->cTerm = EOF; |
p->cTerm = c; |
break; |
break; |
} |
} |
csv_append_char(p, c); |
import_append_char(p, c); |
ppc = pc; |
ppc = pc; |
pc = c; |
pc = c; |
} |
} |
}else{ |
}else{ |
while( c!=EOF && c!=cSep && c!='\n' ){ |
while( c!=EOF && c!=cSep && c!=rSep ){ |
csv_append_char(p, c); |
import_append_char(p, c); |
c = fgetc(p->in); |
c = fgetc(p->in); |
} |
} |
if( c=='\n' ){ |
if( c==rSep ){ |
p->nLine++; |
p->nLine++; |
if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
} |
} |
|
|
return p->z; |
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 |
** Try to transfer data for table zTable. If an error is seen while |
** moving forward, try to go backwards. The backwards movement won't |
** moving forward, try to go backwards. The backwards movement won't |
|
|
int nByte; /* Number of bytes in an SQL string */ |
int nByte; /* Number of bytes in an SQL string */ |
int i, j; /* Loop counters */ |
int i, j; /* Loop counters */ |
int needCommit; /* True to COMMIT or ROLLBACK at end */ |
int needCommit; /* True to COMMIT or ROLLBACK at end */ |
int nSep; /* Number of bytes in p->separator[] */ |
int nSep; /* Number of bytes in p->colSeparator[] */ |
char *zSql; /* An SQL statement */ |
char *zSql; /* An SQL statement */ |
CSVReader sCsv; /* Reader context */ |
ImportCtx sCtx; /* Reader context */ |
|
char *(*xRead)(ImportCtx*); /* Procedure to read one value */ |
int (*xCloser)(FILE*); /* Procedure to close th3 connection */ |
int (*xCloser)(FILE*); /* Procedure to close th3 connection */ |
|
|
if( nArg!=3 ){ |
if( nArg!=3 ){ |
|
|
zFile = azArg[1]; |
zFile = azArg[1]; |
zTable = azArg[2]; |
zTable = azArg[2]; |
seenInterrupt = 0; |
seenInterrupt = 0; |
memset(&sCsv, 0, sizeof(sCsv)); |
memset(&sCtx, 0, sizeof(sCtx)); |
open_db(p, 0); |
open_db(p, 0); |
nSep = strlen30(p->separator); |
nSep = strlen30(p->colSeparator); |
if( nSep==0 ){ |
if( nSep==0 ){ |
fprintf(stderr, "Error: non-null separator required for import\n"); |
fprintf(stderr, "Error: non-null column separator required for import\n"); |
return 1; |
return 1; |
} |
} |
if( nSep>1 ){ |
if( nSep>1 ){ |
fprintf(stderr, "Error: multi-character separators not allowed" |
fprintf(stderr, "Error: multi-character column separators not allowed" |
" for import\n"); |
" for import\n"); |
return 1; |
return 1; |
} |
} |
sCsv.zFile = zFile; |
nSep = strlen30(p->rowSeparator); |
sCsv.nLine = 1; |
if( nSep==0 ){ |
if( sCsv.zFile[0]=='|' ){ |
fprintf(stderr, "Error: non-null row separator required for import\n"); |
sCsv.in = popen(sCsv.zFile+1, "r"); |
return 1; |
sCsv.zFile = "<pipe>"; |
} |
|
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 = "<pipe>"; |
xCloser = pclose; |
xCloser = pclose; |
}else{ |
}else{ |
sCsv.in = fopen(sCsv.zFile, "rb"); |
sCtx.in = fopen(sCtx.zFile, "rb"); |
xCloser = fclose; |
xCloser = fclose; |
} |
} |
if( sCsv.in==0 ){ |
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); |
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
return 1; |
return 1; |
} |
} |
sCsv.cSeparator = p->separator[0]; |
sCtx.cColSep = p->colSeparator[0]; |
|
sCtx.cRowSep = p->rowSeparator[0]; |
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
if( zSql==0 ){ |
if( zSql==0 ){ |
fprintf(stderr, "Error: out of memory\n"); |
fprintf(stderr, "Error: out of memory\n"); |
xCloser(sCsv.in); |
xCloser(sCtx.in); |
return 1; |
return 1; |
} |
} |
nByte = strlen30(zSql); |
nByte = strlen30(zSql); |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */ |
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ |
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ |
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); |
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); |
char cSep = '('; |
char cSep = '('; |
while( csv_read_one_field(&sCsv) ){ |
while( xRead(&sCtx) ){ |
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); |
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); |
cSep = ','; |
cSep = ','; |
if( sCsv.cTerm!=sCsv.cSeparator ) break; |
if( sCtx.cTerm!=sCtx.cColSep ) break; |
} |
} |
if( cSep=='(' ){ |
if( cSep=='(' ){ |
sqlite3_free(zCreate); |
sqlite3_free(zCreate); |
sqlite3_free(sCsv.z); |
sqlite3_free(sCtx.z); |
xCloser(sCsv.in); |
xCloser(sCtx.in); |
fprintf(stderr,"%s: empty file\n", sCsv.zFile); |
fprintf(stderr,"%s: empty file\n", sCtx.zFile); |
return 1; |
return 1; |
} |
} |
zCreate = sqlite3_mprintf("%z\n)", zCreate); |
zCreate = sqlite3_mprintf("%z\n)", zCreate); |
|
|
if( rc ){ |
if( rc ){ |
fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, |
fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, |
sqlite3_errmsg(db)); |
sqlite3_errmsg(db)); |
sqlite3_free(sCsv.z); |
sqlite3_free(sCtx.z); |
xCloser(sCsv.in); |
xCloser(sCtx.in); |
return 1; |
return 1; |
} |
} |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
|
|
if( rc ){ |
if( rc ){ |
if (pStmt) sqlite3_finalize(pStmt); |
if (pStmt) sqlite3_finalize(pStmt); |
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
xCloser(sCsv.in); |
xCloser(sCtx.in); |
return 1; |
return 1; |
} |
} |
nCol = sqlite3_column_count(pStmt); |
nCol = sqlite3_column_count(pStmt); |
|
|
zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); |
zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); |
if( zSql==0 ){ |
if( zSql==0 ){ |
fprintf(stderr, "Error: out of memory\n"); |
fprintf(stderr, "Error: out of memory\n"); |
xCloser(sCsv.in); |
xCloser(sCtx.in); |
return 1; |
return 1; |
} |
} |
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
|
|
if( rc ){ |
if( rc ){ |
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
if (pStmt) sqlite3_finalize(pStmt); |
if (pStmt) sqlite3_finalize(pStmt); |
xCloser(sCsv.in); |
xCloser(sCtx.in); |
return 1; |
return 1; |
} |
} |
needCommit = sqlite3_get_autocommit(db); |
needCommit = sqlite3_get_autocommit(db); |
if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
do{ |
do{ |
int startLine = sCsv.nLine; |
int startLine = sCtx.nLine; |
for(i=0; i<nCol; i++){ |
for(i=0; i<nCol; i++){ |
char *z = csv_read_one_field(&sCsv); |
char *z = xRead(&sCtx); |
|
/* |
|
** Did we reach end-of-file before finding any columns? |
|
** If so, stop instead of NULL filling the remaining columns. |
|
*/ |
if( z==0 && i==0 ) break; |
if( z==0 && i==0 ) break; |
|
/* |
|
** Did we reach end-of-file OR end-of-line before finding any |
|
** columns in ASCII mode? If so, stop instead of NULL filling |
|
** the remaining columns. |
|
*/ |
|
if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; |
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){ |
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
"filling the rest with NULL\n", |
"filling the rest with NULL\n", |
sCsv.zFile, startLine, nCol, i+1); |
sCtx.zFile, startLine, nCol, i+1); |
i++; |
i++; |
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
} |
} |
} |
} |
if( sCsv.cTerm==sCsv.cSeparator ){ |
if( sCtx.cTerm==sCtx.cColSep ){ |
do{ |
do{ |
csv_read_one_field(&sCsv); |
xRead(&sCtx); |
i++; |
i++; |
}while( sCsv.cTerm==sCsv.cSeparator ); |
}while( sCtx.cTerm==sCtx.cColSep ); |
fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
"extras ignored\n", |
"extras ignored\n", |
sCsv.zFile, startLine, nCol, i); |
sCtx.zFile, startLine, nCol, i); |
} |
} |
if( i>=nCol ){ |
if( i>=nCol ){ |
sqlite3_step(pStmt); |
sqlite3_step(pStmt); |
rc = sqlite3_reset(pStmt); |
rc = sqlite3_reset(pStmt); |
if( rc!=SQLITE_OK ){ |
if( rc!=SQLITE_OK ){ |
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, |
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, |
sqlite3_errmsg(db)); |
sqlite3_errmsg(db)); |
} |
} |
} |
} |
}while( sCsv.cTerm!=EOF ); |
}while( sCtx.cTerm!=EOF ); |
|
|
xCloser(sCsv.in); |
xCloser(sCtx.in); |
sqlite3_free(sCsv.z); |
sqlite3_free(sCtx.z); |
sqlite3_finalize(pStmt); |
sqlite3_finalize(pStmt); |
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
}else |
}else |
|
|
p->mode = MODE_Html; |
p->mode = MODE_Html; |
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ |
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ |
p->mode = MODE_Tcl; |
p->mode = MODE_Tcl; |
sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); |
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
p->mode = MODE_Csv; |
p->mode = MODE_Csv; |
sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); |
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
}else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
}else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
p->mode = MODE_List; |
p->mode = MODE_List; |
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); |
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
p->mode = MODE_Insert; |
p->mode = MODE_Insert; |
set_table_name(p, nArg>=3 ? azArg[2] : "table"); |
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 { |
}else { |
fprintf(stderr,"Error: mode should be one of: " |
fprintf(stderr,"Error: mode should be one of: " |
"column csv html insert line list tabs tcl\n"); |
"ascii column csv html insert line list tabs tcl\n"); |
rc = 1; |
rc = 1; |
} |
} |
}else |
}else |
|
|
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ |
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ |
if( nArg==2 ){ |
if( nArg==2 ){ |
sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, |
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, |
"%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); |
"%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); |
}else{ |
}else{ |
fprintf(stderr, "Usage: .nullvalue STRING\n"); |
fprintf(stderr, "Usage: .nullvalue STRING\n"); |
rc = 1; |
rc = 1; |
|
|
sqlite3_close(pSrc); |
sqlite3_close(pSrc); |
}else |
}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 ){ |
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ |
ShellState data; |
ShellState data; |
char *zErrMsg = 0; |
char *zErrMsg = 0; |
|
|
|
|
if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
if( nArg<2 || nArg>3 ){ |
if( nArg<2 || nArg>3 ){ |
fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); |
fprintf(stderr, "Usage: .separator COL ?ROW?\n"); |
rc = 1; |
rc = 1; |
} |
} |
if( nArg>=2 ){ |
if( nArg>=2 ){ |
sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); |
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, |
|
"%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); |
} |
} |
if( nArg>=3 ){ |
if( nArg>=3 ){ |
sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); |
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, |
|
"%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); |
} |
} |
}else |
}else |
|
|
|
|
rc = 1; |
rc = 1; |
goto meta_command_exit; |
goto meta_command_exit; |
} |
} |
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); |
fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); |
fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "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,"%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,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); |
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); |
fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); |
fprintf(p->out,"%9.9s: ", "nullvalue"); |
fprintf(p->out,"%12.12s: ", "nullvalue"); |
output_c_string(p->out, p->nullvalue); |
output_c_string(p->out, p->nullValue); |
fprintf(p->out, "\n"); |
fprintf(p->out, "\n"); |
fprintf(p->out,"%9.9s: %s\n","output", |
fprintf(p->out,"%12.12s: %s\n","output", |
strlen30(p->outfile) ? p->outfile : "stdout"); |
strlen30(p->outfile) ? p->outfile : "stdout"); |
fprintf(p->out,"%9.9s: ", "separator"); |
fprintf(p->out,"%12.12s: ", "colseparator"); |
output_c_string(p->out, p->separator); |
output_c_string(p->out, p->colSeparator); |
fprintf(p->out," "); |
|
output_c_string(p->out, p->newline); |
|
fprintf(p->out, "\n"); |
fprintf(p->out, "\n"); |
fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
fprintf(p->out,"%12.12s: ", "rowseparator"); |
fprintf(p->out,"%9.9s: ","width"); |
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++) { |
for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
fprintf(p->out,"%d ",p->colWidth[i]); |
fprintf(p->out,"%d ",p->colWidth[i]); |
} |
} |
|
|
for(i=0; i<nPrintRow; i++){ |
for(i=0; i<nPrintRow; i++){ |
for(j=i; j<nRow; j+=nPrintRow){ |
for(j=i; j<nRow; j+=nPrintRow){ |
char *zSp = j<nPrintRow ? "" : " "; |
char *zSp = j<nPrintRow ? "" : " "; |
fprintf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); |
fprintf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); |
} |
} |
fprintf(p->out, "\n"); |
fprintf(p->out, "\n"); |
} |
} |
|
|
static char *home_dir = NULL; |
static char *home_dir = NULL; |
if( home_dir ) return home_dir; |
if( home_dir ) return home_dir; |
|
|
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) |
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \ |
|
&& !defined(__RTP__) && !defined(_WRS_KERNEL) |
{ |
{ |
struct passwd *pwent; |
struct passwd *pwent; |
uid_t uid = getuid(); |
uid_t uid = getuid(); |
|
|
** Show available command line options |
** Show available command line options |
*/ |
*/ |
static const char zOptions[] = |
static const char zOptions[] = |
|
" -ascii set output mode to 'ascii'\n" |
" -bail stop after hitting an error\n" |
" -bail stop after hitting an error\n" |
" -batch force batch I/O\n" |
" -batch force batch I/O\n" |
" -column set output mode to 'column'\n" |
" -column set output mode to 'column'\n" |
|
|
#ifdef SQLITE_ENABLE_MULTIPLEX |
#ifdef SQLITE_ENABLE_MULTIPLEX |
" -multiplex enable the multiplexor VFS\n" |
" -multiplex enable the multiplexor VFS\n" |
#endif |
#endif |
" -newline SEP set newline character(s) for CSV\n" |
" -newline SEP set output row separator. Default: '\\n'\n" |
" -nullvalue TEXT set text string for NULL values. Default ''\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" |
" -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" |
" -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" |
" -separator SEP set output field separator. Default: '|'\n" |
" -separator SEP set output column separator. Default: '|'\n" |
" -stats print memory stats before each finalize\n" |
" -stats print memory stats before each finalize\n" |
" -version show SQLite version\n" |
" -version show SQLite version\n" |
" -vfs NAME use NAME as the default VFS\n" |
" -vfs NAME use NAME as the default VFS\n" |
|
|
static void main_init(ShellState *data) { |
static void main_init(ShellState *data) { |
memset(data, 0, sizeof(*data)); |
memset(data, 0, sizeof(*data)); |
data->mode = MODE_List; |
data->mode = MODE_List; |
memcpy(data->separator,"|", 2); |
memcpy(data->colSeparator,SEP_Column, 2); |
memcpy(data->newline,"\r\n", 3); |
memcpy(data->rowSeparator,SEP_Row, 2); |
data->showHeader = 0; |
data->showHeader = 0; |
data->shellFlgs = SHFLG_Lookaside; |
data->shellFlgs = SHFLG_Lookaside; |
sqlite3_config(SQLITE_CONFIG_URI, 1); |
sqlite3_config(SQLITE_CONFIG_URI, 1); |
|
|
char *zErrMsg = 0; |
char *zErrMsg = 0; |
ShellState data; |
ShellState data; |
const char *zInitFile = 0; |
const char *zInitFile = 0; |
char *zFirstCmd = 0; |
|
int i; |
int i; |
int rc = 0; |
int rc = 0; |
int warnInmemoryDb = 0; |
int warnInmemoryDb = 0; |
|
int readStdin = 1; |
|
int nCmd = 0; |
|
char **azCmd = 0; |
|
|
#if USE_SYSTEM_SQLITE+0!=1 |
#if USE_SYSTEM_SQLITE+0!=1 |
if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){ |
if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){ |
|
|
signal(SIGINT, interrupt_handler); |
signal(SIGINT, interrupt_handler); |
#endif |
#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 |
/* Do an initial pass through the command-line argument to locate |
** the name of the database file, the name of the initialization file, |
** the name of the database file, the name of the initialization file, |
** the size of the alternative malloc heap, |
** the size of the alternative malloc heap, |
|
|
if( z[0]!='-' ){ |
if( z[0]!='-' ){ |
if( data.zDbFilename==0 ){ |
if( data.zDbFilename==0 ){ |
data.zDbFilename = z; |
data.zDbFilename = z; |
continue; |
}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( 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( z[1]=='-' ) z++; |
if( strcmp(z,"-separator")==0 |
if( strcmp(z,"-separator")==0 |
|
|
fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); |
fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); |
return 1; |
return 1; |
#endif |
#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; |
data.out = stdout; |
|
|
|
|
data.mode = MODE_Column; |
data.mode = MODE_Column; |
}else if( strcmp(z,"-csv")==0 ){ |
}else if( strcmp(z,"-csv")==0 ){ |
data.mode = MODE_Csv; |
data.mode = MODE_Csv; |
memcpy(data.separator,",",2); |
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 ){ |
}else if( strcmp(z,"-separator")==0 ){ |
sqlite3_snprintf(sizeof(data.separator), data.separator, |
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, |
"%s",cmdline_option_value(argc,argv,++i)); |
"%s",cmdline_option_value(argc,argv,++i)); |
}else if( strcmp(z,"-newline")==0 ){ |
}else if( strcmp(z,"-newline")==0 ){ |
sqlite3_snprintf(sizeof(data.newline), data.newline, |
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, |
"%s",cmdline_option_value(argc,argv,++i)); |
"%s",cmdline_option_value(argc,argv,++i)); |
}else if( strcmp(z,"-nullvalue")==0 ){ |
}else if( strcmp(z,"-nullvalue")==0 ){ |
sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, |
"%s",cmdline_option_value(argc,argv,++i)); |
"%s",cmdline_option_value(argc,argv,++i)); |
}else if( strcmp(z,"-header")==0 ){ |
}else if( strcmp(z,"-header")==0 ){ |
data.showHeader = 1; |
data.showHeader = 1; |
|
|
data.autoEQP = 1; |
data.autoEQP = 1; |
}else if( strcmp(z,"-stats")==0 ){ |
}else if( strcmp(z,"-stats")==0 ){ |
data.statsOn = 1; |
data.statsOn = 1; |
|
}else if( strcmp(z,"-scanstats")==0 ){ |
|
data.scanstatsOn = 1; |
}else if( strcmp(z,"-bail")==0 ){ |
}else if( strcmp(z,"-bail")==0 ){ |
bail_on_error = 1; |
bail_on_error = 1; |
}else if( strcmp(z,"-version")==0 ){ |
}else if( strcmp(z,"-version")==0 ){ |
|
|
}else if( strcmp(z,"-help")==0 ){ |
}else if( strcmp(z,"-help")==0 ){ |
usage(1); |
usage(1); |
}else if( strcmp(z,"-cmd")==0 ){ |
}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; |
if( i==argc-1 ) break; |
z = cmdline_option_value(argc,argv,++i); |
z = cmdline_option_value(argc,argv,++i); |
if( z[0]=='.' ){ |
if( z[0]=='.' ){ |
|
|
} |
} |
} |
} |
|
|
if( zFirstCmd ){ |
if( !readStdin ){ |
/* Run just the command that follows the database name |
/* 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. |
*/ |
*/ |
if( zFirstCmd[0]=='.' ){ |
for(i=0; i<nCmd; i++){ |
rc = do_meta_command(zFirstCmd, &data); |
if( azCmd[i][0]=='.' ){ |
if( rc==2 ) rc = 0; |
rc = do_meta_command(azCmd[i], &data); |
}else{ |
if( rc ) return rc==2 ? 0 : rc; |
open_db(&data, 0); |
}else{ |
rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg); |
open_db(&data, 0); |
if( zErrMsg!=0 ){ |
rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg); |
fprintf(stderr,"Error: %s\n", zErrMsg); |
if( zErrMsg!=0 ){ |
return rc!=0 ? rc : 1; |
fprintf(stderr,"Error: %s\n", zErrMsg); |
}else if( rc!=0 ){ |
return rc!=0 ? rc : 1; |
fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd); |
}else if( rc!=0 ){ |
return rc; |
fprintf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); |
|
return rc; |
|
} |
} |
} |
} |
} |
|
free(azCmd); |
}else{ |
}else{ |
/* Run commands received from standard input |
/* Run commands received from standard input |
*/ |
*/ |
|
|
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
} |
} |
} |
} |
#if defined(HAVE_READLINE) |
if( zHistory ) shell_read_history(zHistory); |
if( zHistory ) read_history(zHistory); |
|
#endif |
|
rc = process_input(&data, 0); |
rc = process_input(&data, 0); |
if( zHistory ){ |
if( zHistory ){ |
stifle_history(100); |
shell_stifle_history(100); |
write_history(zHistory); |
shell_write_history(zHistory); |
free(zHistory); |
free(zHistory); |
} |
} |
}else{ |
}else{ |