/ Check-in [3c796de8]
Login

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

Overview
Comment:Fix the handling of -init option to the sqlite shell. Ticket #568. Also add hooks for encrypting the database. (CVS 1206)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:3c796de8d1af55944f396f08feaa9e69c1652896
User & Date: drh 2004-02-01 01:22:51
Context
2004-02-02
12:29
Remove undocumented extensions from sqlite_mprintf() and friends in order to make it about 10% smaller. (CVS 1207) check-in: 0b3f552b user: drh tags: trunk
2004-02-01
01:22
Fix the handling of -init option to the sqlite shell. Ticket #568. Also add hooks for encrypting the database. (CVS 1206) check-in: 3c796de8 user: drh tags: trunk
2004-01-31
20:40
Fix a bug introduced by the previous check-in. (CVS 1205) check-in: 04cf2278 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/shell.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains code to implement the "sqlite" command line
    13     13   ** utility for accessing SQLite databases.
    14     14   **
    15         -** $Id: shell.c,v 1.83 2003/12/04 20:51:41 drh Exp $
           15  +** $Id: shell.c,v 1.84 2004/02/01 01:22:51 drh Exp $
    16     16   */
    17     17   #include <stdlib.h>
    18     18   #include <string.h>
    19     19   #include <stdio.h>
    20     20   #include "sqlite.h"
    21     21   #include <ctype.h>
    22     22   
................................................................................
   182    182     char nullvalue[20];    /* The text to print when a NULL comes back from
   183    183                            ** the database */
   184    184     struct previous_mode_data explainPrev;
   185    185                            /* Holds the mode information just before
   186    186                            ** .explain ON */
   187    187     char outfile[FILENAME_MAX]; /* Filename for *out */
   188    188     const char *zDbFilename;    /* name of the database file */
          189  +  char *zKey;                 /* Encryption key */
   189    190   };
   190    191   
   191    192   /*
   192    193   ** These are the allowed modes.
   193    194   */
   194    195   #define MODE_Line     0  /* One column per line.  Blank line between records */
   195    196   #define MODE_Column   1  /* One record per line in neat columns */
................................................................................
   485    486     ".mode insert TABLE     Generate SQL insert statements for TABLE\n"
   486    487     ".nullvalue STRING      Print STRING instead of nothing for NULL data\n"
   487    488     ".output FILENAME       Send output to FILENAME\n"
   488    489     ".output stdout         Send output to the screen\n"
   489    490     ".prompt MAIN CONTINUE  Replace the standard prompts\n"
   490    491     ".quit                  Exit this program\n"
   491    492     ".read FILENAME         Execute SQL in FILENAME\n"
          493  +#ifdef SQLITE_HAS_CRYPTO
          494  +  ".rekey OLD NEW NEW     Change the encryption key\n"
          495  +#endif
   492    496     ".schema ?TABLE?        Show the CREATE statements\n"
   493    497     ".separator STRING      Change separator string for \"list\" mode\n"
   494    498     ".show                  Show the current values for various settings\n"
   495    499     ".tables ?PATTERN?      List names of tables matching a pattern\n"
   496    500     ".timeout MS            Try opening locked tables for MS milliseconds\n"
   497    501     ".width NUM NUM ...     Set column widths for \"column\" mode\n"
   498    502   ;
................................................................................
   503    507   /*
   504    508   ** Make sure the database is open.  If it is not, then open it.  If
   505    509   ** the database fails to open, print an error message and exit.
   506    510   */
   507    511   static void open_db(struct callback_data *p){
   508    512     if( p->db==0 ){
   509    513       char *zErrMsg = 0;
   510         -    p->db = db = sqlite_open(p->zDbFilename, 0666, &zErrMsg);
   511         -    if( db==0 ){
   512         -      p->db = db = sqlite_open(p->zDbFilename, 0444, &zErrMsg);
   513         -      if( db==0 ){
   514         -        if( zErrMsg ){
   515         -          fprintf(stderr,"Unable to open database \"%s\": %s\n", 
   516         -             p->zDbFilename, zErrMsg);
   517         -        }else{
   518         -          fprintf(stderr,"Unable to open database %s\n", p->zDbFilename);
   519         -        }
   520         -        exit(1);
          514  +#ifdef SQLITE_HAS_CRYPTO
          515  +    if( p->zKey && p->zKey[0] ){
          516  +      int n = strlen(p->zKey);
          517  +      p->db = sqlite_open_encrypted(p->zDbFilename, p->zKey, n, &zErrMsg);
          518  +    }else
          519  +#endif
          520  +    p->db = sqlite_open(p->zDbFilename, 0, &zErrMsg);
          521  +    if( p->db==0 ){
          522  +      if( zErrMsg ){
          523  +        fprintf(stderr,"Unable to open database \"%s\": %s\n", 
          524  +           p->zDbFilename, zErrMsg);
   521    525         }else{
   522         -        fprintf(stderr,"Database \"%s\" opened READ ONLY!\n", p->zDbFilename);
          526  +        fprintf(stderr,"Unable to open database %s\n", p->zDbFilename);
   523    527         }
          528  +      exit(1);
   524    529       }
   525    530     }
   526    531   }
   527    532   
   528    533   /*
   529    534   ** If an input line begins with "." then invoke this routine to
   530    535   ** process that line.
................................................................................
   776    781       if( alt==0 ){
   777    782         fprintf(stderr,"can't open \"%s\"\n", azArg[1]);
   778    783       }else{
   779    784         process_input(p, alt);
   780    785         fclose(alt);
   781    786       }
   782    787     }else
          788  +
          789  +#ifdef SQLITE_HAS_CRYPTO
          790  +  if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){
          791  +    char *zOld = p->zKey;
          792  +    if( zOld==0 ) zOld = "";
          793  +    if( strcmp(azArg[1],zOld) ){
          794  +      fprintf(stderr,"old key is incorrect\n");
          795  +    }else if( strcmp(azArg[2], azArg[3]) ){
          796  +      fprintf(stderr,"2nd copy of new key does not match the 1st\n");
          797  +    }else{
          798  +      sqlite_freemem(p->zKey);
          799  +      p->zKey = sqlite_mprintf("%s", azArg[2]);
          800  +      sqlite_rekey(p->db, p->zKey, strlen(p->zKey));
          801  +    }
          802  +  }else
          803  +#endif
   783    804   
   784    805     if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
   785    806       struct callback_data data;
   786    807       char *zErrMsg = 0;
   787    808       open_db(p);
   788    809       memcpy(&data, p, sizeof(data));
   789    810       data.showHeader = 0;
................................................................................
  1101   1122     return home_dir;
  1102   1123   }
  1103   1124   
  1104   1125   /*
  1105   1126   ** Read input from the file given by sqliterc_override.  Or if that
  1106   1127   ** parameter is NULL, take input from ~/.sqliterc
  1107   1128   */
  1108         -static void process_sqliterc(struct callback_data *p, char *sqliterc_override){
         1129  +static void process_sqliterc(
         1130  +  struct callback_data *p,        /* Configuration data */
         1131  +  const char *sqliterc_override   /* Name of config file. NULL to use default */
         1132  +){
  1109   1133     char *home_dir = NULL;
  1110         -  char *sqliterc = sqliterc_override;
         1134  +  const char *sqliterc = sqliterc_override;
         1135  +  char *zBuf;
  1111   1136     FILE *in = NULL;
  1112   1137   
  1113   1138     if (sqliterc == NULL) {
  1114   1139       home_dir = find_home_dir();
  1115   1140       if( home_dir==0 ){
  1116   1141         fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0);
  1117   1142         return;
  1118   1143       }
  1119         -    sqliterc = malloc(strlen(home_dir) + 15);
  1120         -    if( sqliterc==0 ){
         1144  +    zBuf = malloc(strlen(home_dir) + 15);
         1145  +    if( zBuf==0 ){
  1121   1146         fprintf(stderr,"%s: out of memory!\n", Argv0);
  1122   1147         exit(1);
  1123   1148       }
  1124         -    sprintf(sqliterc,"%s/.sqliterc",home_dir);
         1149  +    sprintf(zBuf,"%s/.sqliterc",home_dir);
  1125   1150       free(home_dir);
         1151  +    sqliterc = (const char*)zBuf;
  1126   1152     }
  1127   1153     in = fopen(sqliterc,"r");
  1128         -  if(in && isatty(fileno(stdout))) {
  1129         -    printf("Loading resources from %s\n",sqliterc);
         1154  +  if( in ){
         1155  +    if( isatty(fileno(stdout)) ){
         1156  +      printf("Loading resources from %s\n",sqliterc);
         1157  +    }
  1130   1158       process_input(p,in);
  1131   1159       fclose(in);
  1132   1160     }
  1133   1161     return;
  1134   1162   }
  1135   1163   
  1136   1164   /*
................................................................................
  1170   1198     strcpy(mainPrompt,"sqlite> ");
  1171   1199     strcpy(continuePrompt,"   ...> ");
  1172   1200   }
  1173   1201   
  1174   1202   int main(int argc, char **argv){
  1175   1203     char *zErrMsg = 0;
  1176   1204     struct callback_data data;
  1177         -  int origArgc = argc;
  1178         -  char **origArgv = argv;
         1205  +  const char *zInitFile = 0;
         1206  +  char *zFirstCmd = 0;
  1179   1207     int i;
  1180   1208     extern int sqliteOsFileExists(const char*);
  1181   1209   
  1182   1210   #ifdef __MACOS__
  1183   1211     argc = ccommand(&argv);
  1184         -  origArgc = argc;
  1185         -  origArgv = argv;
  1186   1212   #endif
  1187   1213   
  1188   1214     Argv0 = argv[0];
  1189   1215     main_init(&data);
  1190   1216   
  1191   1217     /* Make sure we have a valid signal handler early, before anything
  1192   1218     ** else is done.
  1193   1219     */
  1194   1220   #ifdef SIGINT
  1195   1221     signal(SIGINT, interrupt_handler);
  1196   1222   #endif
  1197   1223   
  1198         -  /* Locate the name of the database file
         1224  +  /* Do an initial pass through the command-line argument to locate
         1225  +  ** the name of the database file, the name of the initialization file,
         1226  +  ** and the first command to execute.
  1199   1227     */
  1200         -  for(i=1; i<argc; i++){
         1228  +  for(i=1; i<argc-1; i++){
  1201   1229       if( argv[i][0]!='-' ) break;
  1202   1230       if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
  1203   1231         i++;
         1232  +    }else if( strcmp(argv[i],"-init")==0 ){
         1233  +      i++;
         1234  +      zInitFile = argv[i];
         1235  +    }else if( strcmp(argv[i],"-key")==0 ){
         1236  +      i++;
         1237  +      data.zKey = sqlite_mprintf("%s",argv[i]);
  1204   1238       }
  1205   1239     }
  1206         -  data.zDbFilename = i<argc ? argv[i] : ":memory:";
         1240  +  if( i<argc ){
         1241  +    data.zDbFilename = argv[i++];
         1242  +  }else{
         1243  +    data.zDbFilename = ":memory:";
         1244  +  }
         1245  +  if( i<argc ){
         1246  +    zFirstCmd = argv[i++];
         1247  +  }
  1207   1248     data.out = stdout;
  1208   1249   
  1209   1250     /* Go ahead and open the database file if it already exists.  If the
  1210   1251     ** file does not exist, delay opening it.  This prevents empty database
  1211   1252     ** files from being created if a user mistypes the database name argument
  1212   1253     ** to the sqlite command-line tool.
  1213   1254     */
  1214   1255     if( sqliteOsFileExists(data.zDbFilename) ){
  1215   1256       open_db(&data);
  1216   1257     }
  1217   1258   
  1218         -  /* Process the ~/.sqliterc file, if there is one
  1219         -  */
  1220         -  process_sqliterc(&data,NULL);
  1221         -
  1222         -  /* Process command-line options
         1259  +  /* Process the initialization file if there is one.  If no -init option
         1260  +  ** is given on the command line, look for a file named ~/.sqliterc and
         1261  +  ** try to process it.
  1223   1262     */
  1224         -  while( argc>=2 && argv[1][0]=='-' ){
  1225         -    if( argc>=3 && strcmp(argv[1],"-init")==0 ){
  1226         -      /* If we get a -init to do, we have to pretend that
  1227         -      ** it replaced the .sqliterc file. Soooo, in order to
  1228         -      ** do that we need to start from scratch...*/
  1229         -      main_init(&data);
         1263  +  process_sqliterc(&data,zInitFile);
  1230   1264   
  1231         -      /* treat this file as the sqliterc... */
  1232         -      process_sqliterc(&data,argv[2]);
  1233         -
  1234         -      /* fix up the command line so we do not re-read
  1235         -      ** the option next time around... */
  1236         -      {
  1237         -        int i = 1;
  1238         -        for(i=1;i<=argc-2;i++) {
  1239         -          argv[i] = argv[i+2];
  1240         -        }
  1241         -      }
  1242         -      origArgc-=2;
  1243         -
  1244         -      /* and reset the command line options to be re-read.*/
  1245         -      argv = origArgv;
  1246         -      argc = origArgc;
  1247         -
  1248         -    }else if( strcmp(argv[1],"-html")==0 ){
         1265  +  /* Make a second pass through the command-line argument and set
         1266  +  ** options.  This second pass is delayed until after the initialization
         1267  +  ** file is processed so that the command-line arguments will override
         1268  +  ** settings in the initialization file.
         1269  +  */
         1270  +  for(i=1; i<argc && argv[i][0]=='-'; i++){
         1271  +    char *z = argv[i];
         1272  +    if( strcmp(z,"-init")==0 || strcmp(z,"-key")==0 ){
         1273  +      i++;
         1274  +    }else if( strcmp(z,"-html")==0 ){
  1249   1275         data.mode = MODE_Html;
  1250         -      argc--;
  1251         -      argv++;
  1252         -    }else if( strcmp(argv[1],"-list")==0 ){
         1276  +    }else if( strcmp(z,"-list")==0 ){
  1253   1277         data.mode = MODE_List;
  1254         -      argc--;
  1255         -      argv++;
  1256         -    }else if( strcmp(argv[1],"-line")==0 ){
         1278  +    }else if( strcmp(z,"-line")==0 ){
  1257   1279         data.mode = MODE_Line;
  1258         -      argc--;
  1259         -      argv++;
  1260         -    }else if( strcmp(argv[1],"-column")==0 ){
         1280  +    }else if( strcmp(z,"-column")==0 ){
  1261   1281         data.mode = MODE_Column;
  1262         -      argc--;
  1263         -      argv++;
  1264         -    }else if( argc>=3 && strcmp(argv[1],"-separator")==0 ){
  1265         -      sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[2]);
  1266         -      argc -= 2;
  1267         -      argv += 2;
  1268         -    }else if( argc>=3 && strcmp(argv[1],"-nullvalue")==0 ){
  1269         -      sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[2]);
  1270         -      argc -= 2;
  1271         -      argv += 2;
  1272         -    }else if( strcmp(argv[1],"-header")==0 ){
         1282  +    }else if( strcmp(z,"-separator")==0 ){
         1283  +      i++;
         1284  +      sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]);
         1285  +    }else if( strcmp(z,"-nullvalue")==0 ){
         1286  +      i++;
         1287  +      sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]);
         1288  +    }else if( strcmp(z,"-header")==0 ){
  1273   1289         data.showHeader = 1;
  1274         -      argc--;
  1275         -      argv++;
  1276         -    }else if( strcmp(argv[1],"-noheader")==0 ){
         1290  +    }else if( strcmp(z,"-noheader")==0 ){
  1277   1291         data.showHeader = 0;
  1278         -      argc--;
  1279         -      argv++;
  1280         -    }else if( strcmp(argv[1],"-echo")==0 ){
         1292  +    }else if( strcmp(z,"-echo")==0 ){
  1281   1293         data.echoOn = 1;
  1282         -      argc--;
  1283         -      argv++;
  1284         -    }else if( strcmp(argv[1],"-version")==0 ){
         1294  +    }else if( strcmp(z,"-version")==0 ){
  1285   1295         printf("%s\n", sqlite_version);
  1286   1296         return 1;
  1287         -    }else if( strcmp(argv[1],"-help")==0 ){
         1297  +    }else if( strcmp(z,"-help")==0 ){
  1288   1298         usage(1);
  1289   1299       }else{
  1290         -      fprintf(stderr,"%s: unknown option: %s\n", Argv0, argv[1]);
         1300  +      fprintf(stderr,"%s: unknown option: %s\n", Argv0, z);
  1291   1301         fprintf(stderr,"Use -help for a list of options.\n");
  1292   1302         return 1;
  1293   1303       }
  1294   1304     }
  1295   1305   
  1296         -  if( argc<2 ){
  1297         -    usage(0);
  1298         -  }else if( argc==3 ){
         1306  +  if( zFirstCmd ){
  1299   1307       /* Run just the command that follows the database name
  1300   1308       */
  1301         -    if( argv[2][0]=='.' ){
  1302         -      do_meta_command(argv[2], &data);
         1309  +    if( zFirstCmd[0]=='.' ){
         1310  +      do_meta_command(zFirstCmd, &data);
  1303   1311         exit(0);
  1304   1312       }else{
  1305   1313         int rc;
  1306   1314         open_db(&data);
  1307         -      rc = sqlite_exec(db, argv[2], callback, &data, &zErrMsg);
         1315  +      rc = sqlite_exec(db, zFirstCmd, callback, &data, &zErrMsg);
  1308   1316         if( rc!=0 && zErrMsg!=0 ){
  1309   1317           fprintf(stderr,"SQL error: %s\n", zErrMsg);
  1310   1318           exit(1);
  1311   1319         }
  1312   1320       }
  1313   1321     }else{
  1314   1322       /* Run commands received from standard input

Changes to src/sqlite.h.in.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This header file defines the interface that the SQLite library
    13     13   ** presents to client programs.
    14     14   **
    15         -** @(#) $Id: sqlite.h.in,v 1.54 2004/01/15 02:44:03 drh Exp $
           15  +** @(#) $Id: sqlite.h.in,v 1.55 2004/02/01 01:22:52 drh Exp $
    16     16   */
    17     17   #ifndef _SQLITE_H_
    18     18   #define _SQLITE_H_
    19     19   #include <stdarg.h>     /* Needed for the definition of va_list */
    20     20   
    21     21   /*
    22     22   ** Make sure we can call this stuff from C++.
................................................................................
   768    768   ** Otherwise NULL is returned.
   769    769   **
   770    770   ** Registering a NULL function disables the callback.
   771    771   **
   772    772   ******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
   773    773   */
   774    774   void *sqlite_commit_hook(sqlite*, int(*)(void*), void*);
          775  +
          776  +/*
          777  +** Open an encrypted SQLite database.  If pKey==0 or nKey==0, this routine
          778  +** is the same as sqlite_open().
          779  +**
          780  +** The code to implement this API is not available in the public release
          781  +** of SQLite.
          782  +*/
          783  +sqlite *sqlite_open_encrypted(
          784  +  const char *zFilename,   /* Name of the encrypted database */
          785  +  const void *pKey,        /* Pointer to the key */
          786  +  int nKey,                /* Number of bytes in the key */
          787  +  char **pzErrmsg          /* Write error message here */
          788  +);
          789  +
          790  +/*
          791  +** Change the key on an open database.  If the current database is not
          792  +** encrypted, this routine will encrypt it.  If pNew==0 or nNew==0, the
          793  +** database is decrypted.
          794  +**
          795  +** The code to implement this API is not available in the public release
          796  +** of SQLite.
          797  +*/
          798  +int sqlite_rekey(
          799  +  sqlite *db,                    /* Database to be rekeyed */
          800  +  const void *pKey, int nKey     /* The new key */
          801  +);
   775    802   
   776    803   #ifdef __cplusplus
   777    804   }  /* End of the 'extern "C"' block */
   778    805   #endif
   779    806   
   780    807   #endif /* _SQLITE_H_ */

Changes to src/tclsqlite.c.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** A TCL Interface to SQLite
    13     13   **
    14         -** $Id: tclsqlite.c,v 1.54 2004/01/15 02:44:03 drh Exp $
           14  +** $Id: tclsqlite.c,v 1.55 2004/02/01 01:22:52 drh Exp $
    15     15   */
    16     16   #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
    17     17   
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include <stdlib.h>
    21     21   #include <string.h>
................................................................................
   482    482   ** The first command opens a connection to the "my_database" database
   483    483   ** and calls that connection "db1".  The second command causes this
   484    484   ** subroutine to be invoked.
   485    485   */
   486    486   static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
   487    487     SqliteDb *pDb = (SqliteDb*)cd;
   488    488     int choice;
          489  +  int rc = TCL_OK;
   489    490     static const char *DB_strs[] = {
   490    491       "authorizer",         "busy",              "changes",
   491    492       "close",              "commit_hook",       "complete",
   492    493       "errorcode",          "eval",              "function",
   493    494       "last_insert_rowid",  "onecolumn",         "progress",
   494         -    "timeout",            "trace",             0
          495  +    "rekey",              "timeout",           "trace",
          496  +    0                    
   495    497     };
   496    498     enum DB_enum {
   497    499       DB_AUTHORIZER,        DB_BUSY,             DB_CHANGES,
   498    500       DB_CLOSE,             DB_COMMIT_HOOK,      DB_COMPLETE,
   499    501       DB_ERRORCODE,         DB_EVAL,             DB_FUNCTION,
   500    502       DB_LAST_INSERT_ROWID, DB_ONECOLUMN,        DB_PROGRESS,
   501         -    DB_TIMEOUT,           DB_TRACE,            
          503  +    DB_REKEY,             DB_TIMEOUT,          DB_TRACE,
   502    504     };
   503    505   
   504    506     if( objc<2 ){
   505    507       Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
   506    508       return TCL_ERROR;
   507    509     }
   508    510     if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
................................................................................
   743    745     ** If "array" is an empty string, then the values are placed in variables
   744    746     ** that have the same name as the fields extracted by the query.
   745    747     */
   746    748     case DB_EVAL: {
   747    749       CallbackData cbData;
   748    750       char *zErrMsg;
   749    751       char *zSql;
   750         -    int rc;
   751    752   #ifdef UTF_TRANSLATION_NEEDED
   752    753       Tcl_DString dSql;
   753    754       int i;
   754    755   #endif
   755    756   
   756    757       if( objc!=5 && objc!=3 ){
   757    758         Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?");
................................................................................
   861    862   
   862    863     /*
   863    864     **     $db onecolumn SQL
   864    865     **
   865    866     ** Return a single column from a single row of the given SQL query.
   866    867     */
   867    868     case DB_ONECOLUMN: {
   868         -    int rc;
   869    869       char *zSql;
   870    870       char *zErrMsg = 0;
   871    871       if( objc!=3 ){
   872    872         Tcl_WrongNumArgs(interp, 2, objv, "SQL");
   873    873         return TCL_ERROR;
   874    874       }
   875    875       zSql = Tcl_GetStringFromObj(objv[2], 0);
   876    876       rc = sqlite_exec(pDb->db, zSql, DbEvalCallback3, interp, &zErrMsg);
   877    877       if( rc==SQLITE_ABORT ){
   878         -      /* Do nothing.  This is normal. */
          878  +      rc = SQLITE_OK;
   879    879       }else if( zErrMsg ){
   880    880         Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
   881    881         free(zErrMsg);
   882    882         rc = TCL_ERROR;
   883    883       }else if( rc!=SQLITE_OK ){
   884    884         Tcl_AppendResult(interp, sqlite_error_string(rc), 0);
   885    885         rc = TCL_ERROR;
   886    886       }
   887    887       break;
   888    888     }
          889  +
          890  +  /*
          891  +  **     $db rekey KEY
          892  +  **
          893  +  ** Change the encryption key on the currently open database.
          894  +  */
          895  +  case DB_REKEY: {
          896  +    int nKey;
          897  +    void *pKey;
          898  +    if( objc!=3 ){
          899  +      Tcl_WrongNumArgs(interp, 2, objv, "KEY");
          900  +      return TCL_ERROR;
          901  +    }
          902  +    pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
          903  +#ifdef SQLITE_HAS_CRYPTO
          904  +    rc = sqlite_rekey(pDb->db, pKey, nKey);
          905  +    if( rc ){
          906  +      Tcl_AppendResult(interp, sqlite_error_string(rc), 0);
          907  +      rc = TCL_ERROR;
          908  +    }
          909  +#endif
          910  +    break;
          911  +  }
   889    912   
   890    913     /*
   891    914     **     $db timeout MILLESECONDS
   892    915     **
   893    916     ** Delay for the number of milliseconds specified when a file is locked.
   894    917     */
   895    918     case DB_TIMEOUT: {
................................................................................
   936    959           sqlite_trace(pDb->db, 0, 0);
   937    960         }
   938    961       }
   939    962       break;
   940    963     }
   941    964   
   942    965     } /* End of the SWITCH statement */
   943         -  return TCL_OK;
          966  +  return rc;
   944    967   }
   945    968   
   946    969   /*
   947         -**   sqlite DBNAME FILENAME ?MODE?
          970  +**   sqlite DBNAME FILENAME ?MODE? ?-key KEY?
   948    971   **
   949    972   ** This is the main Tcl command.  When the "sqlite" Tcl command is
   950    973   ** invoked, this routine runs to process that command.
   951    974   **
   952    975   ** The first argument, DBNAME, is an arbitrary name for a new
   953    976   ** database connection.  This command creates a new command named
   954    977   ** DBNAME that is used to control that connection.  The database
................................................................................
   970    993   **
   971    994   **  sqlite -tcl-uses-utf
   972    995   **
   973    996   **       Return "1" if compiled with a Tcl uses UTF-8.  Return "0" if
   974    997   **       not.  Used by tests to make sure the library was compiled 
   975    998   **       correctly.
   976    999   */
   977         -static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){
         1000  +static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
   978   1001     int mode;
   979   1002     SqliteDb *p;
         1003  +  void *pKey = 0;
         1004  +  int nKey = 0;
         1005  +  const char *zArg;
   980   1006     char *zErrMsg;
         1007  +  const char *zFile;
   981   1008     char zBuf[80];
   982         -  if( argc==2 ){
   983         -    if( strcmp(argv[1],"-encoding")==0 ){
         1009  +  if( objc==2 ){
         1010  +    zArg = Tcl_GetStringFromObj(objv[1], 0);
         1011  +    if( strcmp(zArg,"-encoding")==0 ){
   984   1012         Tcl_AppendResult(interp,sqlite_encoding,0);
   985   1013         return TCL_OK;
   986   1014       }
   987         -    if( strcmp(argv[1],"-version")==0 ){
         1015  +    if( strcmp(zArg,"-version")==0 ){
   988   1016         Tcl_AppendResult(interp,sqlite_version,0);
   989   1017         return TCL_OK;
   990   1018       }
   991         -    if( strcmp(argv[1],"-tcl-uses-utf")==0 ){
         1019  +    if( strcmp(zArg,"-has-crypto")==0 ){
         1020  +#ifdef SQLITE_HAS_CRYPTO
         1021  +      Tcl_AppendResult(interp,"1",0);
         1022  +#else
         1023  +      Tcl_AppendResult(interp,"0",0);
         1024  +#endif
         1025  +      return TCL_OK;
         1026  +    }
         1027  +    if( strcmp(zArg,"-tcl-uses-utf")==0 ){
   992   1028   #ifdef TCL_UTF_MAX
   993   1029         Tcl_AppendResult(interp,"1",0);
   994   1030   #else
   995   1031         Tcl_AppendResult(interp,"0",0);
   996   1032   #endif
   997   1033         return TCL_OK;
   998   1034       }
   999   1035     }
  1000         -  if( argc!=3 && argc!=4 ){
  1001         -    Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
  1002         -       " HANDLE FILENAME ?MODE?\"", 0);
         1036  +  if( objc==5 || objc==6 ){
         1037  +    zArg = Tcl_GetStringFromObj(objv[objc-2], 0);
         1038  +    if( strcmp(zArg,"-key")==0 ){
         1039  +      pKey = Tcl_GetByteArrayFromObj(objv[objc-1], &nKey);
         1040  +      objc -= 2;
         1041  +    }
         1042  +  }
         1043  +  if( objc!=3 && objc!=4 ){
         1044  +    Tcl_WrongNumArgs(interp, 1, objv, 
         1045  +#ifdef SQLITE_HAS_CRYPTO
         1046  +      "HANDLE FILENAME ?-key CRYPTOKEY?"
         1047  +#else
         1048  +      "HANDLE FILENAME ?MODE?"
         1049  +#endif
         1050  +    );
  1003   1051       return TCL_ERROR;
  1004   1052     }
  1005         -  if( argc==3 ){
         1053  +  if( objc==3 ){
  1006   1054       mode = 0666;
  1007         -  }else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){
         1055  +  }else if( Tcl_GetIntFromObj(interp, objv[3], &mode)!=TCL_OK ){
  1008   1056       return TCL_ERROR;
  1009   1057     }
  1010   1058     zErrMsg = 0;
  1011   1059     p = (SqliteDb*)Tcl_Alloc( sizeof(*p) );
  1012   1060     if( p==0 ){
  1013   1061       Tcl_SetResult(interp, "malloc failed", TCL_STATIC);
  1014   1062       return TCL_ERROR;
  1015   1063     }
  1016   1064     memset(p, 0, sizeof(*p));
  1017         -  p->db = sqlite_open(argv[2], mode, &zErrMsg);
         1065  +  zFile = Tcl_GetStringFromObj(objv[2], 0);
         1066  +#ifdef SQLITE_HAS_CRYPTO
         1067  +  if( nKey>0 ){
         1068  +    p->db = sqlite_open_encrypted(zFile, pKey, nKey, &zErrMsg);
         1069  +  }else
         1070  +#endif
         1071  +  p->db = sqlite_open(zFile, mode, &zErrMsg);
  1018   1072     if( p->db==0 ){
  1019   1073       Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
  1020   1074       Tcl_Free((char*)p);
  1021   1075       free(zErrMsg);
  1022   1076       return TCL_ERROR;
  1023   1077     }
  1024         -  Tcl_CreateObjCommand(interp, argv[1], DbObjCmd, (char*)p, DbDeleteCmd);
         1078  +  zArg = Tcl_GetStringFromObj(objv[1], 0);
         1079  +  Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd);
  1025   1080   
  1026   1081     /* The return value is the value of the sqlite* pointer
  1027   1082     */
  1028   1083     sprintf(zBuf, "%p", p->db);
  1029   1084     if( strncmp(zBuf,"0x",2) ){
  1030   1085       sprintf(zBuf, "0x%p", p->db);
  1031   1086     }
................................................................................
  1059   1114   ** (Hence there is no namespace.  There is no point in using a namespace
  1060   1115   ** if the extension only supplies one new name!)  The "sqlite" command is
  1061   1116   ** used to open a new SQLite database.  See the DbMain() routine above
  1062   1117   ** for additional information.
  1063   1118   */
  1064   1119   int Sqlite_Init(Tcl_Interp *interp){
  1065   1120     Tcl_InitStubs(interp, "8.0", 0);
  1066         -  Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0);
         1121  +  Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
  1067   1122     Tcl_PkgProvide(interp, "sqlite", "2.0");
  1068   1123     return TCL_OK;
  1069   1124   }
  1070   1125   int Tclsqlite_Init(Tcl_Interp *interp){
  1071   1126     Tcl_InitStubs(interp, "8.0", 0);
  1072         -  Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0);
         1127  +  Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
  1073   1128     Tcl_PkgProvide(interp, "sqlite", "2.0");
  1074   1129     return TCL_OK;
  1075   1130   }
  1076   1131   int Sqlite_SafeInit(Tcl_Interp *interp){
  1077   1132     return TCL_OK;
  1078   1133   }
  1079   1134   int Tclsqlite_SafeInit(Tcl_Interp *interp){

Changes to test/tclsqlite.test.

    11     11   # This file implements regression tests for TCL interface to the
    12     12   # SQLite library. 
    13     13   #
    14     14   # Actually, all tests are based on the TCL interface, so the main
    15     15   # interface is pretty well tested.  This file contains some addition
    16     16   # tests for fringe issues that the main test suite does not cover.
    17     17   #
    18         -# $Id: tclsqlite.test,v 1.17 2004/01/15 02:44:04 drh Exp $
           18  +# $Id: tclsqlite.test,v 1.18 2004/02/01 01:22:52 drh Exp $
    19     19   
    20     20   set testdir [file dirname $argv0]
    21     21   source $testdir/tester.tcl
    22     22   
    23     23   # Check the error messages generated by tclsqlite
    24     24   #
           25  +if {[sqlite -has-crypto]} {
           26  +  set r "sqlite HANDLE FILENAME ?-key CRYPTOKEY?"
           27  +} else {
           28  +  set r "sqlite HANDLE FILENAME ?MODE?"
           29  +}
    25     30   do_test tcl-1.1 {
    26     31     set v [catch {sqlite bogus} msg]
    27     32     lappend v $msg
    28         -} {1 {wrong # args: should be "sqlite HANDLE FILENAME ?MODE?"}}
           33  +} [list 1 "wrong # args: should be \"$r\""]
    29     34   do_test tcl-1.2 {
    30     35     set v [catch {db bogus} msg]
    31     36     lappend v $msg
    32         -} {1 {bad option "bogus": must be authorizer, busy, changes, close, commit_hook, complete, errorcode, eval, function, last_insert_rowid, onecolumn, progress, timeout, or trace}}
           37  +} {1 {bad option "bogus": must be authorizer, busy, changes, close, commit_hook, complete, errorcode, eval, function, last_insert_rowid, onecolumn, progress, rekey, timeout, or trace}}
    33     38   do_test tcl-1.3 {
    34     39     execsql {CREATE TABLE t1(a int, b int)}
    35     40     execsql {INSERT INTO t1 VALUES(10,20)}
    36     41     set v [catch {
    37     42       db eval {SELECT * FROM t1} data {
    38     43         error "The error message"
    39     44       }
................................................................................
    94     99   #
    95    100   do_test tcl-3.1 {
    96    101     execsql {
    97    102       INSERT INTO t1 SELECT a*2, b*2 FROM t1;
    98    103       INSERT INTO t1 SELECT a*2+1, b*2+1 FROM t1;
    99    104       INSERT INTO t1 SELECT a*2+3, b*2+3 FROM t1;
   100    105     }
   101         -  db onecolumn {SELECT * FROM t1 ORDER BY a}
   102         -} {10}
          106  +  set rc [catch {db onecolumn {SELECT * FROM t1 ORDER BY a}} msg]
          107  +  lappend rc $msg
          108  +} {0 10}
   103    109   do_test tcl-3.2 {
   104    110     db onecolumn {SELECT * FROM t1 WHERE a<0}
   105    111   } {}
   106    112   do_test tcl-3.3 {
   107    113     set rc [catch {db onecolumn} errmsg]
   108    114     lappend rc $errmsg
   109    115   } {1 {wrong # args: should be "db onecolumn SQL"}}
   110    116   
   111    117   
   112    118   finish_test