mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-05 04:30:38 +03:00
Merge all the latest enhancements from trunk.
FossilOrigin-Name: ae7eef117f28a5dae7a05805f2d31ac532a9fcc5
This commit is contained in:
176
src/shell.c
176
src/shell.c
@@ -106,6 +106,26 @@ extern int pclose(FILE*);
|
||||
#define IsDigit(X) isdigit((unsigned char)X)
|
||||
#define ToLower(X) (char)tolower((unsigned char)X)
|
||||
|
||||
/* On Windows, we normally run with output mode of TEXT so that \n characters
|
||||
** are automatically translated into \r\n. However, this behavior needs
|
||||
** to be disabled in some cases (ex: when generating CSV output and when
|
||||
** rendering quoted strings that contain \n characters). The following
|
||||
** routines take care of that.
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
static void setBinaryMode(FILE *out){
|
||||
fflush(out);
|
||||
_setmode(_fileno(out), _O_BINARY);
|
||||
}
|
||||
static void setTextMode(FILE *out){
|
||||
fflush(out);
|
||||
_setmode(_fileno(out), _O_TEXT);
|
||||
}
|
||||
#else
|
||||
# define setBinaryMode(X)
|
||||
# define setTextMode(X)
|
||||
#endif
|
||||
|
||||
|
||||
/* True if the timer is enabled */
|
||||
static int enableTimer = 0;
|
||||
@@ -601,6 +621,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
|
||||
static void output_quoted_string(FILE *out, const char *z){
|
||||
int i;
|
||||
int nSingle = 0;
|
||||
setBinaryMode(out);
|
||||
for(i=0; z[i]; i++){
|
||||
if( z[i]=='\'' ) nSingle++;
|
||||
}
|
||||
@@ -623,6 +644,7 @@ static void output_quoted_string(FILE *out, const char *z){
|
||||
}
|
||||
fprintf(out,"'");
|
||||
}
|
||||
setTextMode(out);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -925,10 +947,7 @@ static int shell_callback(
|
||||
break;
|
||||
}
|
||||
case MODE_Csv: {
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
fflush(p->out);
|
||||
_setmode(_fileno(p->out), _O_BINARY);
|
||||
#endif
|
||||
setBinaryMode(p->out);
|
||||
if( p->cnt++==0 && p->showHeader ){
|
||||
for(i=0; i<nArg; i++){
|
||||
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
|
||||
@@ -941,10 +960,7 @@ static int shell_callback(
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
}
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
fflush(p->out);
|
||||
_setmode(_fileno(p->out), _O_TEXT);
|
||||
#endif
|
||||
setTextMode(p->out);
|
||||
break;
|
||||
}
|
||||
case MODE_Insert: {
|
||||
@@ -1734,6 +1750,7 @@ static char zHelp[] =
|
||||
".bail on|off Stop after hitting an error. Default OFF\n"
|
||||
".clone NEWDB Clone data into NEWDB from the existing database\n"
|
||||
".databases List names and files of attached databases\n"
|
||||
".dbinfo ?DB? Show status information about the database\n"
|
||||
".dump ?TABLE? ... Dump the database in an SQL text format\n"
|
||||
" If TABLE specified, only dump tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
@@ -1746,8 +1763,8 @@ static char zHelp[] =
|
||||
".headers on|off Turn display of headers on or off\n"
|
||||
".help Show this message\n"
|
||||
".import FILE TABLE Import data from FILE into TABLE\n"
|
||||
".indices ?TABLE? Show names of all indices\n"
|
||||
" If TABLE specified, only show indices for tables\n"
|
||||
".indexes ?TABLE? Show names of all indexes\n"
|
||||
" If TABLE specified, only show indexes for tables\n"
|
||||
" matching LIKE pattern TABLE.\n"
|
||||
#ifdef SQLITE_ENABLE_IOTRACE
|
||||
".iotrace FILE Enable I/O diagnostic logging to FILE\n"
|
||||
@@ -2499,6 +2516,115 @@ static void output_reset(ShellState *p){
|
||||
p->out = stdout;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run an SQL command and return the single integer result.
|
||||
*/
|
||||
static int db_int(ShellState *p, const char *zSql){
|
||||
sqlite3_stmt *pStmt;
|
||||
int res = 0;
|
||||
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
res = sqlite3_column_int(pStmt,0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a 2-byte or 4-byte big-endian integer into a native integer
|
||||
*/
|
||||
unsigned int get2byteInt(unsigned char *a){
|
||||
return (a[0]<<8) + a[1];
|
||||
}
|
||||
unsigned int get4byteInt(unsigned char *a){
|
||||
return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the ".info" command.
|
||||
**
|
||||
** Return 1 on error, 2 to exit, and 0 otherwise.
|
||||
*/
|
||||
static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
|
||||
static const struct { const char *zName; int ofst; } aField[] = {
|
||||
{ "file change counter:", 24 },
|
||||
{ "database page count:", 28 },
|
||||
{ "freelist page count:", 36 },
|
||||
{ "schema cookie:", 40 },
|
||||
{ "schema format:", 44 },
|
||||
{ "default cache size:", 48 },
|
||||
{ "autovacuum top root:", 52 },
|
||||
{ "incremental vacuum:", 64 },
|
||||
{ "text encoding:", 56 },
|
||||
{ "user version:", 60 },
|
||||
{ "application id:", 68 },
|
||||
{ "software version:", 96 },
|
||||
};
|
||||
static const struct { const char *zName; const char *zSql; } aQuery[] = {
|
||||
{ "number of tables:",
|
||||
"SELECT count(*) FROM %s WHERE type='table'" },
|
||||
{ "number of indexes:",
|
||||
"SELECT count(*) FROM %s WHERE type='index'" },
|
||||
{ "number of triggers:",
|
||||
"SELECT count(*) FROM %s WHERE type='trigger'" },
|
||||
{ "number of views:",
|
||||
"SELECT count(*) FROM %s WHERE type='view'" },
|
||||
{ "schema size:",
|
||||
"SELECT total(length(sql)) FROM %s" },
|
||||
};
|
||||
sqlite3_file *pFile;
|
||||
int i;
|
||||
char *zSchemaTab;
|
||||
char *zDb = nArg>=2 ? azArg[1] : "main";
|
||||
unsigned char aHdr[100];
|
||||
open_db(p, 0);
|
||||
if( p->db==0 ) return 1;
|
||||
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile);
|
||||
if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){
|
||||
return 1;
|
||||
}
|
||||
i = pFile->pMethods->xRead(pFile, aHdr, 100, 0);
|
||||
if( i!=SQLITE_OK ){
|
||||
fprintf(stderr, "unable to read database header\n");
|
||||
return 1;
|
||||
}
|
||||
i = get2byteInt(aHdr+16);
|
||||
if( i==1 ) i = 65536;
|
||||
fprintf(p->out, "%-20s %d\n", "database page size:", i);
|
||||
fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
|
||||
fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]);
|
||||
fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]);
|
||||
for(i=0; i<sizeof(aField)/sizeof(aField[0]); i++){
|
||||
int ofst = aField[i].ofst;
|
||||
unsigned int val = get4byteInt(aHdr + ofst);
|
||||
fprintf(p->out, "%-20s %u", aField[i].zName, val);
|
||||
switch( ofst ){
|
||||
case 56: {
|
||||
if( val==1 ) fprintf(p->out, " (utf8)");
|
||||
if( val==2 ) fprintf(p->out, " (utf16le)");
|
||||
if( val==3 ) fprintf(p->out, " (utf16be)");
|
||||
}
|
||||
}
|
||||
fprintf(p->out, "\n");
|
||||
}
|
||||
if( zDb==0 ){
|
||||
zSchemaTab = sqlite3_mprintf("main.sqlite_master");
|
||||
}else if( strcmp(zDb,"temp")==0 ){
|
||||
zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_master");
|
||||
}else{
|
||||
zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_master", zDb);
|
||||
}
|
||||
for(i=0; i<sizeof(aQuery)/sizeof(aQuery[0]); i++){
|
||||
char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
|
||||
int val = db_int(p, zSql);
|
||||
sqlite3_free(zSql);
|
||||
fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
|
||||
}
|
||||
sqlite3_free(zSchemaTab);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If an input line begins with "." then invoke this routine to
|
||||
** process that line.
|
||||
@@ -2641,6 +2767,10 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){
|
||||
rc = shell_dbinfo_command(p, nArg, azArg);
|
||||
}else
|
||||
|
||||
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
|
||||
open_db(p, 0);
|
||||
/* When playing back a "dump", the content might appear in an order
|
||||
@@ -3009,7 +3139,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
}else
|
||||
|
||||
if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){
|
||||
if( c=='i' && (strncmp(azArg[0], "indices", n)==0
|
||||
|| strncmp(azArg[0], "indexes", n)==0) ){
|
||||
ShellState data;
|
||||
char *zErrMsg = 0;
|
||||
open_db(p, 0);
|
||||
@@ -3039,7 +3170,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
);
|
||||
zShellStatic = 0;
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .indices ?LIKE-PATTERN?\n");
|
||||
fprintf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
@@ -3402,7 +3533,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
|
||||
extern int sqlite3SelectTrace;
|
||||
sqlite3SelectTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
|
||||
sqlite3SelectTrace = integerValue(azArg[1]);
|
||||
}else
|
||||
#endif
|
||||
|
||||
@@ -3800,6 +3931,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
{ "iskeyword", SQLITE_TESTCTRL_ISKEYWORD },
|
||||
{ "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC },
|
||||
{ "byteorder", SQLITE_TESTCTRL_BYTEORDER },
|
||||
{ "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT },
|
||||
{ "imposter", SQLITE_TESTCTRL_IMPOSTER },
|
||||
};
|
||||
int testctrl = -1;
|
||||
int rc = 0;
|
||||
@@ -3866,7 +3999,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
/* sqlite3_test_control(int, int) */
|
||||
case SQLITE_TESTCTRL_ASSERT:
|
||||
case SQLITE_TESTCTRL_ALWAYS:
|
||||
case SQLITE_TESTCTRL_ALWAYS:
|
||||
case SQLITE_TESTCTRL_NEVER_CORRUPT:
|
||||
if( nArg==3 ){
|
||||
int opt = booleanValue(azArg[2]);
|
||||
rc = sqlite3_test_control(testctrl, opt);
|
||||
@@ -3891,6 +4025,18 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SQLITE_TESTCTRL_IMPOSTER:
|
||||
if( nArg==5 ){
|
||||
rc = sqlite3_test_control(testctrl, p->db,
|
||||
azArg[2],
|
||||
integerValue(azArg[3]),
|
||||
integerValue(azArg[4]));
|
||||
}else{
|
||||
fprintf(stderr,"Usage: .testctrl initmode dbName onoff tnum\n");
|
||||
rc = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SQLITE_TESTCTRL_BITVEC_TEST:
|
||||
case SQLITE_TESTCTRL_FAULT_INSTALL:
|
||||
case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS:
|
||||
@@ -4457,6 +4603,8 @@ int main(int argc, char **argv){
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
setBinaryMode(stdin);
|
||||
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
|
||||
Argv0 = argv[0];
|
||||
main_init(&data);
|
||||
stdin_is_interactive = isatty(0);
|
||||
|
||||
Reference in New Issue
Block a user