/ Check-in [2b1743d6]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Clean up the input reader in the command-line shell for improved legibility and performance.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2b1743d60171635c1e5a6ede6b4928f4671f948d
User & Date: drh 2013-08-06 14:01:46
Context
2013-08-06
14:36
For the ".import" command of the command-line shell, start a transaction if there is not one active already. check-in: 5dcc2d91 user: drh tags: trunk
14:01
Clean up the input reader in the command-line shell for improved legibility and performance. check-in: 2b1743d6 user: drh tags: trunk
07:45
More than double the speed of the resolveP2Values() routine in vdbeaux.c by moving from an extended if-else on every opcode to a switch. Opcodes are reordered in mkopcodesh.awk to put the switched opcodes close together, for additional performance and to reduce code footprint. check-in: 924f7e4d user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/shell.c.

    49     49   # include <editline/editline.h>
    50     50   #endif
    51     51   #if defined(HAVE_READLINE) && HAVE_READLINE==1
    52     52   # include <readline/readline.h>
    53     53   # include <readline/history.h>
    54     54   #endif
    55     55   #if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
    56         -# define readline(p) local_getline(p,stdin,0)
    57     56   # define add_history(X)
    58     57   # define read_history(X)
    59     58   # define write_history(X)
    60     59   # define stifle_history(X)
    61     60   #endif
    62     61   
    63     62   #if defined(_WIN32) || defined(WIN32)
................................................................................
   333    332   
   334    333   /*
   335    334   ** This routine reads a line of text from FILE in, stores
   336    335   ** the text in memory obtained from malloc() and returns a pointer
   337    336   ** to the text.  NULL is returned at end of file, or if malloc()
   338    337   ** fails.
   339    338   **
   340         -** The interface is like "readline" but no command-line editing
   341         -** is done.
          339  +** If zLine is not NULL then it is a malloced buffer returned from
          340  +** a previous call to this routine that may be reused.
   342    341   */
   343         -static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
   344         -  char *zLine;
   345         -  int nLine;
   346         -  int n;
   347         -  int inQuote = 0;
          342  +static char *local_getline(char *zLine, FILE *in){
          343  +  int nLine = zLine==0 ? 0 : 100;
          344  +  int n = 0;
   348    345   
   349         -  if( zPrompt && *zPrompt ){
   350         -    printf("%s",zPrompt);
   351         -    fflush(stdout);
   352         -  }
   353         -  nLine = 100;
   354         -  zLine = malloc( nLine );
   355         -  if( zLine==0 ) return 0;
   356         -  n = 0;
   357    346     while( 1 ){
   358    347       if( n+100>nLine ){
   359    348         nLine = nLine*2 + 100;
   360    349         zLine = realloc(zLine, nLine);
   361    350         if( zLine==0 ) return 0;
   362    351       }
   363    352       if( fgets(&zLine[n], nLine - n, in)==0 ){
................................................................................
   364    353         if( n==0 ){
   365    354           free(zLine);
   366    355           return 0;
   367    356         }
   368    357         zLine[n] = 0;
   369    358         break;
   370    359       }
   371         -    while( zLine[n] ){
   372         -      if( zLine[n]=='"' ) inQuote = !inQuote;
   373         -      n++;
   374         -    }
   375         -    if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){
          360  +    while( zLine[n] ) n++;
          361  +    if( n>0 && zLine[n-1]=='\n' ){
   376    362         n--;
   377    363         if( n>0 && zLine[n-1]=='\r' ) n--;
   378    364         zLine[n] = 0;
   379    365         break;
   380    366       }
   381    367     }
   382         -  zLine = realloc( zLine, n+1 );
   383    368     return zLine;
   384    369   }
   385    370   
   386    371   /*
   387    372   ** Retrieve a single line of input text.
   388    373   **
   389         -** zPrior is a string of prior text retrieved.  If not the empty
   390         -** string, then issue a continuation prompt.
          374  +** If in==0 then read from standard input and prompt before each line.
          375  +** If isContinuation is true, then a continuation prompt is appropriate.
          376  +** If isContinuation is zero, then the main prompt should be used.
          377  +**
          378  +** If zPrior is not NULL then it is a buffer from a prior call to this
          379  +** routine that can be reused.
          380  +**
          381  +** The result is stored in space obtained from malloc() and must either
          382  +** be freed by the caller or else passed back into this routine via the
          383  +** zPrior argument for reuse.
   391    384   */
   392         -static char *one_input_line(const char *zPrior, FILE *in){
          385  +static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
   393    386     char *zPrompt;
   394    387     char *zResult;
   395    388     if( in!=0 ){
   396         -    return local_getline(0, in, 0);
   397         -  }
   398         -  if( zPrior && zPrior[0] ){
   399         -    zPrompt = continuePrompt;
          389  +    zResult = local_getline(zPrior, in);
   400    390     }else{
   401         -    zPrompt = mainPrompt;
   402         -  }
   403         -  zResult = readline(zPrompt);
          391  +    zPrompt = isContinuation ? continuePrompt : mainPrompt;
   404    392   #if defined(HAVE_READLINE) && HAVE_READLINE==1
   405         -  if( zResult && *zResult ) add_history(zResult);
          393  +    free(zPrior);
          394  +    zResult = readline(zPrompt);
          395  +    if( zResult && *zResult ) add_history(zResult);
          396  +#else
          397  +    printf("%s", zPrompt);
          398  +    fflush(stdout);
          399  +    zResult = local_getline(zPrior, stdin);
   406    400   #endif
          401  +  }
   407    402     return zResult;
   408    403   }
   409    404   
   410    405   struct previous_mode_data {
   411    406     int valid;        /* Is there legit data in here? */
   412    407     int mode;
   413    408     int showHeader;
................................................................................
  2777   2772     return rc;
  2778   2773   }
  2779   2774   
  2780   2775   /*
  2781   2776   ** Return TRUE if a semicolon occurs anywhere in the first N characters
  2782   2777   ** of string z[].
  2783   2778   */
  2784         -static int _contains_semicolon(const char *z, int N){
         2779  +static int line_contains_semicolon(const char *z, int N){
  2785   2780     int i;
  2786   2781     for(i=0; i<N; i++){  if( z[i]==';' ) return 1; }
  2787   2782     return 0;
  2788   2783   }
  2789   2784   
  2790   2785   /*
  2791   2786   ** Test to see if a line consists entirely of whitespace.
................................................................................
  2812   2807   }
  2813   2808   
  2814   2809   /*
  2815   2810   ** Return TRUE if the line typed in is an SQL command terminator other
  2816   2811   ** than a semi-colon.  The SQL Server style "go" command is understood
  2817   2812   ** as is the Oracle "/".
  2818   2813   */
  2819         -static int _is_command_terminator(const char *zLine){
         2814  +static int line_is_command_terminator(const char *zLine){
  2820   2815     while( IsSpace(zLine[0]) ){ zLine++; };
  2821   2816     if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
  2822   2817       return 1;  /* Oracle */
  2823   2818     }
  2824   2819     if( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o'
  2825   2820            && _all_whitespace(&zLine[2]) ){
  2826   2821       return 1;  /* SQL Server */
................................................................................
  2828   2823     return 0;
  2829   2824   }
  2830   2825   
  2831   2826   /*
  2832   2827   ** Return true if zSql is a complete SQL statement.  Return false if it
  2833   2828   ** ends in the middle of a string literal or C-style comment.
  2834   2829   */
  2835         -static int _is_complete(char *zSql, int nSql){
         2830  +static int line_is_complete(char *zSql, int nSql){
  2836   2831     int rc;
  2837   2832     if( zSql==0 ) return 1;
  2838   2833     zSql[nSql] = ';';
  2839   2834     zSql[nSql+1] = 0;
  2840   2835     rc = sqlite3_complete(zSql);
  2841   2836     zSql[nSql] = 0;
  2842   2837     return rc;
................................................................................
  2848   2843   ** is coming from a file or device.  A prompt is issued and history
  2849   2844   ** is saved only if input is interactive.  An interrupt signal will
  2850   2845   ** cause this routine to exit immediately, unless input is interactive.
  2851   2846   **
  2852   2847   ** Return the number of errors.
  2853   2848   */
  2854   2849   static int process_input(struct callback_data *p, FILE *in){
  2855         -  char *zLine = 0;
  2856         -  char *zSql = 0;
  2857         -  int nSql = 0;
  2858         -  int nSqlPrior = 0;
  2859         -  char *zErrMsg;
  2860         -  int rc;
  2861         -  int errCnt = 0;
  2862         -  int lineno = 0;
  2863         -  int startline = 0;
         2850  +  char *zLine = 0;          /* A single input line */
         2851  +  char *zSql = 0;           /* Accumulated SQL text */
         2852  +  int nLine;                /* Length of current line */
         2853  +  int nSql = 0;             /* Bytes of zSql[] used */
         2854  +  int nAlloc = 0;           /* Allocated zSql[] space */
         2855  +  int nSqlPrior = 0;        /* Bytes of zSql[] used by prior line */
         2856  +  char *zErrMsg;            /* Error message returned */
         2857  +  int rc;                   /* Error code */
         2858  +  int errCnt = 0;           /* Number of errors seen */
         2859  +  int lineno = 0;           /* Current line number */
         2860  +  int startline = 0;        /* Line number for start of current input */
  2864   2861   
  2865   2862     while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
  2866   2863       fflush(p->out);
  2867         -    free(zLine);
  2868         -    zLine = one_input_line(zSql, in);
         2864  +    zLine = one_input_line(in, zLine, nSql>0);
  2869   2865       if( zLine==0 ){
  2870   2866         /* End of input */
  2871   2867         if( stdin_is_interactive ) printf("\n");
  2872   2868         break;
  2873   2869       }
  2874   2870       if( seenInterrupt ){
  2875   2871         if( in!=0 ) break;
  2876   2872         seenInterrupt = 0;
  2877   2873       }
  2878   2874       lineno++;
  2879         -    if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
         2875  +    if( nSql==0 && _all_whitespace(zLine) ) continue;
  2880   2876       if( zLine && zLine[0]=='.' && nSql==0 ){
  2881   2877         if( p->echoOn ) printf("%s\n", zLine);
  2882   2878         rc = do_meta_command(zLine, p);
  2883   2879         if( rc==2 ){ /* exit requested */
  2884   2880           break;
  2885   2881         }else if( rc ){
  2886   2882           errCnt++;
  2887   2883         }
  2888   2884         continue;
  2889   2885       }
  2890         -    if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){
         2886  +    if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){
  2891   2887         memcpy(zLine,";",2);
         2888  +    }
         2889  +    nLine = strlen30(zLine);
         2890  +    if( nSql+nLine+2>=nAlloc ){
         2891  +      nAlloc = nSql+nLine+100;
         2892  +      zSql = realloc(zSql, nAlloc);
         2893  +      if( zSql==0 ){
         2894  +        fprintf(stderr, "Error: out of memory\n");
         2895  +        exit(1);
         2896  +      }
  2892   2897       }
  2893   2898       nSqlPrior = nSql;
  2894         -    if( zSql==0 ){
         2899  +    if( nSql==0 ){
  2895   2900         int i;
  2896   2901         for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
  2897         -      if( zLine[i]!=0 ){
  2898         -        nSql = strlen30(zLine);
  2899         -        zSql = malloc( nSql+3 );
  2900         -        if( zSql==0 ){
  2901         -          fprintf(stderr, "Error: out of memory\n");
  2902         -          exit(1);
  2903         -        }
  2904         -        memcpy(zSql, zLine, nSql+1);
  2905         -        startline = lineno;
  2906         -      }
         2902  +      memcpy(zSql, zLine+i, nLine+1-i);
         2903  +      startline = lineno;
         2904  +      nSql = nLine-i;
  2907   2905       }else{
  2908         -      int len = strlen30(zLine);
  2909         -      zSql = realloc( zSql, nSql + len + 4 );
  2910         -      if( zSql==0 ){
  2911         -        fprintf(stderr,"Error: out of memory\n");
  2912         -        exit(1);
  2913         -      }
  2914   2906         zSql[nSql++] = '\n';
  2915         -      memcpy(&zSql[nSql], zLine, len+1);
  2916         -      nSql += len;
         2907  +      memcpy(zSql+nSql, zLine, nLine+1);
         2908  +      nSql += nLine;
  2917   2909       }
  2918         -    if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
         2910  +    if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
  2919   2911                   && sqlite3_complete(zSql) ){
  2920   2912         p->cnt = 0;
  2921   2913         open_db(p);
  2922   2914         BEGIN_TIMER;
  2923   2915         rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
  2924   2916         END_TIMER;
  2925   2917         if( rc || zErrMsg ){
................................................................................
  2935   2927             sqlite3_free(zErrMsg);
  2936   2928             zErrMsg = 0;
  2937   2929           }else{
  2938   2930             fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
  2939   2931           }
  2940   2932           errCnt++;
  2941   2933         }
  2942         -      free(zSql);
  2943         -      zSql = 0;
  2944   2934         nSql = 0;
  2945         -    }else if( zSql && _all_whitespace(zSql) ){
  2946         -      free(zSql);
  2947         -      zSql = 0;
         2935  +    }else if( nSql && _all_whitespace(zSql) ){
  2948   2936         nSql = 0;
  2949   2937       }
  2950   2938     }
  2951         -  if( zSql ){
         2939  +  if( nSql ){
  2952   2940       if( !_all_whitespace(zSql) ){
  2953   2941         fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
  2954   2942       }
  2955   2943       free(zSql);
  2956   2944     }
  2957   2945     free(zLine);
  2958   2946     return errCnt>0;