1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-05 04:30:38 +03:00

Improved handling of UTF8 by the command-line shell.

FossilOrigin-Name: 4534ebf15fbcd6fe2028957b7aa591b6cd5da95f
This commit is contained in:
drh
2016-04-04 17:34:54 +00:00
5 changed files with 437 additions and 254 deletions

View File

@@ -1,5 +1,5 @@
C Add\sthe\s".auth\sON|OFF"\scommand\sto\sthe\scommand-line\sshell. C Improved\shandling\sof\sUTF8\sby\sthe\scommand-line\sshell.
D 2016-04-04T17:23:10.395 D 2016-04-04T17:34:54.498
F Makefile.in e812bb732d7af01baa09f1278bd4f4a2e3a09449 F Makefile.in e812bb732d7af01baa09f1278bd4f4a2e3a09449
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc fe57d7e3e74fa383fd01ced796c0ffd966fc094a F Makefile.msc fe57d7e3e74fa383fd01ced796c0ffd966fc094a
@@ -360,7 +360,7 @@ F src/os.h 91ff889115ecd01f436d3611f7f5ea4dc12d92f1
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c b1ccb273771f41dbdbe0ba7c1ad63c38ad5972ec F src/os_unix.c b1ccb273771f41dbdbe0ba7c1ad63c38ad5972ec
F src/os_win.c ff870d89f4cb088a04cbf5ea0cbd9ff1b089ff4a F src/os_win.c b3ba9573d8d893e70a6a8015bbee572ecf7ffbef
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c 38718a019ca762ba4f6795425d5a54db70d1790d F src/pager.c 38718a019ca762ba4f6795425d5a54db70d1790d
F src/pager.h e1d38a2f14849e219df0f91f8323504d134c8a56 F src/pager.h e1d38a2f14849e219df0f91f8323504d134c8a56
@@ -376,7 +376,7 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c b8f7174e5f8c33c44ded3a25a973d0bb89228c20 F src/resolve.c b8f7174e5f8c33c44ded3a25a973d0bb89228c20
F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e
F src/select.c 7849cee0a01952a9c93cd28989daedfa57731143 F src/select.c 7849cee0a01952a9c93cd28989daedfa57731143
F src/shell.c e0996a0be612c8d2630fdf8bcedf4c4260a29734 F src/shell.c b7922fa264f8c8d72a5ec6dd0b091e15a93c4de5
F src/sqlite.h.in c46a7b85d3f37371cacea8f98ec825f5e52c420c F src/sqlite.h.in c46a7b85d3f37371cacea8f98ec825f5e52c420c
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 98f72cbfe00169c39089115427d06ea05fe4b4a2 F src/sqlite3ext.h 98f72cbfe00169c39089115427d06ea05fe4b4a2
@@ -1051,7 +1051,7 @@ F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506 F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test dff5b20ad989770aface6d714491121172dfe8b0 F test/shell1.test a216486c6aeae4d3e75e37d64e4b6c8974108750
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
F test/shell3.test da513d522ef6f01cee8475dcf8332bff8982b3dd F test/shell3.test da513d522ef6f01cee8475dcf8332bff8982b3dd
F test/shell4.test 69995ee1cc278eb149aa8746ce1f935f4eaf98b9 F test/shell4.test 69995ee1cc278eb149aa8746ce1f935f4eaf98b9
@@ -1482,7 +1482,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 8627a4cd6d64bd076b56c1e8ccc3b1dfc1b4c07d P 65c7bcc42786a254966c531ba9062abb8fc8c5bf f4cbd18db47af4af990d7891dcc831e92b3f17e0
R 327fee759e5533f8bd09762659f3f70b R 6a3470f790b8def5155325c3cfdf4cee
T +closed f4cbd18db47af4af990d7891dcc831e92b3f17e0
U drh U drh
Z ff39f0b57606b297cab9fa07bc83cd18 Z cd8719efec0532e424e0fef6159fe981

View File

@@ -1 +1 @@
65c7bcc42786a254966c531ba9062abb8fc8c5bf 4534ebf15fbcd6fe2028957b7aa591b6cd5da95f

View File

@@ -1306,6 +1306,12 @@ void sqlite3_win32_write_debug(const char *zBuf, int nBuf){
int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */
if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */
assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE ); assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE );
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zBuf ){
(void)SQLITE_MISUSE_BKPT;
return;
}
#endif
#if defined(SQLITE_WIN32_HAS_ANSI) #if defined(SQLITE_WIN32_HAS_ANSI)
if( nMin>0 ){ if( nMin>0 ){
memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
@@ -1631,147 +1637,244 @@ void sqlite3MemSetDefault(void){
#endif /* SQLITE_WIN32_MALLOC */ #endif /* SQLITE_WIN32_MALLOC */
/* /*
** Convert a UTF-8 string to Microsoft Unicode (UTF-16?). ** Convert a UTF-8 string to Microsoft Unicode.
** **
** Space to hold the returned string is obtained from malloc. ** Space to hold the returned string is obtained from sqlite3_malloc().
*/ */
static LPWSTR winUtf8ToUnicode(const char *zFilename){ static LPWSTR winUtf8ToUnicode(const char *zText){
int nChar; int nChar;
LPWSTR zWideFilename; LPWSTR zWideText;
nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
if( nChar==0 ){ if( nChar==0 ){
return 0; return 0;
} }
zWideFilename = sqlite3MallocZero( nChar*sizeof(zWideFilename[0]) ); zWideText = sqlite3MallocZero( nChar*sizeof(WCHAR) );
if( zWideFilename==0 ){ if( zWideText==0 ){
return 0; return 0;
} }
nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText,
nChar); nChar);
if( nChar==0 ){ if( nChar==0 ){
sqlite3_free(zWideFilename); sqlite3_free(zWideText);
zWideFilename = 0; zWideText = 0;
} }
return zWideFilename; return zWideText;
} }
/* /*
** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is ** Convert a Microsoft Unicode string to UTF-8.
** obtained from sqlite3_malloc(). **
** Space to hold the returned string is obtained from sqlite3_malloc().
*/ */
static char *winUnicodeToUtf8(LPCWSTR zWideFilename){ static char *winUnicodeToUtf8(LPCWSTR zWideText){
int nByte; int nByte;
char *zFilename; char *zText;
nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
if( nByte == 0 ){ if( nByte == 0 ){
return 0; return 0;
} }
zFilename = sqlite3MallocZero( nByte ); zText = sqlite3MallocZero( nByte );
if( zFilename==0 ){ if( zText==0 ){
return 0; return 0;
} }
nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte,
0, 0); 0, 0);
if( nByte == 0 ){ if( nByte == 0 ){
sqlite3_free(zFilename); sqlite3_free(zText);
zFilename = 0; zText = 0;
} }
return zFilename; return zText;
} }
/* /*
** Convert an ANSI string to Microsoft Unicode, based on the ** Convert an ANSI string to Microsoft Unicode, using the ANSI or OEM
** current codepage settings for file apis. ** code page.
** **
** Space to hold the returned string is obtained ** Space to hold the returned string is obtained from sqlite3_malloc().
** from sqlite3_malloc.
*/ */
static LPWSTR winMbcsToUnicode(const char *zFilename){ static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
int nByte; int nByte;
LPWSTR zMbcsFilename; LPWSTR zMbcsText;
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; int codepage = useAnsi ? CP_ACP : CP_OEMCP;
nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL, nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
0)*sizeof(WCHAR); 0)*sizeof(WCHAR);
if( nByte==0 ){ if( nByte==0 ){
return 0; return 0;
} }
zMbcsFilename = sqlite3MallocZero( nByte*sizeof(zMbcsFilename[0]) ); zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
if( zMbcsFilename==0 ){ if( zMbcsText==0 ){
return 0; return 0;
} }
nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
nByte); nByte);
if( nByte==0 ){ if( nByte==0 ){
sqlite3_free(zMbcsFilename); sqlite3_free(zMbcsText);
zMbcsFilename = 0; zMbcsText = 0;
} }
return zMbcsFilename; return zMbcsText;
} }
/* /*
** Convert Microsoft Unicode to multi-byte character string, based on the ** Convert a Microsoft Unicode string to a multi-byte character string,
** user's ANSI codepage. ** using the ANSI or OEM code page.
** **
** Space to hold the returned string is obtained from ** Space to hold the returned string is obtained from sqlite3_malloc().
** sqlite3_malloc().
*/ */
static char *winUnicodeToMbcs(LPCWSTR zWideFilename){ static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){
int nByte; int nByte;
char *zFilename; char *zText;
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; int codepage = useAnsi ? CP_ACP : CP_OEMCP;
nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, 0, 0, 0, 0);
if( nByte == 0 ){ if( nByte == 0 ){
return 0; return 0;
} }
zFilename = sqlite3MallocZero( nByte ); zText = sqlite3MallocZero( nByte );
if( zFilename==0 ){ if( zText==0 ){
return 0; return 0;
} }
nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, zText,
nByte, 0, 0); nByte, 0, 0);
if( nByte == 0 ){ if( nByte == 0 ){
sqlite3_free(zFilename); sqlite3_free(zText);
zFilename = 0; zText = 0;
} }
return zFilename; return zText;
} }
/* /*
** Convert multibyte character string to UTF-8. Space to hold the ** Convert a multi-byte character string to UTF-8.
** returned string is obtained from sqlite3_malloc(). **
** Space to hold the returned string is obtained from sqlite3_malloc().
*/ */
char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ static char *winMbcsToUtf8(const char *zText, int useAnsi){
char *zFilenameUtf8; char *zTextUtf8;
LPWSTR zTmpWide; LPWSTR zTmpWide;
zTmpWide = winMbcsToUnicode(zFilename); zTmpWide = winMbcsToUnicode(zText, useAnsi);
if( zTmpWide==0 ){ if( zTmpWide==0 ){
return 0; return 0;
} }
zFilenameUtf8 = winUnicodeToUtf8(zTmpWide); zTextUtf8 = winUnicodeToUtf8(zTmpWide);
sqlite3_free(zTmpWide); sqlite3_free(zTmpWide);
return zFilenameUtf8; return zTextUtf8;
} }
/* /*
** Convert UTF-8 to multibyte character string. Space to hold the ** Convert a UTF-8 string to a multi-byte character string.
** returned string is obtained from sqlite3_malloc(). **
** Space to hold the returned string is obtained from sqlite3_malloc().
*/ */
char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ static char *winUtf8ToMbcs(const char *zText, int useAnsi){
char *zFilenameMbcs; char *zTextMbcs;
LPWSTR zTmpWide; LPWSTR zTmpWide;
zTmpWide = winUtf8ToUnicode(zFilename); zTmpWide = winUtf8ToUnicode(zText);
if( zTmpWide==0 ){ if( zTmpWide==0 ){
return 0; return 0;
} }
zFilenameMbcs = winUnicodeToMbcs(zTmpWide); zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi);
sqlite3_free(zTmpWide); sqlite3_free(zTmpWide);
return zFilenameMbcs; return zTextMbcs;
}
/*
** This is a public wrapper for the winUtf8ToUnicode() function.
*/
LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zText ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
return winUtf8ToUnicode(zText);
}
/*
** This is a public wrapper for the winUnicodeToUtf8() function.
*/
char *sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zWideText ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
return winUnicodeToUtf8(zWideText);
}
/*
** This is a public wrapper for the winMbcsToUtf8() function.
*/
char *sqlite3_win32_mbcs_to_utf8(const char *zText){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zText ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
return winMbcsToUtf8(zText, osAreFileApisANSI());
}
/*
** This is a public wrapper for the winMbcsToUtf8() function.
*/
char *sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zText ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
return winMbcsToUtf8(zText, useAnsi);
}
/*
** This is a public wrapper for the winUtf8ToMbcs() function.
*/
char *sqlite3_win32_utf8_to_mbcs(const char *zText){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zText ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
return winUtf8ToMbcs(zText, osAreFileApisANSI());
}
/*
** This is a public wrapper for the winUtf8ToMbcs() function.
*/
char *sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !zText ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
return winUtf8ToMbcs(zText, useAnsi);
} }
/* /*
@@ -1873,7 +1976,7 @@ static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
if( dwLen > 0 ){ if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */ /* allocate a buffer and convert to UTF8 */
sqlite3BeginBenignMalloc(); sqlite3BeginBenignMalloc();
zOut = sqlite3_win32_mbcs_to_utf8(zTemp); zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
sqlite3EndBenignMalloc(); sqlite3EndBenignMalloc();
/* free the system buffer allocated by FormatMessage */ /* free the system buffer allocated by FormatMessage */
osLocalFree(zTemp); osLocalFree(zTemp);
@@ -4318,7 +4421,7 @@ static char *winConvertToUtf8Filename(const void *zFilename){
} }
#ifdef SQLITE_WIN32_HAS_ANSI #ifdef SQLITE_WIN32_HAS_ANSI
else{ else{
zConverted = sqlite3_win32_mbcs_to_utf8(zFilename); zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI());
} }
#endif #endif
/* caller will handle out of memory */ /* caller will handle out of memory */
@@ -4339,7 +4442,7 @@ static void *winConvertFromUtf8Filename(const char *zFilename){
} }
#ifdef SQLITE_WIN32_HAS_ANSI #ifdef SQLITE_WIN32_HAS_ANSI
else{ else{
zConverted = sqlite3_win32_utf8_to_mbcs(zFilename); zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
} }
#endif #endif
/* caller will handle out of memory */ /* caller will handle out of memory */
@@ -4540,7 +4643,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
"winGetTempname3", 0); "winGetTempname3", 0);
} }
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); zUtf8 = winMbcsToUtf8(zMbcsPath, osAreFileApisANSI());
if( zUtf8 ){ if( zUtf8 ){
sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
sqlite3_free(zUtf8); sqlite3_free(zUtf8);
@@ -5318,7 +5421,7 @@ static int winFullPathname(
"winFullPathname4", zRelative); "winFullPathname4", zRelative);
} }
sqlite3_free(zConverted); sqlite3_free(zConverted);
zOut = sqlite3_win32_mbcs_to_utf8(zTemp); zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
sqlite3_free(zTemp); sqlite3_free(zTemp);
} }
#endif #endif

View File

@@ -90,7 +90,7 @@
#else #else
# define shell_read_history(X) # define shell_read_history(X)
# define shell_write_history(X) # define shell_write_history(X)
# define shell_stifle_history(X) # define shell_stifle_history(X)
@@ -136,6 +136,15 @@
#define IsDigit(X) isdigit((unsigned char)X) #define IsDigit(X) isdigit((unsigned char)X)
#define ToLower(X) (char)tolower((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X)
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
/* string conversion routines only needed on Win32 */
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int);
extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int);
#endif
/* On Windows, we normally run with output mode of TEXT so that \n characters /* On Windows, we normally run with output mode of TEXT so that \n characters
** are automatically translated into \r\n. However, this behavior needs ** are automatically translated into \r\n. However, this behavior needs
** to be disabled in some cases (ex: when generating CSV output and when ** to be disabled in some cases (ex: when generating CSV output and when
@@ -143,17 +152,17 @@
** routines take care of that. ** routines take care of that.
*/ */
#if defined(_WIN32) || defined(WIN32) #if defined(_WIN32) || defined(WIN32)
static void setBinaryMode(FILE *out){ static void setBinaryMode(FILE *file, int isOutput){
fflush(out); if( isOutput ) fflush(file);
_setmode(_fileno(out), _O_BINARY); _setmode(_fileno(file), _O_BINARY);
} }
static void setTextMode(FILE *out){ static void setTextMode(FILE *file, int isOutput){
fflush(out); if( isOutput ) fflush(file);
_setmode(_fileno(out), _O_TEXT); _setmode(_fileno(file), _O_TEXT);
} }
#else #else
# define setBinaryMode(X) # define setBinaryMode(X,Y)
# define setTextMode(X) # define setTextMode(X,Y)
#endif #endif
@@ -204,7 +213,7 @@ static void beginTimer(void){
/* Return the difference of two time_structs in seconds */ /* Return the difference of two time_structs in seconds */
static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
(double)(pEnd->tv_sec - pStart->tv_sec); (double)(pEnd->tv_sec - pStart->tv_sec);
} }
@@ -229,8 +238,6 @@ static void endTimer(void){
#elif (defined(_WIN32) || defined(WIN32)) #elif (defined(_WIN32) || defined(WIN32))
#include <windows.h>
/* Saved resource information for the beginning of an operation */ /* Saved resource information for the beginning of an operation */
static HANDLE hProcess; static HANDLE hProcess;
static FILETIME ftKernelBegin; static FILETIME ftKernelBegin;
@@ -261,7 +268,7 @@ static int hasTimer(void){
if( NULL != getProcessTimesAddr ){ if( NULL != getProcessTimesAddr ){
return 1; return 1;
} }
FreeLibrary(hinstLib); FreeLibrary(hinstLib);
} }
} }
} }
@@ -307,7 +314,7 @@ static void endTimer(void){
#define HAS_TIMER hasTimer() #define HAS_TIMER hasTimer()
#else #else
#define BEGIN_TIMER #define BEGIN_TIMER
#define END_TIMER #define END_TIMER
#define HAS_TIMER 0 #define HAS_TIMER 0
#endif #endif
@@ -361,6 +368,38 @@ static char *Argv0;
static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
/*
** Render output like fprintf(). Except, if the output is going to the
** console and if this is running on a Windows machine, translate the
** output from UTF-8 into MBCS.
*/
#if defined(_WIN32) || defined(WIN32)
void utf8_printf(FILE *out, const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
if( stdout_is_console && (out==stdout || out==stderr) ){
char *z1 = sqlite3_vmprintf(zFormat, ap);
char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0);
sqlite3_free(z1);
fputs(z2, out);
sqlite3_free(z2);
}else{
vfprintf(out, zFormat, ap);
}
va_end(ap);
}
#elif !defined(utf8_printf)
# define utf8_printf fprintf
#endif
/*
** Render output like fprintf(). This should not be used on anything that
** includes string formatting (e.g. "%s").
*/
#if !defined(raw_printf)
# define raw_printf fprintf
#endif
/* /*
** Write I/O traces to the following stream. ** Write I/O traces to the following stream.
*/ */
@@ -382,7 +421,7 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
va_start(ap, zFormat); va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap); z = sqlite3_vmprintf(zFormat, ap);
va_end(ap); va_end(ap);
fprintf(iotrace, "%s", z); utf8_printf(iotrace, "%s", z);
sqlite3_free(z); sqlite3_free(z);
} }
#endif #endif
@@ -416,8 +455,8 @@ static int isNumber(const char *z, int *realnum){
} }
/* /*
** A global char* and an SQL function to access its current value ** A global char* and an SQL function to access its current value
** from within an SQL statement. This program used to use the ** from within an SQL statement. This program used to use the
** sqlite_exec_printf() API to substitue a string into an SQL statement. ** sqlite_exec_printf() API to substitue a string into an SQL statement.
** The correct way to do this with sqlite3 is to use the bind API, but ** The correct way to do this with sqlite3 is to use the bind API, but
** since the shell is built around the callback paradigm it would be a lot ** since the shell is built around the callback paradigm it would be a lot
@@ -483,11 +522,10 @@ static char *local_getline(char *zLine, FILE *in){
} }
} }
#if defined(_WIN32) || defined(WIN32) #if defined(_WIN32) || defined(WIN32)
/* For interactive input on Windows systems, translate the /* For interactive input on Windows systems, translate the
** multi-byte characterset characters into UTF-8. */ ** multi-byte characterset characters into UTF-8. */
if( stdin_is_interactive ){ if( stdin_is_interactive ){
extern char *sqlite3_win32_mbcs_to_utf8(const char*); char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0);
char *zTrans = sqlite3_win32_mbcs_to_utf8(zLine);
if( zTrans ){ if( zTrans ){
int nTrans = strlen30(zTrans)+1; int nTrans = strlen30(zTrans)+1;
if( nTrans>nLine ){ if( nTrans>nLine ){
@@ -553,40 +591,7 @@ struct OpenSession {
#endif #endif
/* /*
** Render output like fprintf(). Except, if the output is going to the ** Shell output mode information from before ".explain on",
** console and if this is running on a Windows machine, translate the
** output from UTF-8 into MBCS.
*/
#if defined(_WIN32) || defined(WIN32)
void utf8_printf(FILE *out, const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
if( stdout_is_console && (out==stdout || out==stderr) ){
extern char *sqlite3_win32_utf8_to_mbcs(const char*);
char *z1 = sqlite3_vmprintf(zFormat, ap);
char *z2 = sqlite3_win32_utf8_to_mbcs(z1);
sqlite3_free(z1);
fputs(z2, out);
sqlite3_free(z2);
}else{
vfprintf(out, zFormat, ap);
}
va_end(ap);
}
#elif !defined(utf8_printf)
# define utf8_printf fprintf
#endif
/*
** Render output like fprintf(). This should not be used on anything that
** includes string formatting (e.g. "%s").
*/
#if !defined(raw_printf)
# define raw_printf fprintf
#endif
/*
** Shell output mode information from before ".explain on",
** saved so that it can be restored by ".explain off" ** saved so that it can be restored by ".explain off"
*/ */
typedef struct SavedModeInfo SavedModeInfo; typedef struct SavedModeInfo SavedModeInfo;
@@ -723,7 +728,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
static void output_quoted_string(FILE *out, const char *z){ static void output_quoted_string(FILE *out, const char *z){
int i; int i;
int nSingle = 0; int nSingle = 0;
setBinaryMode(out); setBinaryMode(out, 1);
for(i=0; z[i]; i++){ for(i=0; z[i]; i++){
if( z[i]=='\'' ) nSingle++; if( z[i]=='\'' ) nSingle++;
} }
@@ -746,7 +751,7 @@ static void output_quoted_string(FILE *out, const char *z){
} }
raw_printf(out,"'"); raw_printf(out,"'");
} }
setTextMode(out); setTextMode(out, 1);
} }
/* /*
@@ -788,11 +793,11 @@ static void output_html_string(FILE *out, const char *z){
int i; int i;
if( z==0 ) z = ""; if( z==0 ) z = "";
while( *z ){ while( *z ){
for(i=0; z[i] for(i=0; z[i]
&& z[i]!='<' && z[i]!='<'
&& z[i]!='&' && z[i]!='&'
&& z[i]!='>' && z[i]!='>'
&& z[i]!='\"' && z[i]!='\"'
&& z[i]!='\''; && z[i]!='\'';
i++){} i++){}
if( i>0 ){ if( i>0 ){
@@ -820,22 +825,22 @@ static void output_html_string(FILE *out, const char *z){
** array, then the string must be quoted for CSV. ** array, then the string must be quoted for CSV.
*/ */
static const char needCsvQuote[] = { static const char needCsvQuote[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}; };
/* /*
@@ -852,8 +857,8 @@ static void output_csv(ShellState *p, const char *z, int bSep){
int i; int i;
int nSep = strlen30(p->colSeparator); 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->colSeparator[0] && || (z[i]==p->colSeparator[0] &&
(nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){
i = 0; i = 0;
break; break;
@@ -1109,7 +1114,7 @@ static int shell_callback(
break; break;
} }
case MODE_Csv: { case MODE_Csv: {
setBinaryMode(p->out); setBinaryMode(p->out, 1);
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_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
@@ -1122,7 +1127,7 @@ static int shell_callback(
} }
utf8_printf(p->out, "%s", p->rowSeparator); utf8_printf(p->out, "%s", p->rowSeparator);
} }
setTextMode(p->out); setTextMode(p->out, 1);
break; break;
} }
case MODE_Insert: { case MODE_Insert: {
@@ -1235,7 +1240,7 @@ static void set_table_name(ShellState *p, const char *zName){
** added to zIn, and the result returned in memory obtained from malloc(). ** added to zIn, and the result returned in memory obtained from malloc().
** zIn, if it was not NULL, is freed. ** zIn, if it was not NULL, is freed.
** **
** If the third argument, quote, is not '\0', then it is used as a ** If the third argument, quote, is not '\0', then it is used as a
** quote character for zAppend. ** quote character for zAppend.
*/ */
static char *appendText(char *zIn, char const *zAppend, char quote){ static char *appendText(char *zIn, char const *zAppend, char quote){
@@ -1282,7 +1287,7 @@ static char *appendText(char *zIn, char const *zAppend, char quote){
** semicolon terminator to the end of that line. ** semicolon terminator to the end of that line.
** **
** If the number of columns is 1 and that column contains text "--" ** If the number of columns is 1 and that column contains text "--"
** then write the semicolon on a separate line. That way, if a ** then write the semicolon on a separate line. That way, if a
** "--" comment occurs at the end of the statement, the comment ** "--" comment occurs at the end of the statement, the comment
** won't consume the semicolon terminator. ** won't consume the semicolon terminator.
*/ */
@@ -1312,7 +1317,7 @@ static int run_table_dump_query(
} }
z = (const char*)sqlite3_column_text(pSelect, 0); z = (const char*)sqlite3_column_text(pSelect, 0);
utf8_printf(p->out, "%s", z); utf8_printf(p->out, "%s", z);
for(i=1; i<nResult; i++){ for(i=1; i<nResult; i++){
utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
} }
if( z==0 ) z = ""; if( z==0 ) z = "";
@@ -1321,7 +1326,7 @@ static int run_table_dump_query(
raw_printf(p->out, "\n;\n"); raw_printf(p->out, "\n;\n");
}else{ }else{
raw_printf(p->out, ";\n"); raw_printf(p->out, ";\n");
} }
rc = sqlite3_step(pSelect); rc = sqlite3_step(pSelect);
} }
rc = sqlite3_finalize(pSelect); rc = sqlite3_finalize(pSelect);
@@ -1380,7 +1385,7 @@ static void displayLinuxIoStats(FILE *out){
} }
} }
fclose(in); fclose(in);
} }
#endif #endif
@@ -1396,7 +1401,7 @@ static int display_stats(
int iHiwtr; int iHiwtr;
if( pArg && pArg->out ){ if( pArg && pArg->out ){
iHiwtr = iCur = -1; iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
raw_printf(pArg->out, raw_printf(pArg->out,
@@ -1480,18 +1485,18 @@ static int display_stats(
raw_printf(pArg->out, "Page cache hits: %d\n", iCur); raw_printf(pArg->out, "Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1; iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
raw_printf(pArg->out, "Page cache misses: %d\n", iCur); raw_printf(pArg->out, "Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1; iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
raw_printf(pArg->out, "Page cache writes: %d\n", iCur); raw_printf(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);
raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n",
iCur); 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);
raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n",
iCur); iCur);
} }
if( pArg && pArg->out && db && pArg->pStmt ){ if( pArg && pArg->out && db && pArg->pStmt ){
@@ -1553,7 +1558,7 @@ static void display_scanstats(
sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain);
utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain);
rEstLoop *= rEst; rEstLoop *= rEst;
raw_printf(pArg->out, raw_printf(pArg->out,
" nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n",
nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst
); );
@@ -1580,7 +1585,7 @@ static int str_in_array(const char *zStr, const char **azArray){
/* /*
** If compiled statement pSql appears to be an EXPLAIN statement, allocate ** If compiled statement pSql appears to be an EXPLAIN statement, allocate
** and populate the ShellState.aiIndent[] array with the number of ** and populate the ShellState.aiIndent[] array with the number of
** spaces each opcode should be indented before it is output. ** spaces each opcode should be indented before it is output.
** **
** The indenting rules are: ** The indenting rules are:
** **
@@ -1685,12 +1690,12 @@ static void explain_data_delete(ShellState *p){
} }
/* /*
** Execute a statement or set of statements. Print ** Execute a statement or set of statements. Print
** any result rows/columns depending on the current mode ** any result rows/columns depending on the current mode
** set via the supplied callback. ** set via the supplied callback.
** **
** This is very similar to SQLite's built-in sqlite3_exec() ** This is very similar to SQLite's built-in sqlite3_exec()
** function except it takes a slightly different callback ** function except it takes a slightly different callback
** and callback data argument. ** and callback data argument.
*/ */
static int shell_exec( static int shell_exec(
@@ -1762,7 +1767,7 @@ static int shell_exec(
){ ){
pArg->cMode = MODE_Explain; pArg->cMode = MODE_Explain;
} }
/* If the shell is currently in ".explain" mode, gather the extra /* If the shell is currently in ".explain" mode, gather the extra
** data required to add indents to the output.*/ ** data required to add indents to the output.*/
if( pArg->cMode==MODE_Explain ){ if( pArg->cMode==MODE_Explain ){
@@ -1788,7 +1793,7 @@ static int shell_exec(
char **azVals = &azCols[nCol]; /* Results */ char **azVals = &azCols[nCol]; /* Results */
int *aiTypes = (int *)&azVals[nCol]; /* Result types */ int *aiTypes = (int *)&azVals[nCol]; /* Result types */
int i, x; int i, x;
assert(sizeof(int) <= sizeof(char *)); assert(sizeof(int) <= sizeof(char *));
/* save off ptrs to column names */ /* save off ptrs to column names */
for(i=0; i<nCol; i++){ for(i=0; i<nCol; i++){
azCols[i] = (char *)sqlite3_column_name(pStmt, i); azCols[i] = (char *)sqlite3_column_name(pStmt, i);
@@ -1809,7 +1814,7 @@ static int shell_exec(
} /* end for */ } /* end for */
/* if data and types extracted successfully... */ /* if data and types extracted successfully... */
if( SQLITE_ROW == rc ){ if( SQLITE_ROW == rc ){
/* call the supplied callback with the result row data */ /* call the supplied callback with the result row data */
if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){ if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){
rc = SQLITE_ABORT; rc = SQLITE_ABORT;
@@ -1839,7 +1844,7 @@ static int shell_exec(
display_scanstats(db, pArg); 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. */
rc2 = sqlite3_finalize(pStmt); rc2 = sqlite3_finalize(pStmt);
@@ -1881,7 +1886,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
zTable = azArg[0]; zTable = azArg[0];
zType = azArg[1]; zType = azArg[1];
zSql = azArg[2]; zSql = azArg[2];
if( strcmp(zTable, "sqlite_sequence")==0 ){ if( strcmp(zTable, "sqlite_sequence")==0 ){
zPrepStmt = "DELETE FROM sqlite_sequence;\n"; zPrepStmt = "DELETE FROM sqlite_sequence;\n";
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
@@ -1911,7 +1916,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
char *zTableInfo = 0; char *zTableInfo = 0;
char *zTmp = 0; char *zTmp = 0;
int nRow = 0; int nRow = 0;
zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
zTableInfo = appendText(zTableInfo, zTable, '"'); zTableInfo = appendText(zTableInfo, zTable, '"');
zTableInfo = appendText(zTableInfo, ");", 0); zTableInfo = appendText(zTableInfo, ");", 0);
@@ -1970,7 +1975,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
** "ORDER BY rowid DESC" to the end. ** "ORDER BY rowid DESC" to the end.
*/ */
static int run_schema_dump_query( static int run_schema_dump_query(
ShellState *p, ShellState *p,
const char *zQuery const char *zQuery
){ ){
int rc; int rc;
@@ -2085,7 +2090,7 @@ static char zHelp[] =
** Print help information for the ".sessions" command ** Print help information for the ".sessions" command
*/ */
void session_help(ShellState *p){ void session_help(ShellState *p){
fprintf(p->out, raw_printf(p->out,
".session ?NAME? SUBCOMMAND ?ARGS...?\n" ".session ?NAME? SUBCOMMAND ?ARGS...?\n"
"If ?NAME? is omitted, the first defined session is used.\n" "If ?NAME? is omitted, the first defined session is used.\n"
"Subcommands:\n" "Subcommands:\n"
@@ -2093,7 +2098,7 @@ void session_help(ShellState *p){
" changeset FILE Write a changeset into FILE\n" " changeset FILE Write a changeset into FILE\n"
" close Close one session\n" " close Close one session\n"
" enable ?BOOLEAN? Set or query the enable bit\n" " enable ?BOOLEAN? Set or query the enable bit\n"
" filter GLOB... Reject tables matching GLOBs\n" " filter GLOB... Reject tables matching GLOBs\n"
" indirect ?BOOLEAN? Mark or query the indirect status\n" " indirect ?BOOLEAN? Mark or query the indirect status\n"
" isempty Query whether the session is empty\n" " isempty Query whether the session is empty\n"
" list List currently open session names\n" " list List currently open session names\n"
@@ -2230,7 +2235,7 @@ static void open_db(ShellState *p, int keepAlive){
shellstaticFunc, 0, 0); shellstaticFunc, 0, 0);
} }
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(p->db)); p->zDbFilename, sqlite3_errmsg(p->db));
if( keepAlive ) return; if( keepAlive ) return;
exit(1); exit(1);
@@ -2394,7 +2399,7 @@ static void output_file_close(FILE *f){
/* /*
** Try to open an output file. The names "stdout" and "stderr" are ** Try to open an output file. The names "stdout" and "stderr" are
** recognized and do the right thing. NULL is returned if the output ** recognized and do the right thing. NULL is returned if the output
** filename is "off". ** filename is "off".
*/ */
static FILE *output_file_open(const char *zFile){ static FILE *output_file_open(const char *zFile){
@@ -2583,7 +2588,7 @@ static void tryToCloneData(
sqlite3 *newDb, sqlite3 *newDb,
const char *zTable const char *zTable
){ ){
sqlite3_stmt *pQuery = 0; sqlite3_stmt *pQuery = 0;
sqlite3_stmt *pInsert = 0; sqlite3_stmt *pInsert = 0;
char *zQuery = 0; char *zQuery = 0;
char *zInsert = 0; char *zInsert = 0;
@@ -2887,9 +2892,9 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
utf8_printf(p->out, "%-20s %u", aField[i].zName, val); utf8_printf(p->out, "%-20s %u", aField[i].zName, val);
switch( ofst ){ switch( ofst ){
case 56: { case 56: {
if( val==1 ) raw_printf(p->out, " (utf8)"); if( val==1 ) raw_printf(p->out, " (utf8)");
if( val==2 ) raw_printf(p->out, " (utf16le)"); if( val==2 ) raw_printf(p->out, " (utf16le)");
if( val==3 ) raw_printf(p->out, " (utf16be)"); if( val==3 ) raw_printf(p->out, " (utf16be)");
} }
} }
raw_printf(p->out, "\n"); raw_printf(p->out, "\n");
@@ -2949,9 +2954,9 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zLine[h]=='\'' || zLine[h]=='"' ){ if( zLine[h]=='\'' || zLine[h]=='"' ){
int delim = zLine[h++]; int delim = zLine[h++];
azArg[nArg++] = &zLine[h]; azArg[nArg++] = &zLine[h];
while( zLine[h] && zLine[h]!=delim ){ while( zLine[h] && zLine[h]!=delim ){
if( zLine[h]=='\\' && delim=='"' && zLine[h+1]!=0 ) h++; if( zLine[h]=='\\' && delim=='"' && zLine[h+1]!=0 ) h++;
h++; h++;
} }
if( zLine[h]==delim ){ if( zLine[h]==delim ){
zLine[h++] = 0; zLine[h++] = 0;
@@ -3053,9 +3058,9 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='b' && n>=3 && strncmp(azArg[0], "binary", n)==0 ){ if( c=='b' && n>=3 && strncmp(azArg[0], "binary", n)==0 ){
if( nArg==2 ){ if( nArg==2 ){
if( booleanValue(azArg[1]) ){ if( booleanValue(azArg[1]) ){
setBinaryMode(p->out); setBinaryMode(p->out, 1);
}else{ }else{
setTextMode(p->out); setTextMode(p->out, 1);
} }
}else{ }else{
raw_printf(stderr, "Usage: .binary on|off\n"); raw_printf(stderr, "Usage: .binary on|off\n");
@@ -3127,11 +3132,11 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
p->nErr = 0; p->nErr = 0;
if( nArg==1 ){ if( nArg==1 ){
run_schema_dump_query(p, run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master " "SELECT name, type, sql FROM sqlite_master "
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
); );
run_schema_dump_query(p, run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master " "SELECT name, type, sql FROM sqlite_master "
"WHERE name=='sqlite_sequence'" "WHERE name=='sqlite_sequence'"
); );
@@ -3180,7 +3185,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{ }else{
raw_printf(stderr, "Usage: .eqp on|off\n"); raw_printf(stderr, "Usage: .eqp on|off\n");
rc = 1; rc = 1;
} }
}else }else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
@@ -3562,7 +3567,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0); open_db(p, 0);
if( nArg==1 ){ if( nArg==1 ){
for(i=0; i<ArraySize(aLimit); i++){ for(i=0; i<ArraySize(aLimit); i++){
printf("%20s %d\n", aLimit[i].zLimitName, printf("%20s %d\n", aLimit[i].zLimitName,
sqlite3_limit(p->db, aLimit[i].limitCode, -1)); sqlite3_limit(p->db, aLimit[i].limitCode, -1));
} }
}else if( nArg>3 ){ }else if( nArg>3 ){
@@ -3966,11 +3971,11 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCmd!=2 ) goto session_syntax_error; if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ){ if( pSession->p==0 ){
session_not_open: session_not_open:
fprintf(stderr, "ERROR: No sessions are open\n"); raw_printf(stderr, "ERROR: No sessions are open\n");
}else{ }else{
rc = sqlite3session_attach(pSession->p, azCmd[1]); rc = sqlite3session_attach(pSession->p, azCmd[1]);
if( rc ){ if( rc ){
fprintf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); raw_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc);
rc = 0; rc = 0;
} }
} }
@@ -3986,7 +3991,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( pSession->p==0 ) goto session_not_open; if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb"); out = fopen(azCmd[1], "wb");
if( out==0 ){ if( out==0 ){
fprintf(stderr, "ERROR: cannot open \"%s\" for writing\n", azCmd[1]); utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n", azCmd[1]);
}else{ }else{
int szChng; int szChng;
void *pChng; void *pChng;
@@ -3999,9 +4004,9 @@ static int do_meta_command(char *zLine, ShellState *p){
printf("Error: error code %d\n", rc); printf("Error: error code %d\n", rc);
rc = 0; rc = 0;
} }
if( pChng if( pChng
&& fwrite(pChng, szChng, 1, out)!=1 ){ && fwrite(pChng, szChng, 1, out)!=1 ){
fprintf(stderr, "ERROR: Failed to write entire %d-byte output\n", raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n",
szChng); szChng);
} }
sqlite3_free(pChng); sqlite3_free(pChng);
@@ -4029,7 +4034,8 @@ static int do_meta_command(char *zLine, ShellState *p){
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( p->nSession ){ if( p->nSession ){
ii = sqlite3session_enable(pSession->p, ii); ii = sqlite3session_enable(pSession->p, ii);
fprintf(p->out, "session %s enable flag = %d\n", pSession->zName, ii); utf8_printf(p->out, "session %s enable flag = %d\n",
pSession->zName, ii);
} }
}else }else
@@ -4047,11 +4053,11 @@ static int do_meta_command(char *zLine, ShellState *p){
nByte = sizeof(pSession->azFilter[0])*(nCmd-1); nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
pSession->azFilter = sqlite3_malloc( nByte ); pSession->azFilter = sqlite3_malloc( nByte );
if( pSession->azFilter==0 ){ if( pSession->azFilter==0 ){
fprintf(stderr, "Error: out or memory\n"); raw_printf(stderr, "Error: out or memory\n");
exit(1); exit(1);
} }
for(ii=1; ii<nCmd; ii++){ for(ii=1; ii<nCmd; ii++){
pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]); pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
} }
pSession->nFilter = ii-1; pSession->nFilter = ii-1;
} }
@@ -4066,7 +4072,8 @@ static int do_meta_command(char *zLine, ShellState *p){
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( p->nSession ){ if( p->nSession ){
ii = sqlite3session_indirect(pSession->p, ii); ii = sqlite3session_indirect(pSession->p, ii);
fprintf(p->out, "session %s indirect flag = %d\n", pSession->zName,ii); utf8_printf(p->out, "session %s indirect flag = %d\n",
pSession->zName, ii);
} }
}else }else
@@ -4078,7 +4085,8 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCmd!=1 ) goto session_syntax_error; if( nCmd!=1 ) goto session_syntax_error;
if( p->nSession ){ if( p->nSession ){
ii = sqlite3session_isempty(pSession->p); ii = sqlite3session_isempty(pSession->p);
fprintf(p->out, "session %s isempty flag = %d\n", pSession->zName, ii); utf8_printf(p->out, "session %s isempty flag = %d\n",
pSession->zName, ii);
} }
}else }else
@@ -4087,7 +4095,7 @@ static int do_meta_command(char *zLine, ShellState *p){
*/ */
if( strcmp(azCmd[0],"list")==0 ){ if( strcmp(azCmd[0],"list")==0 ){
for(i=0; i<p->nSession; i++){ for(i=0; i<p->nSession; i++){
fprintf(p->out, "%d %s\n", i, p->aSession[i].zName); utf8_printf(p->out, "%d %s\n", i, p->aSession[i].zName);
} }
}else }else
@@ -4102,18 +4110,18 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zName[0]==0 ) goto session_syntax_error; if( zName[0]==0 ) goto session_syntax_error;
for(i=0; i<p->nSession; i++){ for(i=0; i<p->nSession; i++){
if( strcmp(p->aSession[i].zName,zName)==0 ){ if( strcmp(p->aSession[i].zName,zName)==0 ){
fprintf(stderr, "Session \"%s\" already exists\n", zName); utf8_printf(stderr, "Session \"%s\" already exists\n", zName);
goto meta_command_exit; goto meta_command_exit;
} }
} }
if( p->nSession>=ArraySize(p->aSession) ){ if( p->nSession>=ArraySize(p->aSession) ){
fprintf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession)); raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession));
goto meta_command_exit; goto meta_command_exit;
} }
pSession = &p->aSession[p->nSession]; pSession = &p->aSession[p->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){ if( rc ){
fprintf(stderr, "Cannot open session: error code=%d\n", rc); raw_printf(stderr, "Cannot open session: error code=%d\n", rc);
rc = 0; rc = 0;
goto meta_command_exit; goto meta_command_exit;
} }
@@ -4382,9 +4390,9 @@ static int do_meta_command(char *zLine, ShellState *p){
/* sqlite3_test_control(int, db, int) */ /* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_OPTIMIZATIONS: case SQLITE_TESTCTRL_OPTIMIZATIONS:
case SQLITE_TESTCTRL_RESERVE: case SQLITE_TESTCTRL_RESERVE:
if( nArg==3 ){ if( nArg==3 ){
int opt = (int)strtol(azArg[2], 0, 0); int opt = (int)strtol(azArg[2], 0, 0);
rc2 = sqlite3_test_control(testctrl, p->db, opt); rc2 = sqlite3_test_control(testctrl, p->db, opt);
raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
} else { } else {
@@ -4408,7 +4416,7 @@ static int do_meta_command(char *zLine, ShellState *p){
break; break;
/* sqlite3_test_control(int, uint) */ /* sqlite3_test_control(int, uint) */
case SQLITE_TESTCTRL_PENDING_BYTE: case SQLITE_TESTCTRL_PENDING_BYTE:
if( nArg==3 ){ if( nArg==3 ){
unsigned int opt = (unsigned int)integerValue(azArg[2]); unsigned int opt = (unsigned int)integerValue(azArg[2]);
rc2 = sqlite3_test_control(testctrl, opt); rc2 = sqlite3_test_control(testctrl, opt);
@@ -4418,13 +4426,13 @@ static int do_meta_command(char *zLine, ShellState *p){
" int option\n", azArg[1]); " int option\n", azArg[1]);
} }
break; break;
/* sqlite3_test_control(int, int) */ /* sqlite3_test_control(int, int) */
case SQLITE_TESTCTRL_ASSERT: case SQLITE_TESTCTRL_ASSERT:
case SQLITE_TESTCTRL_ALWAYS: case SQLITE_TESTCTRL_ALWAYS:
case SQLITE_TESTCTRL_NEVER_CORRUPT: case SQLITE_TESTCTRL_NEVER_CORRUPT:
if( nArg==3 ){ if( nArg==3 ){
int opt = booleanValue(azArg[2]); int opt = booleanValue(azArg[2]);
rc2 = sqlite3_test_control(testctrl, opt); rc2 = sqlite3_test_control(testctrl, opt);
raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
} else { } else {
@@ -4435,9 +4443,9 @@ static int do_meta_command(char *zLine, ShellState *p){
/* sqlite3_test_control(int, char *) */ /* sqlite3_test_control(int, char *) */
#ifdef SQLITE_N_KEYWORD #ifdef SQLITE_N_KEYWORD
case SQLITE_TESTCTRL_ISKEYWORD: case SQLITE_TESTCTRL_ISKEYWORD:
if( nArg==3 ){ if( nArg==3 ){
const char *opt = azArg[2]; const char *opt = azArg[2];
rc2 = sqlite3_test_control(testctrl, opt); rc2 = sqlite3_test_control(testctrl, opt);
raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
} else { } else {
@@ -4450,7 +4458,7 @@ static int do_meta_command(char *zLine, ShellState *p){
case SQLITE_TESTCTRL_IMPOSTER: case SQLITE_TESTCTRL_IMPOSTER:
if( nArg==5 ){ if( nArg==5 ){
rc2 = sqlite3_test_control(testctrl, p->db, rc2 = sqlite3_test_control(testctrl, p->db,
azArg[2], azArg[2],
integerValue(azArg[3]), integerValue(azArg[3]),
integerValue(azArg[4])); integerValue(azArg[4]));
@@ -4460,10 +4468,10 @@ static int do_meta_command(char *zLine, ShellState *p){
} }
break; break;
case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_BITVEC_TEST:
case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_FAULT_INSTALL:
case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS:
case SQLITE_TESTCTRL_SCRATCHMALLOC: case SQLITE_TESTCTRL_SCRATCHMALLOC:
default: default:
utf8_printf(stderr, utf8_printf(stderr,
"Error: CLI support for testctrl %s not implemented\n", "Error: CLI support for testctrl %s not implemented\n",
@@ -4477,7 +4485,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0); open_db(p, 0);
sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0);
}else }else
if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){ if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){
if( nArg==2 ){ if( nArg==2 ){
enableTimer = booleanValue(azArg[1]); enableTimer = booleanValue(azArg[1]);
@@ -4490,7 +4498,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1; rc = 1;
} }
}else }else
if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){ if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){
open_db(p, 0); open_db(p, 0);
if( nArg!=2 ){ if( nArg!=2 ){
@@ -4570,7 +4578,7 @@ static int do_meta_command(char *zLine, ShellState *p){
raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n");
rc = 1; rc = 1;
goto meta_command_exit; goto meta_command_exit;
} }
}else }else
#endif /* SQLITE_USER_AUTHENTICATION */ #endif /* SQLITE_USER_AUTHENTICATION */
@@ -4802,7 +4810,7 @@ static int process_input(ShellState *p, FILE *in){
if( rc || zErrMsg ){ if( rc || zErrMsg ){
char zPrefix[100]; char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){ if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix, sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"Error: near line %d:", startline); "Error: near line %d:", startline);
}else{ }else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
@@ -4944,7 +4952,7 @@ static void process_sqliterc(
/* /*
** 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" " -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"
@@ -4981,7 +4989,7 @@ static const char zOptions[] =
; ;
static void usage(int showDetail){ static void usage(int showDetail){
utf8_printf(stderr, utf8_printf(stderr,
"Usage: %s [OPTIONS] FILENAME [SQL]\n" "Usage: %s [OPTIONS] FILENAME [SQL]\n"
"FILENAME is the name of an SQLite database. A new database is created\n" "FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist.\n", Argv0); "if the file does not previously exist.\n", Argv0);
if( showDetail ){ if( showDetail ){
@@ -5043,7 +5051,20 @@ static char *cmdline_option_value(int argc, char **argv, int i){
return argv[i]; return argv[i];
} }
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER)
# define SQLITE_SHELL_IS_UTF8 (0)
# else
# define SQLITE_SHELL_IS_UTF8 (1)
# endif
#endif
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL main(int argc, char **argv){ int SQLITE_CDECL main(int argc, char **argv){
#else
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char **argv;
#endif
char *zErrMsg = 0; char *zErrMsg = 0;
ShellState data; ShellState data;
const char *zInitFile = 0; const char *zInitFile = 0;
@@ -5054,6 +5075,11 @@ int SQLITE_CDECL main(int argc, char **argv){
int nCmd = 0; int nCmd = 0;
char **azCmd = 0; char **azCmd = 0;
setBinaryMode(stdin, 0);
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
#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 ){
utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
@@ -5061,12 +5087,24 @@ int SQLITE_CDECL main(int argc, char **argv){
exit(1); exit(1);
} }
#endif #endif
setBinaryMode(stdin);
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
Argv0 = argv[0];
main_init(&data); main_init(&data);
stdin_is_interactive = isatty(0); #if !SQLITE_SHELL_IS_UTF8
stdout_is_console = isatty(1); sqlite3_initialize();
argv = sqlite3_malloc64(sizeof(argv[0])*argc);
if( argv==0 ){
raw_printf(stderr, "out of memory\n");
exit(1);
}
for(i=0; i<argc; i++){
argv[i] = sqlite3_win32_unicode_to_utf8(wargv[i]);
if( argv[i]==0 ){
raw_printf(stderr, "out of memory\n");
exit(1);
}
}
#endif
assert( argc>=1 && argv && argv[0] );
Argv0 = argv[0];
/* Make sure we have a valid signal handler early, before anything /* Make sure we have a valid signal handler early, before anything
** else is done. ** else is done.
@@ -5122,7 +5160,7 @@ int SQLITE_CDECL main(int argc, char **argv){
zInitFile = cmdline_option_value(argc, argv, ++i); zInitFile = cmdline_option_value(argc, argv, ++i);
}else if( strcmp(z,"-batch")==0 ){ }else if( strcmp(z,"-batch")==0 ){
/* Need to check for batch mode here to so we can avoid printing /* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before ** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass. ** we do the actual processing of arguments later in a second pass.
*/ */
stdin_is_interactive = 0; stdin_is_interactive = 0;
@@ -5397,6 +5435,10 @@ int SQLITE_CDECL main(int argc, char **argv){
session_close_all(&data); session_close_all(&data);
sqlite3_close(data.db); sqlite3_close(data.db);
} }
sqlite3_free(data.zFreeOnClose); sqlite3_free(data.zFreeOnClose);
#if !SQLITE_SHELL_IS_UTF8
for(i=0; i<argc; i++) sqlite3_free(argv[i]);
sqlite3_free(argv);
#endif
return rc; return rc;
} }

View File

@@ -76,20 +76,20 @@ do_test shell1-1.3.3 {
# -echo print commands before execution # -echo print commands before execution
do_test shell1-1.4.1 { do_test shell1-1.4.1 {
catchcmd "-echo test.db" "" catchcmd "-echo test.db" ""
} {0 {}} } {0 {}}
# -[no]header turn headers on or off # -[no]header turn headers on or off
do_test shell1-1.5.1 { do_test shell1-1.5.1 {
catchcmd "-header test.db" "" catchcmd "-header test.db" ""
} {0 {}} } {0 {}}
do_test shell1-1.5.2 { do_test shell1-1.5.2 {
catchcmd "-noheader test.db" "" catchcmd "-noheader test.db" ""
} {0 {}} } {0 {}}
# -bail stop after hitting an error # -bail stop after hitting an error
do_test shell1-1.6.1 { do_test shell1-1.6.1 {
catchcmd "-bail test.db" "" catchcmd "-bail test.db" ""
} {0 {}} } {0 {}}
# -interactive force interactive I/O # -interactive force interactive I/O
@@ -103,40 +103,40 @@ do_test shell1-1.7.1 {
# -batch force batch I/O # -batch force batch I/O
do_test shell1-1.8.1 { do_test shell1-1.8.1 {
catchcmd "-batch test.db" "" catchcmd "-batch test.db" ""
} {0 {}} } {0 {}}
# -column set output mode to 'column' # -column set output mode to 'column'
do_test shell1-1.9.1 { do_test shell1-1.9.1 {
catchcmd "-column test.db" "" catchcmd "-column test.db" ""
} {0 {}} } {0 {}}
# -csv set output mode to 'csv' # -csv set output mode to 'csv'
do_test shell1-1.10.1 { do_test shell1-1.10.1 {
catchcmd "-csv test.db" "" catchcmd "-csv test.db" ""
} {0 {}} } {0 {}}
# -html set output mode to HTML # -html set output mode to HTML
do_test shell1-1.11.1 { do_test shell1-1.11.1 {
catchcmd "-html test.db" "" catchcmd "-html test.db" ""
} {0 {}} } {0 {}}
# -line set output mode to 'line' # -line set output mode to 'line'
do_test shell1-1.12.1 { do_test shell1-1.12.1 {
catchcmd "-line test.db" "" catchcmd "-line test.db" ""
} {0 {}} } {0 {}}
# -list set output mode to 'list' # -list set output mode to 'list'
do_test shell1-1.13.1 { do_test shell1-1.13.1 {
catchcmd "-list test.db" "" catchcmd "-list test.db" ""
} {0 {}} } {0 {}}
# -separator 'x' set output field separator (|) # -separator 'x' set output field separator (|)
do_test shell1-1.14.1 { do_test shell1-1.14.1 {
catchcmd "-separator 'x' test.db" "" catchcmd "-separator 'x' test.db" ""
} {0 {}} } {0 {}}
do_test shell1-1.14.2 { do_test shell1-1.14.2 {
catchcmd "-separator x test.db" "" catchcmd "-separator x test.db" ""
} {0 {}} } {0 {}}
do_test shell1-1.14.3 { do_test shell1-1.14.3 {
set res [catchcmd "-separator" ""] set res [catchcmd "-separator" ""]
@@ -147,7 +147,7 @@ do_test shell1-1.14.3 {
# -stats print memory stats before each finalize # -stats print memory stats before each finalize
do_test shell1-1.14b.1 { do_test shell1-1.14b.1 {
catchcmd "-stats test.db" "" catchcmd "-stats test.db" ""
} {0 {}} } {0 {}}
# -nullvalue 'text' set text string for NULL values # -nullvalue 'text' set text string for NULL values
@@ -175,7 +175,7 @@ do_test shell1-1.16.1 {
# check first token handling # check first token handling
do_test shell1-2.1.1 { do_test shell1-2.1.1 {
catchcmd "test.db" ".foo" catchcmd "test.db" ".foo"
} {1 {Error: unknown command or invalid arguments: "foo". Enter ".help" for help}} } {1 {Error: unknown command or invalid arguments: "foo". Enter ".help" for help}}
do_test shell1-2.1.2 { do_test shell1-2.1.2 {
catchcmd "test.db" ".\"foo OFF\"" catchcmd "test.db" ".\"foo OFF\""
@@ -910,4 +910,41 @@ do_test shell1-5.0 {
} }
} {} } {}
# The string used here is the word "test" in Chinese.
# In UTF-8, it is encoded as: \xE6\xB5\x8B\xE8\xAF\x95
set test \u6D4B\u8BD5
do_test shell1-6.0 {
set fileName $test; append fileName .db
catch {forcedelete $fileName}
set x [catchcmdex $fileName "CREATE TABLE t1(x);\n.schema\n"]
set code [lindex $x 0]
set res [string trim [lindex $x 1]]
if {$code ne "0"} {
error "failed with error: $res"
}
if {$res ne "CREATE TABLE t1(x);"} {
error "failed with mismatch: $res"
}
if {![file exists $fileName]} {
error "file \"$fileName\" (Unicode) does not exist"
}
forcedelete $fileName
} {}
do_test shell1-6.1 {
catch {forcedelete test3.db}
set x [catchcmdex test3.db \
"CREATE TABLE [encoding convertto utf-8 $test](x);\n.schema\n"]
set code [lindex $x 0]
set res [string trim [lindex $x 1]]
if {$code ne "0"} {
error "failed with error: $res"
}
if {$res ne "CREATE TABLE ${test}(x);"} {
error "failed with mismatch: $res"
}
forcedelete test3.db
} {}
finish_test finish_test