mirror of
				https://github.com/sqlite/sqlite.git
				synced 2025-11-03 16:53:36 +03:00 
			
		
		
		
	Merge updates from trunk.
FossilOrigin-Name: acf7684323da4dc3aaf9746bd13b0f56054a17dd
This commit is contained in:
		
							
								
								
									
										197
									
								
								src/shell.c
									
									
									
									
									
								
							
							
						
						
									
										197
									
								
								src/shell.c
									
									
									
									
									
								
							@@ -59,18 +59,38 @@
 | 
			
		||||
# include <readline/readline.h>
 | 
			
		||||
# include <readline/history.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if HAVE_EDITLINE
 | 
			
		||||
# undef HAVE_READLINE
 | 
			
		||||
# define HAVE_READLINE 1
 | 
			
		||||
# include <editline/readline.h>
 | 
			
		||||
#endif
 | 
			
		||||
#if !HAVE_READLINE
 | 
			
		||||
# define add_history(X)
 | 
			
		||||
# define read_history(X)
 | 
			
		||||
# define write_history(X)
 | 
			
		||||
# define stifle_history(X)
 | 
			
		||||
 | 
			
		||||
#if HAVE_EDITLINE || HAVE_READLINE
 | 
			
		||||
 | 
			
		||||
# define shell_add_history(X) add_history(X)
 | 
			
		||||
# define shell_read_history(X) read_history(X)
 | 
			
		||||
# define shell_write_history(X) write_history(X)
 | 
			
		||||
# define shell_stifle_history(X) stifle_history(X)
 | 
			
		||||
# define shell_readline(X) readline(X)
 | 
			
		||||
 | 
			
		||||
#elif HAVE_LINENOISE
 | 
			
		||||
 | 
			
		||||
# include "linenoise.h"
 | 
			
		||||
# define shell_add_history(X) linenoiseHistoryAdd(X)
 | 
			
		||||
# define shell_read_history(X) linenoiseHistoryLoad(X)
 | 
			
		||||
# define shell_write_history(X) linenoiseHistorySave(X)
 | 
			
		||||
# define shell_stifle_history(X) linenoiseHistorySetMaxLen(X)
 | 
			
		||||
# define shell_readline(X) linenoise(X)
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
# define shell_read_history(X) 
 | 
			
		||||
# define shell_write_history(X)
 | 
			
		||||
# define shell_stifle_history(X)
 | 
			
		||||
 | 
			
		||||
# define SHELL_USE_LOCAL_GETLINE 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32) || defined(WIN32)
 | 
			
		||||
# include <io.h>
 | 
			
		||||
# include <fcntl.h>
 | 
			
		||||
@@ -113,11 +133,11 @@ extern int pclose(FILE*);
 | 
			
		||||
** routines take care of that.
 | 
			
		||||
*/
 | 
			
		||||
#if defined(_WIN32) || defined(WIN32)
 | 
			
		||||
static setBinaryMode(FILE *out){
 | 
			
		||||
static void setBinaryMode(FILE *out){
 | 
			
		||||
  fflush(out);
 | 
			
		||||
  _setmode(_fileno(out), _O_BINARY);
 | 
			
		||||
}
 | 
			
		||||
static setTextMode(FILE *out){
 | 
			
		||||
static void setTextMode(FILE *out){
 | 
			
		||||
  fflush(out);
 | 
			
		||||
  _setmode(_fileno(out), _O_TEXT);
 | 
			
		||||
}
 | 
			
		||||
@@ -451,14 +471,14 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
 | 
			
		||||
    zResult = local_getline(zPrior, in);
 | 
			
		||||
  }else{
 | 
			
		||||
    zPrompt = isContinuation ? continuePrompt : mainPrompt;
 | 
			
		||||
#if HAVE_READLINE
 | 
			
		||||
    free(zPrior);
 | 
			
		||||
    zResult = readline(zPrompt);
 | 
			
		||||
    if( zResult && *zResult ) add_history(zResult);
 | 
			
		||||
#else
 | 
			
		||||
#if SHELL_USE_LOCAL_GETLINE
 | 
			
		||||
    printf("%s", zPrompt);
 | 
			
		||||
    fflush(stdout);
 | 
			
		||||
    zResult = local_getline(zPrior, stdin);
 | 
			
		||||
#else
 | 
			
		||||
    free(zPrior);
 | 
			
		||||
    zResult = shell_readline(zPrompt);
 | 
			
		||||
    if( zResult && *zResult ) shell_add_history(zResult);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  return zResult;
 | 
			
		||||
@@ -1734,6 +1754,7 @@ static char zHelp[] =
 | 
			
		||||
  ".binary on|off         Turn binary output on or off.  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 +1767,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"
 | 
			
		||||
@@ -2445,6 +2466,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.
 | 
			
		||||
@@ -2600,6 +2730,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
 | 
			
		||||
@@ -2939,7 +3073,7 @@ static int do_meta_command(char *zLine, ShellState *p){
 | 
			
		||||
          fprintf(stderr, "%s:%d: expected %d columns but found %d - "
 | 
			
		||||
                          "filling the rest with NULL\n",
 | 
			
		||||
                          sCtx.zFile, startLine, nCol, i+1);
 | 
			
		||||
          i++;
 | 
			
		||||
          i += 2;
 | 
			
		||||
          while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
@@ -2968,7 +3102,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);
 | 
			
		||||
@@ -2998,7 +3133,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -3361,7 +3496,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
 | 
			
		||||
 | 
			
		||||
@@ -3568,6 +3703,7 @@ static int do_meta_command(char *zLine, ShellState *p){
 | 
			
		||||
      { "scratchmalloc",         SQLITE_TESTCTRL_SCRATCHMALLOC          },
 | 
			
		||||
      { "byteorder",             SQLITE_TESTCTRL_BYTEORDER              },
 | 
			
		||||
      { "never_corrupt",         SQLITE_TESTCTRL_NEVER_CORRUPT          },
 | 
			
		||||
      { "imposter",              SQLITE_TESTCTRL_IMPOSTER               },
 | 
			
		||||
    };
 | 
			
		||||
    int testctrl = -1;
 | 
			
		||||
    int rc = 0;
 | 
			
		||||
@@ -3660,6 +3796,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: 
 | 
			
		||||
@@ -4227,6 +4375,7 @@ int main(int argc, char **argv){
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  setBinaryMode(stdin);
 | 
			
		||||
  setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
 | 
			
		||||
  Argv0 = argv[0];
 | 
			
		||||
  main_init(&data);
 | 
			
		||||
  stdin_is_interactive = isatty(0);
 | 
			
		||||
@@ -4539,13 +4688,11 @@ int main(int argc, char **argv){
 | 
			
		||||
          sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
#if HAVE_READLINE
 | 
			
		||||
      if( zHistory ) read_history(zHistory);
 | 
			
		||||
#endif
 | 
			
		||||
      if( zHistory ) shell_read_history(zHistory);
 | 
			
		||||
      rc = process_input(&data, 0);
 | 
			
		||||
      if( zHistory ){
 | 
			
		||||
        stifle_history(100);
 | 
			
		||||
        write_history(zHistory);
 | 
			
		||||
        shell_stifle_history(100);
 | 
			
		||||
        shell_write_history(zHistory);
 | 
			
		||||
        free(zHistory);
 | 
			
		||||
      }
 | 
			
		||||
    }else{
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user