/ Check-in [3baa3ff3]
Login

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

Overview
Comment:Add the sqlite3_rollback_hook() API. Still requires further testing. (CVS 2823)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:3baa3ff32435b64e7ae7646b17a98fef9296aaa0
User & Date: danielk1977 2005-12-16 06:54:02
Context
2005-12-16
15:24
Verify that the rollback-hook is invoked correctly when a malloc() failure occurs. (CVS 2824) check-in: 83c8ae5b user: danielk1977 tags: trunk
06:54
Add the sqlite3_rollback_hook() API. Still requires further testing. (CVS 2823) check-in: 3baa3ff3 user: danielk1977 tags: trunk
01:06
Initial infrastructure for recognizing DESC indices and being able to read and write older databases that specify DESC indices but do not really use them. Nothing is close to working yet. (CVS 2822) check-in: cd110aa2 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to main.mk.

129
130
131
132
133
134
135
136
137
138
139
140
141


142
143
144
145
146
147
148
  $(TOP)/src/printf.c \
  $(TOP)/src/test1.c \
  $(TOP)/src/test2.c \
  $(TOP)/src/test3.c \
  $(TOP)/src/test4.c \
  $(TOP)/src/test5.c \
  $(TOP)/src/test6.c \
  $(TOP)/src/test_async.c \
  $(TOP)/src/utf.c \
  $(TOP)/src/util.c \
  $(TOP)/src/vdbe.c \
  $(TOP)/src/md5.c \
  $(TOP)/src/where.c



# Header files used by all library source files.
#
HDR = \
   sqlite3.h  \
   $(TOP)/src/btree.h \
   $(TOP)/src/hash.h \







<





>
>







129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144
145
146
147
148
149
  $(TOP)/src/printf.c \
  $(TOP)/src/test1.c \
  $(TOP)/src/test2.c \
  $(TOP)/src/test3.c \
  $(TOP)/src/test4.c \
  $(TOP)/src/test5.c \
  $(TOP)/src/test6.c \

  $(TOP)/src/utf.c \
  $(TOP)/src/util.c \
  $(TOP)/src/vdbe.c \
  $(TOP)/src/md5.c \
  $(TOP)/src/where.c

#  $(TOP)/src/test_async.c 

# Header files used by all library source files.
#
HDR = \
   sqlite3.h  \
   $(TOP)/src/btree.h \
   $(TOP)/src/hash.h \

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
4902
4903
4904
4905
4906
4907
4908

4909
4910
4911

4912
4913
4914
4915
4916
4917
4918
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.273 2005/12/09 20:02:05 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    if( pCur->pgnoRoot==(Pgno)iTable ){
      if( pCur->wrFlag==0 ) return SQLITE_LOCKED;
      moveToRoot(pCur);
    }
  }
  rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);

  if( rc ){
    sqlite3BtreeRollback(pBt);
  }

  return rc;
}

/*
** Erase all information in a table and add the root of the table to
** the freelist.  Except, the root of the principle table (the one on
** page 1) is never added to the freelist.







|







 







>



>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.274 2005/12/16 06:54:02 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    if( pCur->pgnoRoot==(Pgno)iTable ){
      if( pCur->wrFlag==0 ) return SQLITE_LOCKED;
      moveToRoot(pCur);
    }
  }
  rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
#if 0
  if( rc ){
    sqlite3BtreeRollback(pBt);
  }
#endif
  return rc;
}

/*
** Erase all information in a table and add the root of the table to
** the freelist.  Except, the root of the principle table (the one on
** page 1) is never added to the freelist.

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
206
207
208
209
210
211
212

213






214
215
216
217
218
219
220
...
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
...
548
549
550
551
552
553
554
555
556
557
558
559

560
561

562
563














564
565
566
567
568
569
570
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.310 2005/12/15 15:22:09 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** The following constant value is used by the SQLITE_BIGENDIAN and
................................................................................
  int i;
  for(i=0; i<db->nDb; i++){
    if( db->aDb[i].pBt ){
      sqlite3BtreeRollback(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }

  sqlite3ResetInternalSchema(db, 0);






}

/*
** Return a static string that describes the kind of error specified in the
** argument.
*/
const char *sqlite3ErrStr(int rc){
................................................................................
  return pOld;
}
#endif /* SQLITE_OMIT_TRACE */

/*** EXPERIMENTAL ***
**
** Register a function to be invoked when a transaction comments.
** If either function returns non-zero, then the commit becomes a
** rollback.
*/
void *sqlite3_commit_hook(
  sqlite3 *db,              /* Attach the hook to this database */
  int (*xCallback)(void*),  /* Function to invoke on each commit */
  void *pArg                /* Argument to the function */
){
................................................................................
  return pOld;
}

/*
** Register a callback to be invoked each time a row is updated,
** inserted or deleted using this database connection.
*/
void sqlite3_update_hook(
  sqlite3 *db,              /* Attach the hook to this database */
  void (*xCallback)(void*,int,char const *,char const *,sqlite_int64),
  void *pArg                /* Argument to the function */
){

  db->xUpdateCallback = xCallback;
  db->pUpdateArg = pArg;

}
















/*
** This routine is called to create a connection to a database BTree
** driver.  If zFilename is the name of a file, then that file is
** opened and used.  If zFilename is the magic name ":memory:" then
** the database is stored in memory (and is thus forgotten as soon as
** the connection is closed.)  If zFilename is NULL then the database







|







 







>
|
>
>
>
>
>
>







 







|







 







|




>


>


>
>
>
>
>
>
>
>
>
>
>
>
>
>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.311 2005/12/16 06:54:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** The following constant value is used by the SQLITE_BIGENDIAN and
................................................................................
  int i;
  for(i=0; i<db->nDb; i++){
    if( db->aDb[i].pBt ){
      sqlite3BtreeRollback(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  if( db->flags&SQLITE_InternChanges ){
    sqlite3ResetInternalSchema(db, 0);
  }

  /* If one has been configured, invoke the rollback-hook callback */
  if( db->xRollbackCallback ){
    db->xRollbackCallback(db->pRollbackArg);
  }
}

/*
** Return a static string that describes the kind of error specified in the
** argument.
*/
const char *sqlite3ErrStr(int rc){
................................................................................
  return pOld;
}
#endif /* SQLITE_OMIT_TRACE */

/*** EXPERIMENTAL ***
**
** Register a function to be invoked when a transaction comments.
** If the invoked function returns non-zero, then the commit becomes a
** rollback.
*/
void *sqlite3_commit_hook(
  sqlite3 *db,              /* Attach the hook to this database */
  int (*xCallback)(void*),  /* Function to invoke on each commit */
  void *pArg                /* Argument to the function */
){
................................................................................
  return pOld;
}

/*
** Register a callback to be invoked each time a row is updated,
** inserted or deleted using this database connection.
*/
void *sqlite3_update_hook(
  sqlite3 *db,              /* Attach the hook to this database */
  void (*xCallback)(void*,int,char const *,char const *,sqlite_int64),
  void *pArg                /* Argument to the function */
){
  void *pRet = db->pUpdateArg;
  db->xUpdateCallback = xCallback;
  db->pUpdateArg = pArg;
  return pRet;
}

/*
** Register a callback to be invoked each time a transaction is rolled
** back by this database connection.
*/
void *sqlite3_rollback_hook(
  sqlite3 *db,              /* Attach the hook to this database */
  void (*xCallback)(void*), /* Callback function */
  void *pArg                /* Argument to the function */
){
  void *pRet = db->pRollbackArg;
  db->xRollbackCallback = xCallback;
  db->pRollbackArg = pArg;
  return pRet;
}

/*
** This routine is called to create a connection to a database BTree
** driver.  If zFilename is the name of a file, then that file is
** opened and used.  If zFilename is the magic name ":memory:" then
** the database is stored in memory (and is thus forgotten as soon as
** the connection is closed.)  If zFilename is NULL then the database

Changes to src/sqlite.h.in.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1305
1306
1307
1308
1309
1310
1311



1312
1313
1314
1315
1316
1317


1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.146 2005/12/15 15:22:09 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** fourth arguments to the callback contain pointers to the database and 
** table name containing the affected row. The final callback parameter is 
** the rowid of the row. In the case of an update, this is the rowid after 
** the update takes place.
**
** The update hook is not invoked when internal system tables are
** modified (i.e. sqlite_master and sqlite_sequence).



*/
void sqlite3_update_hook(
  sqlite3*, 
  void(*)(void *,int ,char const *,char const *,sqlite_int64),
  void*
);



/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif







|







 







>
>
>

|




>
>













8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.147 2005/12/16 06:54:02 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** fourth arguments to the callback contain pointers to the database and 
** table name containing the affected row. The final callback parameter is 
** the rowid of the row. In the case of an update, this is the rowid after 
** the update takes place.
**
** The update hook is not invoked when internal system tables are
** modified (i.e. sqlite_master and sqlite_sequence).
**
** If another function was previously registered, its pArg value is returned.
** Otherwise NULL is returned.
*/
void *sqlite3_update_hook(
  sqlite3*, 
  void(*)(void *,int ,char const *,char const *,sqlite_int64),
  void*
);

void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
436
437
438
439
440
441
442
443
444


445
446
447
448
449
450
451
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.438 2005/12/16 01:06:17 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
** Setting NDEBUG makes the code smaller and run faster.  So the following
................................................................................
  } init;
  struct Vdbe *pVdbe;           /* List of active virtual machines */
  int activeVdbeCnt;            /* Number of vdbes currently executing */
  void (*xTrace)(void*,const char*);        /* Trace function */
  void *pTraceArg;                          /* Argument to the trace function */
  void (*xProfile)(void*,const char*,u64);  /* Profiling function */
  void *pProfileArg;                        /* Argument to profile function */
  void *pCommitArg;             /* Argument to xCommitCallback() */   
  int (*xCommitCallback)(void*);/* Invoked at every commit. */


  void *pUpdateArg;
  void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
  void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
  void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
  void *pCollNeededArg;
  sqlite3_value *pErr;          /* Most recent error message */
  char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */







|







 







|
|
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.439 2005/12/16 06:54:02 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
** Setting NDEBUG makes the code smaller and run faster.  So the following
................................................................................
  } init;
  struct Vdbe *pVdbe;           /* List of active virtual machines */
  int activeVdbeCnt;            /* Number of vdbes currently executing */
  void (*xTrace)(void*,const char*);        /* Trace function */
  void *pTraceArg;                          /* Argument to the trace function */
  void (*xProfile)(void*,const char*,u64);  /* Profiling function */
  void *pProfileArg;                        /* Argument to profile function */
  void *pCommitArg;                 /* Argument to xCommitCallback() */   
  int (*xCommitCallback)(void*);    /* Invoked at every commit. */
  void *pRollbackArg;               /* Argument to xRollbackCallback() */   
  void (*xRollbackCallback)(void*); /* Invoked at every commit. */
  void *pUpdateArg;
  void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
  void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
  void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
  void *pCollNeededArg;
  sqlite3_value *pErr;          /* Most recent error message */
  char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */

Changes to src/tclsqlite.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
96
97
98
99
100
101
102

103
104
105
106
107
108
109
...
210
211
212
213
214
215
216



217
218
219
220
221
222
223
...
299
300
301
302
303
304
305








306
307
308
309
310
311
312
...
649
650
651
652
653
654
655
656
657
658
659

660
661
662
663
664
665
666
667
668
669
670

671
672
673
674
675
676
677
....
1868
1869
1870
1871
1872
1873
1874

1875
1876












1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888

1889
1890
1891
1892
1893
1894
1895
1896




1897
1898
1899
1900
1901
1902
1903
....
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.139 2005/12/15 15:22:10 danielk1977 Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "hash.h"
#include "tcl.h"
#include <stdlib.h>
................................................................................
  char *zTrace;              /* The trace callback routine */
  char *zProfile;            /* The profile callback routine */
  char *zProgress;           /* The progress callback routine */
  char *zAuth;               /* The authorization callback routine */
  char *zNull;               /* Text to substitute for an SQL NULL value */
  SqlFunc *pFunc;            /* List of SQL functions */
  Tcl_Obj *pUpdateHook;      /* Update hook script (if any) */

  SqlCollate *pCollate;      /* List of SQL collation functions */
  int rc;                    /* Return code of most recent sqlite3_exec() */
  Tcl_Obj *pCollateNeeded;   /* Collation needed script */
  SqlPreparedStmt *stmtList; /* List of prepared statements*/
  SqlPreparedStmt *stmtLast; /* Last statement in the list */
  int maxStmt;               /* The next maximum number of stmtList */
  int nStmt;                 /* Number of statements in stmtList */
................................................................................
  }
  if( pDb->zNull ){
    Tcl_Free(pDb->zNull);
  }
  if( pDb->pUpdateHook ){
    Tcl_DecrRefCount(pDb->pUpdateHook);
  }



  if( pDb->pCollateNeeded ){
    Tcl_DecrRefCount(pDb->pCollateNeeded);
  }
  Tcl_Free((char*)pDb);
}

/*
................................................................................

  rc = Tcl_Eval(pDb->interp, pDb->zCommit);
  if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
    return 1;
  }
  return 0;
}









static void DbUpdateHandler(
  void *p, 
  int op,
  const char *zDb, 
  const char *zTbl, 
  sqlite_int64 rowid
................................................................................
  static const char *DB_strs[] = {
    "authorizer",         "busy",              "cache",
    "changes",            "close",             "collate",
    "collation_needed",   "commit_hook",       "complete",
    "copy",               "errorcode",         "eval",
    "exists",             "function",          "last_insert_rowid",
    "nullvalue",          "onecolumn",         "profile",
    "progress",           "rekey",             "soft_heap_limit",
    "timeout",            "total_changes",     "trace",
    "transaction",        "update_hook",       "version",
    0                    

  };
  enum DB_enum {
    DB_AUTHORIZER,        DB_BUSY,             DB_CACHE,
    DB_CHANGES,           DB_CLOSE,            DB_COLLATE,
    DB_COLLATION_NEEDED,  DB_COMMIT_HOOK,      DB_COMPLETE,
    DB_COPY,              DB_ERRORCODE,        DB_EVAL,
    DB_EXISTS,            DB_FUNCTION,         DB_LAST_INSERT_ROWID,
    DB_NULLVALUE,         DB_ONECOLUMN,        DB_PROFILE,
    DB_PROGRESS,          DB_REKEY,            DB_SOFT_HEAP_LIMIT,
    DB_TIMEOUT,           DB_TOTAL_CHANGES,    DB_TRACE,
    DB_TRANSACTION,       DB_UPDATE_HOOK,      DB_VERSION

  };
  /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
................................................................................
      sqlite3_exec(pDb->db, zEnd, 0, 0, 0);
    }
    break;
  }

  /*
  **    $db update_hook ?script?

  */
  case DB_UPDATE_HOOK: {












    if( objc!=2 && objc!=3 ){
       Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
       return TCL_ERROR;
    }
    if( pDb->pUpdateHook ){
      Tcl_SetObjResult(interp, pDb->pUpdateHook);
      if( objc==3 ){
        Tcl_DecrRefCount(pDb->pUpdateHook);
        pDb->pUpdateHook = 0;
      }
    }
    if( objc==3 ){

      if( Tcl_GetCharLength(objv[2])>0 ){
        pDb->pUpdateHook = objv[2];
        Tcl_IncrRefCount(pDb->pUpdateHook);
        sqlite3_update_hook(pDb->db, DbUpdateHandler, pDb);
      }else{
        sqlite3_update_hook(pDb->db, 0, 0);
      }
    }




    break;
  }

  /*    $db version
  **
  ** Return the version string for this database.
  */
................................................................................

    Sqlitetest1_Init(interp);
    Sqlitetest2_Init(interp);
    Sqlitetest3_Init(interp);
    Sqlitetest4_Init(interp);
    Sqlitetest5_Init(interp);
    Sqlitetest6_Init(interp);
    Sqlitetestasync_Init(interp);
    Md5_Init(interp);
#ifdef SQLITE_SSE
    Sqlitetestsse_Init(interp);
#endif
  }
#endif
  if( argc>=2 || TCLSH==2 ){







|







 







>







 







>
>
>







 







>
>
>
>
>
>
>
>







 







|
|
|
<
>








|
|
|
>







 







>

|
>
>
>
>
>
>
>
>
>
>
>
>




|
|

|
|



>

|
|
<
<
<


>
>
>
>







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
661
662
663
664
665
666
667
668
669
670

671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
....
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918



1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
....
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.140 2005/12/16 06:54:03 danielk1977 Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "hash.h"
#include "tcl.h"
#include <stdlib.h>
................................................................................
  char *zTrace;              /* The trace callback routine */
  char *zProfile;            /* The profile callback routine */
  char *zProgress;           /* The progress callback routine */
  char *zAuth;               /* The authorization callback routine */
  char *zNull;               /* Text to substitute for an SQL NULL value */
  SqlFunc *pFunc;            /* List of SQL functions */
  Tcl_Obj *pUpdateHook;      /* Update hook script (if any) */
  Tcl_Obj *pRollbackHook;    /* Rollback hook script (if any) */
  SqlCollate *pCollate;      /* List of SQL collation functions */
  int rc;                    /* Return code of most recent sqlite3_exec() */
  Tcl_Obj *pCollateNeeded;   /* Collation needed script */
  SqlPreparedStmt *stmtList; /* List of prepared statements*/
  SqlPreparedStmt *stmtLast; /* Last statement in the list */
  int maxStmt;               /* The next maximum number of stmtList */
  int nStmt;                 /* Number of statements in stmtList */
................................................................................
  }
  if( pDb->zNull ){
    Tcl_Free(pDb->zNull);
  }
  if( pDb->pUpdateHook ){
    Tcl_DecrRefCount(pDb->pUpdateHook);
  }
  if( pDb->pRollbackHook ){
    Tcl_DecrRefCount(pDb->pRollbackHook);
  }
  if( pDb->pCollateNeeded ){
    Tcl_DecrRefCount(pDb->pCollateNeeded);
  }
  Tcl_Free((char*)pDb);
}

/*
................................................................................

  rc = Tcl_Eval(pDb->interp, pDb->zCommit);
  if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
    return 1;
  }
  return 0;
}

static void DbRollbackHandler(void *clientData){
  SqliteDb *pDb = (SqliteDb*)clientData;
  assert(pDb->pRollbackHook);
  if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
    Tcl_BackgroundError(pDb->interp);
  }
}

static void DbUpdateHandler(
  void *p, 
  int op,
  const char *zDb, 
  const char *zTbl, 
  sqlite_int64 rowid
................................................................................
  static const char *DB_strs[] = {
    "authorizer",         "busy",              "cache",
    "changes",            "close",             "collate",
    "collation_needed",   "commit_hook",       "complete",
    "copy",               "errorcode",         "eval",
    "exists",             "function",          "last_insert_rowid",
    "nullvalue",          "onecolumn",         "profile",
    "progress",           "rekey",             "rollback_hook",
    "soft_heap_limit",    "timeout",           "total_changes",
    "trace",              "transaction",       "update_hook",       

    "version",            0                    
  };
  enum DB_enum {
    DB_AUTHORIZER,        DB_BUSY,             DB_CACHE,
    DB_CHANGES,           DB_CLOSE,            DB_COLLATE,
    DB_COLLATION_NEEDED,  DB_COMMIT_HOOK,      DB_COMPLETE,
    DB_COPY,              DB_ERRORCODE,        DB_EVAL,
    DB_EXISTS,            DB_FUNCTION,         DB_LAST_INSERT_ROWID,
    DB_NULLVALUE,         DB_ONECOLUMN,        DB_PROFILE,
    DB_PROGRESS,          DB_REKEY,            DB_ROLLBACK_HOOK,
    DB_SOFT_HEAP_LIMIT,   DB_TIMEOUT,          DB_TOTAL_CHANGES,    
    DB_TRACE,             DB_TRANSACTION,      DB_UPDATE_HOOK,      
    DB_VERSION
  };
  /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
................................................................................
      sqlite3_exec(pDb->db, zEnd, 0, 0, 0);
    }
    break;
  }

  /*
  **    $db update_hook ?script?
  **    $db rollback_hook ?script?
  */
  case DB_UPDATE_HOOK: 
  case DB_ROLLBACK_HOOK: {

    /* set ppHook to point at pUpdateHook or pRollbackHook, depending on 
    ** whether [$db update_hook] or [$db rollback_hook] was invoked.
    */
    Tcl_Obj **ppHook; 
    if( choice==DB_UPDATE_HOOK ){
      ppHook = &pDb->pUpdateHook;
    }else{
      ppHook = &pDb->pRollbackHook;
    }

    if( objc!=2 && objc!=3 ){
       Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
       return TCL_ERROR;
    }
    if( *ppHook ){
      Tcl_SetObjResult(interp, *ppHook);
      if( objc==3 ){
        Tcl_DecrRefCount(*ppHook);
        *ppHook = 0;
      }
    }
    if( objc==3 ){
      assert( !(*ppHook) );
      if( Tcl_GetCharLength(objv[2])>0 ){
        *ppHook = objv[2];
        Tcl_IncrRefCount(*ppHook);



      }
    }

    sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
    sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);

    break;
  }

  /*    $db version
  **
  ** Return the version string for this database.
  */
................................................................................

    Sqlitetest1_Init(interp);
    Sqlitetest2_Init(interp);
    Sqlitetest3_Init(interp);
    Sqlitetest4_Init(interp);
    Sqlitetest5_Init(interp);
    Sqlitetest6_Init(interp);
    /* Sqlitetestasync_Init(interp); */
    Md5_Init(interp);
#ifdef SQLITE_SSE
    Sqlitetestsse_Init(interp);
#endif
  }
#endif
  if( argc>=2 || TCLSH==2 ){

Changes to src/vdbeaux.c.

1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240









1241
1242
1243
1244
1245
1246

1247
1248
1249
1250
1251
1252
1253
    }else{
      xFunc = sqlite3BtreeRollback;
      db->autoCommit = 1;
      abortOtherActiveVdbes(p);
    }
  }

  /* If xFunc is not NULL, then it is one of sqlite3BtreeRollback,
  ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
  ** each backend. If an error occurs and the return code is still
  ** SQLITE_OK, set the return code to the new error value.
  */









  for(i=0; xFunc && i<db->nDb; i++){ 
    int rc;
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      rc = xFunc(pBt);
      if( p->rc==SQLITE_OK ) p->rc = rc;

    }
  }

  /* If this was an INSERT, UPDATE or DELETE, set the change counter. */
  if( p->changeCntOn && p->pc>=0 ){
    if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
      sqlite3VdbeSetChanges(db, p->nChange);







|




>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
>







1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
    }else{
      xFunc = sqlite3BtreeRollback;
      db->autoCommit = 1;
      abortOtherActiveVdbes(p);
    }
  }

  /* If xFunc is not NULL, then it is one of 
  ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
  ** each backend. If an error occurs and the return code is still
  ** SQLITE_OK, set the return code to the new error value.
  */
  assert(!xFunc ||
    xFunc==sqlite3BtreeCommitStmt ||
    xFunc==sqlite3BtreeRollbackStmt ||
    xFunc==sqlite3BtreeRollback
  );
  if( xFunc==sqlite3BtreeRollback ){
    assert( p->rc!=SQLITE_OK );
    sqlite3RollbackAll(db);
  }else{
    for(i=0; xFunc && i<db->nDb; i++){ 
      int rc;
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = xFunc(pBt);
        if( p->rc==SQLITE_OK ) p->rc = rc;
      }
    }
  }

  /* If this was an INSERT, UPDATE or DELETE, set the change counter. */
  if( p->changeCntOn && p->pc>=0 ){
    if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
      sqlite3VdbeSetChanges(db, p->nChange);

Changes to test/hook.test.

9
10
11
12
13
14
15
16
17

18
19

20
21
22
23
24
25
26
..
87
88
89
90
91
92
93



94
95
96







97


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214



















































215
216
#
#***********************************************************************
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# The focus of the tests in this file is the  following interface:
#
#      sqlite_commit_hook
#      sqlite_update_hook (tests hook-4 onwards)

#
# $Id: hook.test,v 1.6 2005/12/15 15:22:10 danielk1977 Exp $


set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test hook-1.2 {
  db commit_hook
} {}
................................................................................
  set ::commit_cnt {}
  execsql {
    INSERT INTO t2 VALUES(7,8);
  }
  set ::commit_cnt
} {}




# Very simple tests. Test that the update hook is invoked correctly for INSERT,
# DELETE and UPDATE statements, including DELETE statements with no WHERE
# clause.







#


do_test hook-4.1 {
  catchsql {
    DROP TABLE t1;
  }
  execsql {
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
    INSERT INTO t1 VALUES(1, 'one');
    INSERT INTO t1 VALUES(2, 'two');
    INSERT INTO t1 VALUES(3, 'three');
  }
  db update_hook [list lappend ::update_hook]
} {}
do_test hook-4.2 {
  execsql {
    INSERT INTO t1 VALUES(4, 'four');
    DELETE FROM t1 WHERE b = 'two';
    UPDATE t1 SET b = '' WHERE a = 1 OR a = 3;
    DELETE FROM t1;
  }
  set ::update_hook
................................................................................
    UPDATE main t1 1 \
    UPDATE main t1 3 \
    DELETE main t1 1 \
    DELETE main t1 3 \
    DELETE main t1 4 \
]

# Check that the update-hook is invoked for rows modified by trigger
# bodies.
#
set ::update_hook {}
do_test hook-5.1 {
  catchsql {
    DROP TABLE t2;
  }
  execsql {
    CREATE TABLE t2(c INTEGER PRIMARY KEY, d);
    CREATE TRIGGER t1_trigger AFTER INSERT ON t1 BEGIN
      INSERT INTO t2 VALUES(new.a, new.b);
      UPDATE t2 SET d = d || ' via trigger' WHERE new.a = c;
      DELETE FROM t2 WHERE new.a = c;
    END;
  }
} {}
do_test hook-5.2 {
  execsql {
    INSERT INTO t1 VALUES(1, 'one');
    INSERT INTO t1 VALUES(2, 'two');
  }
  set ::update_hook
} [list \
    INSERT main t1 1 \
................................................................................
    DELETE main t2 1 \
    INSERT main t1 2 \
    INSERT main t2 2 \
    UPDATE main t2 2 \
    DELETE main t2 2 \
]


set ::update_hook {}
do_test hook-6.1 {
  file delete -force test2.db
  execsql {
    ATTACH 'test2.db' AS aux;
    CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b);
    INSERT INTO aux.t3 SELECT * FROM t1;
    UPDATE t3 SET b = 'two or so' WHERE a = 2;
    DELETE FROM t3;
................................................................................
    INSERT aux t3 1 \
    INSERT aux t3 2 \
    UPDATE aux t3 2 \
    DELETE aux t3 1 \
    DELETE aux t3 2 \
]

# Do some sorting, grouping, compound queries, population and depopulation 
# of indices, to make sure the update-hook is not invoked incorrectly.
#
set ::update_hook {}
do_test hook-7.1 {
  execsql {
    DROP TRIGGER t1_trigger;
    CREATE INDEX t1_i ON t1(b);
    INSERT INTO t1 VALUES(3, 'three');
    UPDATE t1 SET b = '';
    DELETE FROM t1 WHERE a > 1;
  }
................................................................................
    UPDATE main t1 1 \
    UPDATE main t1 2 \
    UPDATE main t1 3 \
    DELETE main t1 2 \
    DELETE main t1 3 \
]
set ::update_hook {}
do_test hook-7.2 {
  execsql {
    SELECT * FROM t1 UNION SELECT * FROM t3;
    SELECT * FROM t1 UNION ALL SELECT * FROM t3;
    SELECT * FROM t1 INTERSECT SELECT * FROM t3;
    SELECT * FROM t1 EXCEPT SELECT * FROM t3;
    SELECT * FROM t1 ORDER BY b;
    SELECT * FROM t1 GROUP BY b;
  }
  set ::update_hook
} [list]





















































finish_test








|
|
>

<
>







 







>
>
>
|
|
<
>
>
>
>
>
>
>

>
>
|











|







 







<
<
<

|












|







 







>

|







 







|
|
<

|







 







|










>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


9
10
11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
..
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
133
134
135
136
137
138
139



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
...
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#
#***********************************************************************
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# The focus of the tests in this file is the  following interface:
#
#      sqlite_commit_hook    (tests hook-1..hook-3 inclusive)
#      sqlite_update_hook    (tests hook-4-*)
#      sqlite_rollback_hook  (tests hook-5.*)
#

# $Id: hook.test,v 1.7 2005/12/16 06:54:03 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test hook-1.2 {
  db commit_hook
} {}
................................................................................
  set ::commit_cnt {}
  execsql {
    INSERT INTO t2 VALUES(7,8);
  }
  set ::commit_cnt
} {}

#----------------------------------------------------------------------------
# Tests for the update-hook.
#
# 4.1.* - Very simple tests. Test that the update hook is invoked correctly 
#         for INSERT, DELETE and UPDATE statements, including DELETE 

#         statements with no WHERE clause.
# 4.2.* - Check that the update-hook is invoked for rows modified by trigger
#         bodies. Also that the database name is correctly reported when 
#         an attached database is modified.
# 4.3.* - Do some sorting, grouping, compound queries, population and 
#         depopulation of indices, to make sure the update-hook is not 
#         invoked incorrectly.
#

# Simple tests
do_test hook-4.1.1 {
  catchsql {
    DROP TABLE t1;
  }
  execsql {
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
    INSERT INTO t1 VALUES(1, 'one');
    INSERT INTO t1 VALUES(2, 'two');
    INSERT INTO t1 VALUES(3, 'three');
  }
  db update_hook [list lappend ::update_hook]
} {}
do_test hook-4.1.2 {
  execsql {
    INSERT INTO t1 VALUES(4, 'four');
    DELETE FROM t1 WHERE b = 'two';
    UPDATE t1 SET b = '' WHERE a = 1 OR a = 3;
    DELETE FROM t1;
  }
  set ::update_hook
................................................................................
    UPDATE main t1 1 \
    UPDATE main t1 3 \
    DELETE main t1 1 \
    DELETE main t1 3 \
    DELETE main t1 4 \
]




set ::update_hook {}
do_test hook-4.2.1 {
  catchsql {
    DROP TABLE t2;
  }
  execsql {
    CREATE TABLE t2(c INTEGER PRIMARY KEY, d);
    CREATE TRIGGER t1_trigger AFTER INSERT ON t1 BEGIN
      INSERT INTO t2 VALUES(new.a, new.b);
      UPDATE t2 SET d = d || ' via trigger' WHERE new.a = c;
      DELETE FROM t2 WHERE new.a = c;
    END;
  }
} {}
do_test hook-4.2.2 {
  execsql {
    INSERT INTO t1 VALUES(1, 'one');
    INSERT INTO t1 VALUES(2, 'two');
  }
  set ::update_hook
} [list \
    INSERT main t1 1 \
................................................................................
    DELETE main t2 1 \
    INSERT main t1 2 \
    INSERT main t2 2 \
    UPDATE main t2 2 \
    DELETE main t2 2 \
]

# Triggers + ATTACH
set ::update_hook {}
do_test hook-4.2.3 {
  file delete -force test2.db
  execsql {
    ATTACH 'test2.db' AS aux;
    CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b);
    INSERT INTO aux.t3 SELECT * FROM t1;
    UPDATE t3 SET b = 'two or so' WHERE a = 2;
    DELETE FROM t3;
................................................................................
    INSERT aux t3 1 \
    INSERT aux t3 2 \
    UPDATE aux t3 2 \
    DELETE aux t3 1 \
    DELETE aux t3 2 \
]

# Test that other vdbe operations involving btree structures do not 
# incorrectly invoke the update-hook.

set ::update_hook {}
do_test hook-4.3.1 {
  execsql {
    DROP TRIGGER t1_trigger;
    CREATE INDEX t1_i ON t1(b);
    INSERT INTO t1 VALUES(3, 'three');
    UPDATE t1 SET b = '';
    DELETE FROM t1 WHERE a > 1;
  }
................................................................................
    UPDATE main t1 1 \
    UPDATE main t1 2 \
    UPDATE main t1 3 \
    DELETE main t1 2 \
    DELETE main t1 3 \
]
set ::update_hook {}
do_test hook-4.3.2 {
  execsql {
    SELECT * FROM t1 UNION SELECT * FROM t3;
    SELECT * FROM t1 UNION ALL SELECT * FROM t3;
    SELECT * FROM t1 INTERSECT SELECT * FROM t3;
    SELECT * FROM t1 EXCEPT SELECT * FROM t3;
    SELECT * FROM t1 ORDER BY b;
    SELECT * FROM t1 GROUP BY b;
  }
  set ::update_hook
} [list]
db update_hook {}
#
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# Test the rollback-hook. The rollback-hook is a bit more complicated than
# either the commit or update hooks because a rollback can happen 
# explicitly (an sql ROLLBACK statement) or implicitly (a constraint or 
# error condition).
#
# hook-5.1.* - Test explicit rollbacks.
# hook-5.2.* - Test implicit rollbacks.
# hook-5.3.* - Test hot-journal rollbacks.
#

do_test hook-5.0 {
  # Configure the rollback hook to increment global variable 
  # $::rollback_hook each time it is invoked.
  set ::rollback_hook 0
  db rollback_hook [list incr ::rollback_hook]
} {}

# Test explicit rollbacks. Not much can really go wrong here.
do_test hook-5.1.1 {
  set ::rollback_hook 0
  execsql {
    BEGIN;
    ROLLBACK;
  }
  set ::rollback_hook
} {1}

# Test implicit rollbacks caused by constraints.
do_test hook-5.2.1 {
  set ::rollback_hook 0
  catchsql {
    DROP TABLE t1;
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES('one', 'I');
    INSERT INTO t1 VALUES('one', 'I');
  }
  set ::rollback_hook
} {1}
do_test hook-5.2.2 {
  # Check that the INSERT transaction above really was rolled back.
  execsql {
    SELECT count(*) FROM t1;
  }
} {1}

#
#----------------------------------------------------------------------------

finish_test

Changes to test/tclsqlite.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# Actually, all tests are based on the TCL interface, so the main
# 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.46 2005/12/12 06:53:05 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Check the error messages generated by tclsqlite
#
if {[sqlite3 -has-codec]} {
................................................................................
do_test tcl-1.1 {
  set v [catch {sqlite3 bogus} msg]
  lappend v $msg
} [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, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, errorcode, eval, exists, function, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, soft_heap_limit, timeout, total_changes, trace, transaction, or version}}
do_test tcl-1.3 {
  execsql {CREATE TABLE t1(a int, b int)}
  execsql {INSERT INTO t1 VALUES(10,20)}
  set v [catch {
    db eval {SELECT * FROM t1} data {
      error "The error message"
    }







|







 







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# Actually, all tests are based on the TCL interface, so the main
# 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.47 2005/12/16 06:54:03 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Check the error messages generated by tclsqlite
#
if {[sqlite3 -has-codec]} {
................................................................................
do_test tcl-1.1 {
  set v [catch {sqlite3 bogus} msg]
  lappend v $msg
} [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, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, errorcode, eval, exists, function, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, rollback_hook, soft_heap_limit, timeout, total_changes, trace, transaction, update_hook, or version}}
do_test tcl-1.3 {
  execsql {CREATE TABLE t1(a int, b int)}
  execsql {INSERT INTO t1 VALUES(10,20)}
  set v [catch {
    db eval {SELECT * FROM t1} data {
      error "The error message"
    }