-C Rename\sall\stests\sso\sthat\sthe\sfirst\spart\sof\sthe\stest\sname\scorresponds\sto\sthe\nfile\sthat\scontains\sthat\stest.\s\sThis\smakes\sit\smuch\seasier\sto\sfind\sa\sparticular\ntest\safter\sit\sfail.\s(CVS\s749)
-D 2002-09-14T12:04:56
+C Do\snot\sput\sa\swrite\slock\son\sthe\smain\sdatabase\sfile\swhen\swriting\sto\sa\stemporary\ntable.\s(CVS\s750)
+D 2002-09-14T13:47:32
F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
F src/btree.c 8024b87635c2adf133f153f1bb595125ec1c7d7b
F src/btree.h 0ca6c2631338df62e4f7894252d9347ae234eda9
-F src/build.c 0116afe4f67687206364c4d1e88dc07aefc661de
-F src/delete.c c9f59ee217e062eb9de7b64b76b5cfff42b2f028
+F src/build.c d41b8da6b52ff84b235a785b226c37f3090ed276
+F src/delete.c aad9d4051ab46e6f6391ea5f7b8994a7c05bdd15
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
F src/expr.c e1327eb020a68ff7c49382e121ad4b71b3441b2a
F src/func.c e45cd908b9b723d9b91473d09e12c23f786b3fc2
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
-F src/insert.c a2f5455009904476b43ec5304a181b505235f72f
+F src/insert.c 764300a0bd8074a2174946c0bf8a550bd833397a
F src/main.c ff7c05ef88fa1374e5540ce20173ae8e1836f8a4
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
F src/os.c 091a89297bf80927cde146cd1dbf89c908864f3a
F src/shell.c 9e9a6eb6bca07f01e6472a603f908a0127ea50ff
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in d3999a9c6374675779058d6cfe5431131618e92b
-F src/sqliteInt.h 62177a08d332148b1d69cd040840aac45ad86a42
+F src/sqliteInt.h 54caf09fbb64b43a060637c46fb7464ea7b6f759
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
-F src/tclsqlite.c 79ceb1d0092cca22785cc00a0a596ba0aca6b363
+F src/tclsqlite.c fa646506f02509455c1e4a878d1303bd2d4c3ead
F src/test1.c a46e9f61915b32787c5d5a05a4b92e4dacc437d9
F src/test2.c 5fa694d130b3309e3f9c852f0a437750fcb5a006
F src/test3.c 540fa7fc3cb3732517b779b5f90ad9cc4303d0ab
F src/threadtest.c 72bce0a284647314847bbea44616ceb056bfb77f
F src/tokenize.c 62c98842447effe92eba9622bb2f9a2a8a4b97ad
-F src/trigger.c c90a292a4bef25e478fd5deda6d300319be6a023
-F src/update.c f07e6ed2c517c92871e54d3f5886d1cf56121b11
+F src/trigger.c 5ba917fc226b96065108da28186c2efaec53e481
+F src/update.c 881e4c8e7c786545da4fd2d95da19252b2e31137
F src/util.c c70d5da5357e01b58392faebae3c3620c1d71f14
-F src/vdbe.c 7e7392f2a92187ba1d2351fed0524c2dd607cffb
+F src/vdbe.c 8e567db1f36b2c6dda4719ebe53d565c087a5702
F src/vdbe.h b7584044223104ba7896a7f87b66daebdd6022ba
F src/where.c 53959c9d94adaf93b409271815e26eafa6ddd515
F test/all.test efd958d048c70a3247997c482f0b33561f7759f0
F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
F test/join.test 90a620f2a2d015e5139d5a4cde0eeb4cf62523bf
F test/limit.test 9f26f874bc765df5b3f5c92d26d1b12eac6d4cf9
-F test/lock.test 5079615ba0ef0899c4cbade42ffec291620a2819
+F test/lock.test 388a3a10962d2d571c0c1821cc35bf069ee73473
F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85
F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd
F test/memleak.test b4f59aa44488793b00feff2011d77d0f05b22468
F test/subselect.test f0fea8cf9f386d416d64d152e3c65f9116d0f50f
F test/table.test 10508e5e53fb7971b9fa6acb29d85748e545745c
F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
-F test/tclsqlite.test 6f4b9760681c7dbca52a18d0ab46a1679cdc79b9
+F test/tclsqlite.test 2441ab135e5af85110326b3e3b057e7257c144e1
F test/temptable.test 03b7bdb7d6ce2c658ad20c94b037652c6cad34e0
F test/tester.tcl 6f603d90881bd835ea27c568a7fecaa57dce91cc
F test/trans.test 10b53c77e2cc4ad9529c15fdcb390b8d5722ea65
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P ef7116751ddc4e82228c115b0a332ffb47a22ae5
-R 8189584121ea08912ee4948047822dc1
+P 6cb80ae10af60863cc25c22a6442ba1d43b7409c
+R 780c4a4a4908db085bbda24c5386e9ab
U drh
-Z 5104f4a49d2a5667b7c13a5b97f926e5
+Z 662e185948fcdefe24022119237f420c
-6cb80ae10af60863cc25c22a6442ba1d43b7409c
\ No newline at end of file
+3f253afe15d4f7392555f340a41d780d1248087f
\ No newline at end of file
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.111 2002/08/31 18:53:06 drh Exp $
+** $Id: build.c,v 1.112 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
** now.
*/
if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, isTemp);
if( !isTemp ){
sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0);
sqliteVdbeAddOp(v, OP_SetCookie, 0, 1);
};
Index *pIdx;
Trigger *pTrigger;
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, pTable->isTemp);
sqliteOpenMasterTable(v, pTable->isTemp);
/* Drop all triggers associated with the table being dropped */
pTrigger = pTable->pTrigger;
v = sqliteGetVdbe(pParse);
if( v==0 ) goto exit_create_index;
if( pTable!=0 ){
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, isTemp);
sqliteOpenMasterTable(v, isTemp);
}
sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
int base;
Table *pTab = pIndex->pTable;
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, pTab->isTemp);
sqliteOpenMasterTable(v, pTab->isTemp);
base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0);
v = sqliteGetVdbe(pParse);
if( v ){
int openOp;
- sqliteBeginWriteOperation(pParse, 1);
+ sqliteBeginWriteOperation(pParse, 1, pTab->isTemp);
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
sqliteVdbeDequoteP3(v, addr);
"within a transaction", 0);
return;
}
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, 0);
db->flags |= SQLITE_InTrans;
db->onError = onError;
}
** rollback the whole transaction. For operations where all constraints
** can be checked before any changes are made to the database, it is never
** necessary to undo a write and the checkpoint should not be set.
+**
+** The tempOnly flag indicates that only temporary tables will be changed
+** during this write operation. The primary database table is not
+** write-locked. Only the temporary database file gets a write lock.
+** Other processes can continue to read or write the primary database file.
*/
-void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint){
+void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){
Vdbe *v;
v = sqliteGetVdbe(pParse);
if( v==0 ) return;
if( pParse->trigStack ) return; /* if this is in a trigger */
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
- sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
- sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
- pParse->schemaVerified = 1;
+ sqliteVdbeAddOp(v, OP_Transaction, tempOnly, 0);
+ if( !tempOnly ){
+ sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
+ pParse->schemaVerified = 1;
+ }
}else if( setCheckpoint ){
sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
}
int addr;
int size = atoi(zRight);
if( size<0 ) size = -size;
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, 0);
sqliteVdbeAddOp(v, OP_Integer, size, 0);
sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
int addr;
int size = db->cache_size;
if( size<0 ) size = -size;
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, 0);
sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
-** $Id: delete.c,v 1.41 2002/07/19 18:52:41 drh Exp $
+** $Id: delete.c,v 1.42 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
if( v==0 ){
goto delete_from_cleanup;
}
- sqliteBeginWriteOperation(pParse, row_triggers_exist);
+ sqliteBeginWriteOperation(pParse, row_triggers_exist,
+ !row_triggers_exist && pTab->isTemp);
/* Initialize the counter of the number of rows deleted, if
** we are counting rows.
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
-** $Id: insert.c,v 1.66 2002/08/28 03:00:58 drh Exp $
+** $Id: insert.c,v 1.67 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
*/
v = sqliteGetVdbe(pParse);
if( v==0 ) goto insert_cleanup;
- sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist);
+ sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
+ !row_triggers_exist && pTab->isTemp);
/* if there are row triggers, allocate a temp table for new.* references. */
if( row_triggers_exist ){
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.145 2002/08/31 18:53:07 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.146 2002/09/14 13:47:32 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*);
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
-void sqliteBeginWriteOperation(Parse*, int);
+void sqliteBeginWriteOperation(Parse*, int, int);
void sqliteEndWriteOperation(Parse*);
Expr *sqliteExprDup(Expr*);
void sqliteTokenCopy(Token*, Token*);
*************************************************************************
** A TCL Interface to SQLite
**
-** $Id: tclsqlite.c,v 1.41 2002/09/03 19:43:24 drh Exp $
+** $Id: tclsqlite.c,v 1.42 2002/09/14 13:47:32 drh Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
# define UTF_TRANSLATION_NEEDED 1
#endif
+/*
+** New SQL functions can be created as TCL scripts. Each such function
+** is described by an instance of the following structure.
+*/
+typedef struct SqlFunc SqlFunc;
+struct SqlFunc {
+ Tcl_Interp *interp; /* The TCL interpret to execute the function */
+ char *zScript; /* The script to be run */
+ SqlFunc *pNext; /* Next function on the list of them all */
+};
+
/*
** There is one instance of this structure for each SQLite database
** that has been opened by the SQLite TCL interface.
sqlite *db; /* The "real" database structure */
Tcl_Interp *interp; /* The interpreter used for this database */
char *zBusy; /* The busy callback routine */
+ SqlFunc *pFunc; /* List of SQL functions */
};
/*
static void DbDeleteCmd(void *db){
SqliteDb *pDb = (SqliteDb*)db;
sqlite_close(pDb->db);
+ while( pDb->pFunc ){
+ SqlFunc *pFunc = pDb->pFunc;
+ pDb->pFunc = pFunc->pNext;
+ Tcl_Free((char*)pFunc);
+ }
if( pDb->zBusy ){
Tcl_Free(pDb->zBusy);
}
return 1;
}
+/*
+** This routine is called to evaluate an SQL function implemented
+** using TCL script.
+*/
+static void tclSqlFunc(sqlite_func *context, int argc, const char **argv){
+ SqlFunc *p = sqlite_user_data(context);
+ Tcl_DString cmd;
+ int i;
+ int rc;
+
+ Tcl_DStringInit(&cmd);
+ Tcl_DStringAppend(&cmd, p->zScript, -1);
+ for(i=0; i<argc; i++){
+ Tcl_DStringAppendElement(&cmd, argv[i] ? argv[i] : "");
+ }
+ rc = Tcl_Eval(p->interp, Tcl_DStringValue(&cmd));
+ if( rc ){
+ sqlite_set_result_error(context, Tcl_GetStringResult(p->interp), -1);
+ }else{
+ sqlite_set_result_string(context, Tcl_GetStringResult(p->interp), -1);
+ }
+}
+
/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database. This routine is invoked
int choice;
static const char *DB_strs[] = {
"busy", "changes", "close",
- "complete", "eval", "last_insert_rowid",
- "open_aux_file", "timeout", 0
+ "complete", "eval", "function",
+ "last_insert_rowid", "open_aux_file", "timeout",
+ 0
};
enum DB_enum {
DB_BUSY, DB_CHANGES, DB_CLOSE,
- DB_COMPLETE, DB_EVAL, DB_LAST_INSERT_ROWID,
- DB_OPEN_AUX_FILE, DB_TIMEOUT,
+ DB_COMPLETE, DB_EVAL, DB_FUNCTION,
+ DB_LAST_INSERT_ROWID, DB_OPEN_AUX_FILE, DB_TIMEOUT,
};
if( objc<2 ){
return rc;
}
+ /*
+ ** $db function NAME SCRIPT
+ **
+ ** Create a new SQL function called NAME. Whenever that function is
+ ** called, invoke SCRIPT to evaluate the function.
+ */
+ case DB_FUNCTION: {
+ SqlFunc *pFunc;
+ char *zName;
+ char *zScript;
+ int nScript;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
+ return TCL_ERROR;
+ }
+ zName = Tcl_GetStringFromObj(objv[2], 0);
+ zScript = Tcl_GetStringFromObj(objv[3], &nScript);
+ pFunc = (SqlFunc*)Tcl_Alloc( sizeof(*pFunc) + nScript + 1 );
+ if( pFunc==0 ) return TCL_ERROR;
+ pFunc->interp = interp;
+ pFunc->pNext = pDb->pFunc;
+ pFunc->zScript = (char*)&pFunc[1];
+ strcpy(pFunc->zScript, zScript);
+ sqlite_create_function(pDb->db, zName, -1, tclSqlFunc, pFunc);
+ sqlite_function_type(pDb->db, zName, SQLITE_NUMERIC);
+ break;
+ }
+
/*
** $db last_insert_rowid
**
/* Make an entry in the sqlite_master table */
v = sqliteGetVdbe(pParse);
if( v==0 ) goto trigger_cleanup;
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, 0);
sqliteOpenMasterTable(v, tab->isTemp);
addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME,
{ OP_Next, 0, ADDR(3), 0}, /* 7 */
};
- sqliteBeginWriteOperation(pParse, 0);
+ sqliteBeginWriteOperation(pParse, 0, 0);
sqliteOpenMasterTable(v, pTable->isTemp);
base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
sqliteVdbeChangeP3(v, base+1, zName, 0);
v = sqliteGetVdbe(pParse);
assert(v);
- sqliteBeginWriteOperation(pParse, 1);
+ sqliteBeginWriteOperation(pParse, 1, 0);
/* Allocate temp tables */
oldIdx = pParse->nTab++;
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.49 2002/07/21 23:09:55 danielk1977 Exp $
+** $Id: update.c,v 1.50 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
*/
v = sqliteGetVdbe(pParse);
if( v==0 ) goto update_cleanup;
- sqliteBeginWriteOperation(pParse, 1);
+ sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->isTemp);
/* Begin the database scan
*/
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.177 2002/09/08 17:23:43 drh Exp $
+** $Id: vdbe.c,v 1.178 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
unsigned uniqueCnt = 0; /* Used by OP_MakeRecord when P2!=0 */
int errorAction = OE_Abort; /* Recovery action to do in case of an error */
int undoTransOnError = 0; /* If error, either ROLLBACK or COMMIT */
+ int inTempTrans = 0; /* True if temp database is transactioned */
char zBuf[100]; /* Space to sprintf() an integer */
int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */
int returnDepth = 0; /* Next unused element in returnStack[] */
break;
}
-/* Opcode: Transaction * * *
+/* Opcode: Transaction P1 * *
**
** Begin a transaction. The transaction ends when a Commit or Rollback
** opcode is encountered. Depending on the ON CONFLICT setting, the
** transaction might also be rolled back if an error is encountered.
**
+** If P1 is true, then the transaction is started on the temporary
+** tables of the database only. The main database file is not write
+** locked and other processes can continue to read the main database
+** file.
+**
** A write lock is obtained on the database file when a transaction is
** started. No other process can read or write the file while the
** transaction is underway. Starting a transaction also creates a
*/
case OP_Transaction: {
int busy = 0;
- if( db->pBeTemp ){
+ if( db->pBeTemp && !inTempTrans ){
rc = sqliteBtreeBeginTrans(db->pBeTemp);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
+ inTempTrans = 1;
}
- do{
+ if( pOp->p1==0 ) do{
rc = sqliteBtreeBeginTrans(pBt);
switch( rc ){
case SQLITE_BUSY: {
/* Fall thru into the next case */
}
case SQLITE_OK: {
+ inTempTrans = 0;
busy = 0;
break;
}
*/
case OP_Commit: {
if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){
- rc = sqliteBtreeCommit(pBt);
+ rc = inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt);
}
if( rc==SQLITE_OK ){
sqliteCommitInternalChanges(db);
sqliteBtreeRollback(pBt);
sqliteRollbackInternalChanges(db);
}
+ inTempTrans = 0;
break;
}
# This file implements regression tests for SQLite library. The
# focus of this script is database locks.
#
-# $Id: lock.test,v 1.17 2002/08/29 23:59:50 drh Exp $
+# $Id: lock.test,v 1.18 2002/09/14 13:47:33 drh Exp $
set testdir [file dirname $argv0]
set rc [catch {db2 eval {SELECT * FROM t1}} msg]
lappend rc $msg $::callback_value
} {1 {database is locked} {1 2 3 4 5}}
+execsql {ROLLBACK}
+# When one thread is writing, other threads cannot read. Except if the
+# writing thread is writing to its temporary tables, the other threads
+# can still read.
+#
+proc tx_exec {sql} {
+ db2 eval $sql
+}
+do_test lock-5.1 {
+ execsql {
+ SELECT * FROM t1
+ }
+} {2 1}
+do_test lock-5.2 {
+ db function tx_exec tx_exec
+ catchsql {
+ INSERT INTO t1(a,b) SELECT 3, tx_exec('SELECT y FROM t2 LIMIT 1');
+ }
+} {1 {database is locked}}
+do_test lock-5.3 {
+ execsql {
+ CREATE TEMP TABLE t3(x);
+ SELECT * FROM t3;
+ }
+} {}
+do_test lock-5.4 {
+ catchsql {
+ INSERT INTO t3 SELECT tx_exec('SELECT y FROM t2 LIMIT 1');
+ }
+} {0 {}}
+do_test lock-5.5 {
+ execsql {
+ SELECT * FROM t3;
+ }
+} {8}
+do_test lock-5.6 {
+ catchsql {
+ UPDATE t1 SET a=tx_exec('SELECT x FROM t2');
+ }
+} {1 {database is locked}}
+do_test lock-5.7 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {2 1}
+do_test lock-5.8 {
+ catchsql {
+ UPDATE t3 SET x=tx_exec('SELECT x FROM t2');
+ }
+} {0 {}}
+do_test lock-5.9 {
+ execsql {
+ SELECT * FROM t3;
+ }
+} {9}
do_test lock-999.1 {
rename db2 {}
# 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.7 2002/06/25 19:31:18 drh Exp $
+# $Id: tclsqlite.test,v 1.8 2002/09/14 13:47:33 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
-} {1 {bad option "bogus": must be busy, changes, close, complete, eval, last_insert_rowid, open_aux_file, or timeout}}
+} {1 {bad option "bogus": must be busy, changes, close, complete, eval, function, last_insert_rowid, open_aux_file, or timeout}}
do_test tcl-1.3 {
execsql {CREATE TABLE t1(a int, b int)}
execsql {INSERT INTO t1 VALUES(10,20)}