diff --git a/manifest b/manifest index ece5381fde..050dbc5935 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbug\sintroduced\sby\sthe\sprevious\scheck-in.\s(CVS\s1205) -D 2004-01-31T20:40:42 +C Fix\sthe\shandling\sof\s-init\soption\sto\sthe\ssqlite\sshell.\sTicket\s#568.\nAlso\sadd\shooks\sfor\sencrypting\sthe\sdatabase.\s(CVS\s1206) +D 2004-02-01T01:22:51 F Makefile.in 0515ff9218ad8d5a8f6220f0494b8ef94c67013b F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -47,11 +47,11 @@ F src/pragma.c 89d62c31c6f0a43376fe8d20549b87a6d30c467a F src/printf.c 292a7bfc5a815cb6465e32b2d5c9fe9bd43b27f0 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/select.c b7694067df8d57fd0c85ddcc0840532d181552ad -F src/shell.c 3b067edc098c45caca164bcad1fa79192c3ec5ae -F src/sqlite.h.in c70d8533cd5a5ae8af580597dbc726693ef82de9 +F src/shell.c a069d35277983d54348105aa3c73be3c45eb9c38 +F src/sqlite.h.in 1798588cab21ebf9fac3aad7fc1539b396c1f91d F src/sqliteInt.h c5b727d5d07b88654c204c0fc1ae79c9f635a008 F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895 -F src/tclsqlite.c 85810fc4a850e2178d71aa5efe576dcd331db596 +F src/tclsqlite.c 30afbb2e446d193d867e49fb0ca5ed84f1e0867f F src/test1.c e8652055d04d241d4fb437b5c33ff07d9f13b4b4 F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700 F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5 @@ -127,7 +127,7 @@ F test/sort.test ba07b107c16070208e6aab3cadea66ba079d85ba F test/subselect.test f0fea8cf9f386d416d64d152e3c65f9116d0f50f F test/table.test 371a1fc1c470982b2f68f9732f903a5d96f949c4 F test/tableapi.test d881e787779a175238b72f55b5e50d3a85ab47a6 -F test/tclsqlite.test 6921477a25aa630fd8d89b1ad231f80ef0dea88e +F test/tclsqlite.test 97233f45db35a76ceae9e56d244af68fc8061ceb F test/temptable.test c82bd6f800f10e8cf96921af6315e5f1c21e2692 F test/tester.tcl 2671536d3650c29e7c105219f277568b0884cb58 F test/thread1.test 0c1fcc2f9bdd887225e56f48db8ddfbb3d0794ba @@ -182,7 +182,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P 06e7ff4cb8c73fd690c6d5b5f530a30d83f4f10c -R ff7fd92d976b4f09b582d8f23eb657e9 +P 04cf22785e68fcd4098e6c10a89386108cd0bf07 +R 6b544ab7468b55cbc9dfec6b7298d72d U drh -Z 2fd2d1288462765557d2cdab9570e192 +Z e87a644a943e033216fc69c960947b44 diff --git a/manifest.uuid b/manifest.uuid index baaf37f24a..985787fcb0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -04cf22785e68fcd4098e6c10a89386108cd0bf07 \ No newline at end of file +3c796de8d1af55944f396f08feaa9e69c1652896 \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 0fe82be3bc..1cf0da0a10 100644 --- a/src/shell.c +++ b/src/shell.c @@ -12,7 +12,7 @@ ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.83 2003/12/04 20:51:41 drh Exp $ +** $Id: shell.c,v 1.84 2004/02/01 01:22:51 drh Exp $ */ #include #include @@ -186,6 +186,7 @@ struct callback_data { ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ + char *zKey; /* Encryption key */ }; /* @@ -489,6 +490,9 @@ static char zHelp[] = ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" +#ifdef SQLITE_HAS_CRYPTO + ".rekey OLD NEW NEW Change the encryption key\n" +#endif ".schema ?TABLE? Show the CREATE statements\n" ".separator STRING Change separator string for \"list\" mode\n" ".show Show the current values for various settings\n" @@ -507,20 +511,21 @@ static void process_input(struct callback_data *p, FILE *in); static void open_db(struct callback_data *p){ if( p->db==0 ){ char *zErrMsg = 0; - p->db = db = sqlite_open(p->zDbFilename, 0666, &zErrMsg); - if( db==0 ){ - p->db = db = sqlite_open(p->zDbFilename, 0444, &zErrMsg); - if( db==0 ){ - if( zErrMsg ){ - fprintf(stderr,"Unable to open database \"%s\": %s\n", - p->zDbFilename, zErrMsg); - }else{ - fprintf(stderr,"Unable to open database %s\n", p->zDbFilename); - } - exit(1); +#ifdef SQLITE_HAS_CRYPTO + if( p->zKey && p->zKey[0] ){ + int n = strlen(p->zKey); + p->db = sqlite_open_encrypted(p->zDbFilename, p->zKey, n, &zErrMsg); + }else +#endif + p->db = sqlite_open(p->zDbFilename, 0, &zErrMsg); + if( p->db==0 ){ + if( zErrMsg ){ + fprintf(stderr,"Unable to open database \"%s\": %s\n", + p->zDbFilename, zErrMsg); }else{ - fprintf(stderr,"Database \"%s\" opened READ ONLY!\n", p->zDbFilename); + fprintf(stderr,"Unable to open database %s\n", p->zDbFilename); } + exit(1); } } } @@ -781,6 +786,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){ } }else +#ifdef SQLITE_HAS_CRYPTO + if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){ + char *zOld = p->zKey; + if( zOld==0 ) zOld = ""; + if( strcmp(azArg[1],zOld) ){ + fprintf(stderr,"old key is incorrect\n"); + }else if( strcmp(azArg[2], azArg[3]) ){ + fprintf(stderr,"2nd copy of new key does not match the 1st\n"); + }else{ + sqlite_freemem(p->zKey); + p->zKey = sqlite_mprintf("%s", azArg[2]); + sqlite_rekey(p->db, p->zKey, strlen(p->zKey)); + } + }else +#endif + if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ struct callback_data data; char *zErrMsg = 0; @@ -1105,9 +1126,13 @@ static char *find_home_dir(void){ ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc */ -static void process_sqliterc(struct callback_data *p, char *sqliterc_override){ +static void process_sqliterc( + struct callback_data *p, /* Configuration data */ + const char *sqliterc_override /* Name of config file. NULL to use default */ +){ char *home_dir = NULL; - char *sqliterc = sqliterc_override; + const char *sqliterc = sqliterc_override; + char *zBuf; FILE *in = NULL; if (sqliterc == NULL) { @@ -1116,17 +1141,20 @@ static void process_sqliterc(struct callback_data *p, char *sqliterc_override){ fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0); return; } - sqliterc = malloc(strlen(home_dir) + 15); - if( sqliterc==0 ){ + zBuf = malloc(strlen(home_dir) + 15); + if( zBuf==0 ){ fprintf(stderr,"%s: out of memory!\n", Argv0); exit(1); } - sprintf(sqliterc,"%s/.sqliterc",home_dir); + sprintf(zBuf,"%s/.sqliterc",home_dir); free(home_dir); + sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"r"); - if(in && isatty(fileno(stdout))) { - printf("Loading resources from %s\n",sqliterc); + if( in ){ + if( isatty(fileno(stdout)) ){ + printf("Loading resources from %s\n",sqliterc); + } process_input(p,in); fclose(in); } @@ -1174,15 +1202,13 @@ void main_init(struct callback_data *data) { int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; - int origArgc = argc; - char **origArgv = argv; + const char *zInitFile = 0; + char *zFirstCmd = 0; int i; extern int sqliteOsFileExists(const char*); #ifdef __MACOS__ argc = ccommand(&argv); - origArgc = argc; - origArgv = argv; #endif Argv0 = argv[0]; @@ -1195,15 +1221,30 @@ int main(int argc, char **argv){ signal(SIGINT, interrupt_handler); #endif - /* Locate the name of the database file + /* Do an initial pass through the command-line argument to locate + ** the name of the database file, the name of the initialization file, + ** and the first command to execute. */ - for(i=1; i=2 && argv[1][0]=='-' ){ - if( argc>=3 && strcmp(argv[1],"-init")==0 ){ - /* If we get a -init to do, we have to pretend that - ** it replaced the .sqliterc file. Soooo, in order to - ** do that we need to start from scratch...*/ - main_init(&data); - - /* treat this file as the sqliterc... */ - process_sqliterc(&data,argv[2]); - - /* fix up the command line so we do not re-read - ** the option next time around... */ - { - int i = 1; - for(i=1;i<=argc-2;i++) { - argv[i] = argv[i+2]; - } - } - origArgc-=2; - - /* and reset the command line options to be re-read.*/ - argv = origArgv; - argc = origArgc; - - }else if( strcmp(argv[1],"-html")==0 ){ + for(i=1; i=3 && strcmp(argv[1],"-separator")==0 ){ - sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[2]); - argc -= 2; - argv += 2; - }else if( argc>=3 && strcmp(argv[1],"-nullvalue")==0 ){ - sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[2]); - argc -= 2; - argv += 2; - }else if( strcmp(argv[1],"-header")==0 ){ + }else if( strcmp(z,"-separator")==0 ){ + i++; + sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]); + }else if( strcmp(z,"-nullvalue")==0 ){ + i++; + sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); + }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; - argc--; - argv++; - }else if( strcmp(argv[1],"-noheader")==0 ){ + }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; - argc--; - argv++; - }else if( strcmp(argv[1],"-echo")==0 ){ + }else if( strcmp(z,"-echo")==0 ){ data.echoOn = 1; - argc--; - argv++; - }else if( strcmp(argv[1],"-version")==0 ){ + }else if( strcmp(z,"-version")==0 ){ printf("%s\n", sqlite_version); return 1; - }else if( strcmp(argv[1],"-help")==0 ){ + }else if( strcmp(z,"-help")==0 ){ usage(1); }else{ - fprintf(stderr,"%s: unknown option: %s\n", Argv0, argv[1]); + fprintf(stderr,"%s: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } } - if( argc<2 ){ - usage(0); - }else if( argc==3 ){ + if( zFirstCmd ){ /* Run just the command that follows the database name */ - if( argv[2][0]=='.' ){ - do_meta_command(argv[2], &data); + if( zFirstCmd[0]=='.' ){ + do_meta_command(zFirstCmd, &data); exit(0); }else{ int rc; open_db(&data); - rc = sqlite_exec(db, argv[2], callback, &data, &zErrMsg); + rc = sqlite_exec(db, zFirstCmd, callback, &data, &zErrMsg); if( rc!=0 && zErrMsg!=0 ){ fprintf(stderr,"SQL error: %s\n", zErrMsg); exit(1); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index e493a13fec..a2c6d1557c 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.54 2004/01/15 02:44:03 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.55 2004/02/01 01:22:52 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -773,6 +773,33 @@ void sqlite_progress_handler(sqlite*, int, int(*)(void*), void*); */ void *sqlite_commit_hook(sqlite*, int(*)(void*), void*); +/* +** Open an encrypted SQLite database. If pKey==0 or nKey==0, this routine +** is the same as sqlite_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +sqlite *sqlite_open_encrypted( + const char *zFilename, /* Name of the encrypted database */ + const void *pKey, /* Pointer to the key */ + int nKey, /* Number of bytes in the key */ + char **pzErrmsg /* Write error message here */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +int sqlite_rekey( + sqlite *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 70706306ad..6fb3c3addc 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.54 2004/01/15 02:44:03 drh Exp $ +** $Id: tclsqlite.c,v 1.55 2004/02/01 01:22:52 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -486,19 +486,21 @@ static int auth_callback( static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ SqliteDb *pDb = (SqliteDb*)cd; int choice; + int rc = TCL_OK; static const char *DB_strs[] = { "authorizer", "busy", "changes", "close", "commit_hook", "complete", "errorcode", "eval", "function", "last_insert_rowid", "onecolumn", "progress", - "timeout", "trace", 0 + "rekey", "timeout", "trace", + 0 }; enum DB_enum { DB_AUTHORIZER, DB_BUSY, DB_CHANGES, DB_CLOSE, DB_COMMIT_HOOK, DB_COMPLETE, DB_ERRORCODE, DB_EVAL, DB_FUNCTION, DB_LAST_INSERT_ROWID, DB_ONECOLUMN, DB_PROGRESS, - DB_TIMEOUT, DB_TRACE, + DB_REKEY, DB_TIMEOUT, DB_TRACE, }; if( objc<2 ){ @@ -747,7 +749,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ CallbackData cbData; char *zErrMsg; char *zSql; - int rc; #ifdef UTF_TRANSLATION_NEEDED Tcl_DString dSql; int i; @@ -865,7 +866,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** Return a single column from a single row of the given SQL query. */ case DB_ONECOLUMN: { - int rc; char *zSql; char *zErrMsg = 0; if( objc!=3 ){ @@ -875,7 +875,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ zSql = Tcl_GetStringFromObj(objv[2], 0); rc = sqlite_exec(pDb->db, zSql, DbEvalCallback3, interp, &zErrMsg); if( rc==SQLITE_ABORT ){ - /* Do nothing. This is normal. */ + rc = SQLITE_OK; }else if( zErrMsg ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); free(zErrMsg); @@ -887,6 +887,29 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* + ** $db rekey KEY + ** + ** Change the encryption key on the currently open database. + */ + case DB_REKEY: { + int nKey; + void *pKey; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "KEY"); + return TCL_ERROR; + } + pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); +#ifdef SQLITE_HAS_CRYPTO + rc = sqlite_rekey(pDb->db, pKey, nKey); + if( rc ){ + Tcl_AppendResult(interp, sqlite_error_string(rc), 0); + rc = TCL_ERROR; + } +#endif + break; + } + /* ** $db timeout MILLESECONDS ** @@ -940,11 +963,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } } /* End of the SWITCH statement */ - return TCL_OK; + return rc; } /* -** sqlite DBNAME FILENAME ?MODE? +** sqlite DBNAME FILENAME ?MODE? ?-key KEY? ** ** This is the main Tcl command. When the "sqlite" Tcl command is ** invoked, this routine runs to process that command. @@ -974,21 +997,34 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** not. Used by tests to make sure the library was compiled ** correctly. */ -static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ +static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ int mode; SqliteDb *p; + void *pKey = 0; + int nKey = 0; + const char *zArg; char *zErrMsg; + const char *zFile; char zBuf[80]; - if( argc==2 ){ - if( strcmp(argv[1],"-encoding")==0 ){ + if( objc==2 ){ + zArg = Tcl_GetStringFromObj(objv[1], 0); + if( strcmp(zArg,"-encoding")==0 ){ Tcl_AppendResult(interp,sqlite_encoding,0); return TCL_OK; } - if( strcmp(argv[1],"-version")==0 ){ + if( strcmp(zArg,"-version")==0 ){ Tcl_AppendResult(interp,sqlite_version,0); return TCL_OK; } - if( strcmp(argv[1],"-tcl-uses-utf")==0 ){ + if( strcmp(zArg,"-has-crypto")==0 ){ +#ifdef SQLITE_HAS_CRYPTO + Tcl_AppendResult(interp,"1",0); +#else + Tcl_AppendResult(interp,"0",0); +#endif + return TCL_OK; + } + if( strcmp(zArg,"-tcl-uses-utf")==0 ){ #ifdef TCL_UTF_MAX Tcl_AppendResult(interp,"1",0); #else @@ -997,14 +1033,26 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ return TCL_OK; } } - if( argc!=3 && argc!=4 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], - " HANDLE FILENAME ?MODE?\"", 0); + if( objc==5 || objc==6 ){ + zArg = Tcl_GetStringFromObj(objv[objc-2], 0); + if( strcmp(zArg,"-key")==0 ){ + pKey = Tcl_GetByteArrayFromObj(objv[objc-1], &nKey); + objc -= 2; + } + } + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, +#ifdef SQLITE_HAS_CRYPTO + "HANDLE FILENAME ?-key CRYPTOKEY?" +#else + "HANDLE FILENAME ?MODE?" +#endif + ); return TCL_ERROR; } - if( argc==3 ){ + if( objc==3 ){ mode = 0666; - }else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){ + }else if( Tcl_GetIntFromObj(interp, objv[3], &mode)!=TCL_OK ){ return TCL_ERROR; } zErrMsg = 0; @@ -1014,14 +1062,21 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ return TCL_ERROR; } memset(p, 0, sizeof(*p)); - p->db = sqlite_open(argv[2], mode, &zErrMsg); + zFile = Tcl_GetStringFromObj(objv[2], 0); +#ifdef SQLITE_HAS_CRYPTO + if( nKey>0 ){ + p->db = sqlite_open_encrypted(zFile, pKey, nKey, &zErrMsg); + }else +#endif + p->db = sqlite_open(zFile, mode, &zErrMsg); if( p->db==0 ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); Tcl_Free((char*)p); free(zErrMsg); return TCL_ERROR; } - Tcl_CreateObjCommand(interp, argv[1], DbObjCmd, (char*)p, DbDeleteCmd); + zArg = Tcl_GetStringFromObj(objv[1], 0); + Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); /* The return value is the value of the sqlite* pointer */ @@ -1063,13 +1118,13 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ */ int Sqlite_Init(Tcl_Interp *interp){ Tcl_InitStubs(interp, "8.0", 0); - Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0); + Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); Tcl_PkgProvide(interp, "sqlite", "2.0"); return TCL_OK; } int Tclsqlite_Init(Tcl_Interp *interp){ Tcl_InitStubs(interp, "8.0", 0); - Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0); + Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); Tcl_PkgProvide(interp, "sqlite", "2.0"); return TCL_OK; } diff --git a/test/tclsqlite.test b/test/tclsqlite.test index 5b358674dd..b8c90dc7b3 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -15,21 +15,26 @@ # interface is pretty well tested. This file contains some addition # tests for fringe issues that the main test suite does not cover. # -# $Id: tclsqlite.test,v 1.17 2004/01/15 02:44:04 drh Exp $ +# $Id: tclsqlite.test,v 1.18 2004/02/01 01:22:52 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Check the error messages generated by tclsqlite # +if {[sqlite -has-crypto]} { + set r "sqlite HANDLE FILENAME ?-key CRYPTOKEY?" +} else { + set r "sqlite HANDLE FILENAME ?MODE?" +} do_test tcl-1.1 { set v [catch {sqlite bogus} msg] lappend v $msg -} {1 {wrong # args: should be "sqlite HANDLE FILENAME ?MODE?"}} +} [list 1 "wrong # args: should be \"$r\""] do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg -} {1 {bad option "bogus": must be authorizer, busy, changes, close, commit_hook, complete, errorcode, eval, function, last_insert_rowid, onecolumn, progress, timeout, or trace}} +} {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}} do_test tcl-1.3 { execsql {CREATE TABLE t1(a int, b int)} execsql {INSERT INTO t1 VALUES(10,20)} @@ -98,8 +103,9 @@ do_test tcl-3.1 { INSERT INTO t1 SELECT a*2+1, b*2+1 FROM t1; INSERT INTO t1 SELECT a*2+3, b*2+3 FROM t1; } - db onecolumn {SELECT * FROM t1 ORDER BY a} -} {10} + set rc [catch {db onecolumn {SELECT * FROM t1 ORDER BY a}} msg] + lappend rc $msg +} {0 10} do_test tcl-3.2 { db onecolumn {SELECT * FROM t1 WHERE a<0} } {}