/ Check-in [6289b863]
Login

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

Overview
Comment:Remove the begin_hook and commit_hook APIs. They were a bad idea. Add a "trace" method to the TCL interface. (CVS 929)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6289b863590ecc5de3d1efaaa60aa6f3f64fefb3
User & Date: drh 2003-04-23 12:25:24
Context
2003-04-24
01:45
Fix some issues with INSTEAD OF triggers. (CVS 930) check-in: 206b1739 user: drh tags: trunk
2003-04-23
12:25
Remove the begin_hook and commit_hook APIs. They were a bad idea. Add a "trace" method to the TCL interface. (CVS 929) check-in: 6289b863 user: drh tags: trunk
2003-04-22
20:30
Update the authorizer API so that it reports the database that table and indices belong to and so that it reports when actions are taken in response to a trigger. (CVS 928) check-in: c675a550 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
....
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
**
*************************************************************************
** 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.126 2003/04/17 22:57:54 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................
  if( db->pVdbe==0 ){ db->nChange = 0; }
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.xCallback = xCallback;
  sParse.pArg = pArg;
  sParse.useDb = -1;
  sParse.useCallback = ppVm==0;
#ifndef SQLITE_OMIT_TRACE
  if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
#endif
  sqliteRunParser(&sParse, zSql, pzErrMsg);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
    sqliteRollbackAll(db);
    sqliteResetInternalSchema(db, 0);
    db->flags &= ~SQLITE_InTrans;
................................................................................
** is returned.  
**
** A NULL trace function means that no tracing is executes.  A non-NULL
** trace is a pointer to a function that is invoked at the start of each
** sqlite_exec().
*/
void *sqlite_trace(sqlite *db, void (*xTrace)(void*,const char*), void *pArg){
#ifndef SQLITE_OMIT_TRACE
  void *pOld = db->pTraceArg;
  db->xTrace = xTrace;
  db->pTraceArg = pArg;
  return pOld;
#else
  return 0;
#endif
}

/*
** Register functions to be invoked when a transaction is started or when
** a transaction commits.  If either function returns non-zero, then the
** corresponding operation aborts with a constraint error.
**
** EXPERIMENTAL.  This API is under evaluation and is not yet an
** official part of the SQLite interface.  This means it could change
** or be deleted in future releases.
*/
void *sqlite_begin_hook(
  sqlite *db,
  int (*xCallback)(void*),
  void *pArg
){
  void *pOld = db->pBeginArg;
  db->xBeginCallback = xCallback;
  db->pBeginArg = pArg;
  return pOld;
}
void *sqlite_commit_hook(
  sqlite *db,
  int (*xCallback)(void*),
  void *pArg
){
  void *pOld = db->pCommitArg;
  db->xCommitCallback = xCallback;
  db->pCommitArg = pArg;
  return pOld;
}

/*
** 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







|







 







<

<







 







<




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
719
720
721
722
723
724
725

726

727
728
729
730
731
732
733
....
1017
1018
1019
1020
1021
1022
1023

1024
1025
1026
1027

































1028
1029
1030
1031
1032
1033
1034
**
*************************************************************************
** 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.127 2003/04/23 12:25:24 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................
  if( db->pVdbe==0 ){ db->nChange = 0; }
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.xCallback = xCallback;
  sParse.pArg = pArg;
  sParse.useDb = -1;
  sParse.useCallback = ppVm==0;

  if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);

  sqliteRunParser(&sParse, zSql, pzErrMsg);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
    sqliteRollbackAll(db);
    sqliteResetInternalSchema(db, 0);
    db->flags &= ~SQLITE_InTrans;
................................................................................
** is returned.  
**
** A NULL trace function means that no tracing is executes.  A non-NULL
** trace is a pointer to a function that is invoked at the start of each
** sqlite_exec().
*/
void *sqlite_trace(sqlite *db, void (*xTrace)(void*,const char*), void *pArg){

  void *pOld = db->pTraceArg;
  db->xTrace = xTrace;
  db->pTraceArg = pArg;
  return pOld;

































}

/*
** 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

Changes to src/sqlite.h.in.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
**    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.45 2003/04/22 20:30:39 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** when this routine is called, that is like encountering an error or
** an interrupt.  (See sqlite_interrupt().)  Incomplete updates may be
** rolled back and transactions cancelled,  depending on the circumstances,
** and the result code returned will be SQLITE_ABORT.
*/
int sqlite_finalize(sqlite_vm*, char **pzErrMsg);

/*** EXPERIMENTAL ***
** 
** Register a callback function to be invoked whenever a new transaction
** is started or committed.  The pArg argument is passed through to the
** callback.  If the callback function returns non-zero, then the operation
** is aborted with a constraint error.
**
** If another function was previously registered, its pArg value is returned.
** Otherwise NULL is returned.
**
** Registering a NULL function disables the callback.
*/
void *sqlite_begin_hook(sqlite*, int(*)(void*), void*);
void *sqlite_commit_hook(sqlite*, int(*)(void*), void*);

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

#endif /* _SQLITE_H_ */







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
675
676
677
678
679
680
681















682
683
684
685
686
**    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.46 2003/04/23 12:25:24 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** when this routine is called, that is like encountering an error or
** an interrupt.  (See sqlite_interrupt().)  Incomplete updates may be
** rolled back and transactions cancelled,  depending on the circumstances,
** and the result code returned will be SQLITE_ABORT.
*/
int sqlite_finalize(sqlite_vm*, char **pzErrMsg);
















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

#endif /* _SQLITE_H_ */

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
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
...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
**    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.178 2003/04/22 20:30:39 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
** When building SQLite for embedded systems where memory is scarce,
** you can define one or more of the following macros to omit extra
** features of the library and thus keep the size of the library to
** a minimum.
*/
/* #define SQLITE_OMIT_AUTHORIZATION  1 */
/* #define SQLITE_OMIT_INMEMORYDB     1 */
/* #define SQLITE_OMIT_TRACE          1 */
/* #define SQLITE_OMIT_VACUUM         1 */

/*
** Integers of known sizes.  These typedefs might change for architectures
** where the sizes very.  Preprocessor macros are available so that the
** types can be conveniently redefined at compile-type.  Like this:
**
................................................................................
#endif
#ifndef UINT16_TYPE
# define UINT16_TYPE unsigned short int
#endif
#ifndef UINT8_TYPE
# define UINT8_TYPE unsigned char
#endif
#ifndef INT8_TYPE
# define INT8_TYPE signed char
#endif
#ifndef INTPTR_TYPE
# if SQLITE_PTR_SZ==4
#   define INTPTR_TYPE int
# else
#   define INTPTR_TYPE long long
# endif
#endif
typedef UINT32_TYPE u32;           /* 4-byte unsigned integer */
typedef UINT16_TYPE u16;           /* 2-byte unsigned integer */
typedef UINT8_TYPE u8;             /* 1-byte unsigned integer */
typedef INT8_TYPE i8;              /* 1-byte signed integer */
typedef INTPTR_TYPE ptr;           /* Big enough to hold a pointer */
typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */

/*
** This macro casts a pointer to an integer.  Useful for doing
** pointer arithmetic.
*/
................................................................................
  u8 want_to_close;             /* Close after all VDBEs are deallocated */
  int next_cookie;              /* Next value of aDb[0].schema_cookie */
  int cache_size;               /* Number of pages to use in the cache */
  int temp_store;               /* 1=file, 2=memory, 0=compile-time default */
  int nTable;                   /* Number of tables in the database */
  void *pBusyArg;               /* 1st Argument to the busy callback */
  int (*xBusyCallback)(void *,const char*,int);  /* The busy callback */
  void *pBeginArg;              /* Argument to the xBeginCallback() */
  int (*xBeginCallback)(void*); /* Invoked at every transaction start */
  void *pCommitArg;             /* Argument to xCommitCallback() */   
  int (*xCommitCallback)(void*);/* Invoked at every commit. */
  Hash aFunc;                   /* All functions that can be in SQL exprs */
  int lastRowid;                /* ROWID of most recent insert */
  int priorNewRowid;            /* Last randomly generated ROWID */
  int onError;                  /* Default conflict algorithm */
  int magic;                    /* Magic number for detect library misuse */
  int nChange;                  /* Number of rows changed */
  struct Vdbe *pVdbe;           /* List of active virtual machines */
................................................................................
** be the right operand of an IN operator.  Or, if a scalar SELECT appears
** in an expression the opcode is TK_SELECT and Expr.pSelect is the only
** operand.
*/
struct Expr {
  u8 op;                 /* Operation performed by this node */
  u8 dataType;           /* Either SQLITE_SO_TEXT or SQLITE_SO_NUM */
  i8 iDb;                /* Database referenced by this expression */
  u8 flags;              /* Various flags.  See below */
  Expr *pLeft, *pRight;  /* Left and right subnodes */
  ExprList *pList;       /* A list of expressions used as function arguments
                         ** or in "<expr> IN (<expr-list)" */
  Token token;           /* An operand token */
  Token span;            /* Complete text of the expression */
  int iTable, iColumn;   /* When op==TK_COLUMN, then this expr node means the







|







 







<







 







<
<
<










<







 







<
<
<
<







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
82
83
84
85
86
87
88

89
90
91
92
93
94
95
...
100
101
102
103
104
105
106



107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
...
269
270
271
272
273
274
275




276
277
278
279
280
281
282
...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
**    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.179 2003/04/23 12:25:24 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
** When building SQLite for embedded systems where memory is scarce,
** you can define one or more of the following macros to omit extra
** features of the library and thus keep the size of the library to
** a minimum.
*/
/* #define SQLITE_OMIT_AUTHORIZATION  1 */
/* #define SQLITE_OMIT_INMEMORYDB     1 */

/* #define SQLITE_OMIT_VACUUM         1 */

/*
** Integers of known sizes.  These typedefs might change for architectures
** where the sizes very.  Preprocessor macros are available so that the
** types can be conveniently redefined at compile-type.  Like this:
**
................................................................................
#endif
#ifndef UINT16_TYPE
# define UINT16_TYPE unsigned short int
#endif
#ifndef UINT8_TYPE
# define UINT8_TYPE unsigned char
#endif



#ifndef INTPTR_TYPE
# if SQLITE_PTR_SZ==4
#   define INTPTR_TYPE int
# else
#   define INTPTR_TYPE long long
# endif
#endif
typedef UINT32_TYPE u32;           /* 4-byte unsigned integer */
typedef UINT16_TYPE u16;           /* 2-byte unsigned integer */
typedef UINT8_TYPE u8;             /* 1-byte unsigned integer */

typedef INTPTR_TYPE ptr;           /* Big enough to hold a pointer */
typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */

/*
** This macro casts a pointer to an integer.  Useful for doing
** pointer arithmetic.
*/
................................................................................
  u8 want_to_close;             /* Close after all VDBEs are deallocated */
  int next_cookie;              /* Next value of aDb[0].schema_cookie */
  int cache_size;               /* Number of pages to use in the cache */
  int temp_store;               /* 1=file, 2=memory, 0=compile-time default */
  int nTable;                   /* Number of tables in the database */
  void *pBusyArg;               /* 1st Argument to the busy callback */
  int (*xBusyCallback)(void *,const char*,int);  /* The busy callback */




  Hash aFunc;                   /* All functions that can be in SQL exprs */
  int lastRowid;                /* ROWID of most recent insert */
  int priorNewRowid;            /* Last randomly generated ROWID */
  int onError;                  /* Default conflict algorithm */
  int magic;                    /* Magic number for detect library misuse */
  int nChange;                  /* Number of rows changed */
  struct Vdbe *pVdbe;           /* List of active virtual machines */
................................................................................
** be the right operand of an IN operator.  Or, if a scalar SELECT appears
** in an expression the opcode is TK_SELECT and Expr.pSelect is the only
** operand.
*/
struct Expr {
  u8 op;                 /* Operation performed by this node */
  u8 dataType;           /* Either SQLITE_SO_TEXT or SQLITE_SO_NUM */
  u8 iDb;                /* Database referenced by this expression */
  u8 flags;              /* Various flags.  See below */
  Expr *pLeft, *pRight;  /* Left and right subnodes */
  ExprList *pList;       /* A list of expressions used as function arguments
                         ** or in "<expr> IN (<expr-list)" */
  Token token;           /* An operand token */
  Token span;            /* Complete text of the expression */
  int iTable, iColumn;   /* When op==TK_COLUMN, then this expr node means the

Changes to src/tclsqlite.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
...
297
298
299
300
301
302
303
304
305
306
307
308

309
310

311

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337
338
339
340
...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461
462
463
...
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
...
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
...
821
822
823
824
825
826
827





































828
829
830
831
832
833
834
**    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.47 2003/04/22 20:30:39 drh Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
** that has been opened by the SQLite TCL interface.
*/
typedef struct SqliteDb SqliteDb;
struct SqliteDb {
  sqlite *db;           /* The "real" database structure */
  Tcl_Interp *interp;   /* The interpreter used for this database */
  char *zBusy;          /* The busy callback routine */
  char *zBegin;         /* The begin-transaction callback routine */
  char *zCommit;        /* The commit-transaction callback routine */
  char *zAuth;          /* The authorization callback routine */
  SqlFunc *pFunc;       /* List of SQL functions */
  int rc;               /* Return code of most recent sqlite_exec() */
};

/*
** An instance of this structure passes information thru the sqlite
................................................................................
    SqlFunc *pFunc = pDb->pFunc;
    pDb->pFunc = pFunc->pNext;
    Tcl_Free((char*)pFunc);
  }
  if( pDb->zBusy ){
    Tcl_Free(pDb->zBusy);
  }
  if( pDb->zBegin ){
    Tcl_Free(pDb->zBegin);
  }
  if( pDb->zCommit ){
    Tcl_Free(pDb->zCommit);
  }
  if( pDb->zAuth ){
    Tcl_Free(pDb->zAuth);
  }
  Tcl_Free((char*)pDb);
}

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

/*
** This routine is called when a new transaction is started.  The
** TCL script in pDb->zBegin is executed.  If it returns non-zero or
** if it throws an exception, the transaction is aborted.
*/
static int DbBeginHandler(void *cd){

  SqliteDb *pDb = (SqliteDb*)cd;
  int rc;



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

/*
** This routine is called when a transaction is committed.  The
** TCL script in pDb->zCommit is executed.  If it returns non-zero or
** if it throws an exception, the transaction is rolled back instead
** of being committed.
*/
static int DbCommitHandler(void *cd){
  SqliteDb *pDb = (SqliteDb*)cd;
  int rc;

  rc = Tcl_Eval(pDb->interp, pDb->zCommit);

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

/*
** This routine is called to evaluate an SQL function implemented
** using TCL script.
*/
static void tclSqlFunc(sqlite_func *context, int argc, const char **argv){
................................................................................
** and calls that connection "db1".  The second command causes this
** subroutine to be invoked.
*/
static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
  SqliteDb *pDb = (SqliteDb*)cd;
  int choice;
  static const char *DB_strs[] = {
    "authorizer",         "begin_hook",        "busy",
    "changes",            "close",             "commit_hook",
    "complete",           "errorcode",         "eval",
    "function",           "last_insert_rowid", "timeout",
    0                    
  };
  enum DB_enum {
    DB_AUTHORIZER,        DB_BEGIN_HOOK,       DB_BUSY,
    DB_CHANGES,           DB_CLOSE,            DB_COMMIT_HOOK,

    DB_COMPLETE,          DB_ERRORCODE,        DB_EVAL,
    DB_FUNCTION,          DB_LAST_INSERT_ROWID,DB_TIMEOUT,
  };

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
................................................................................
  ** If this method is invoked with no arguments, the current authorization
  ** callback string is returned.
  */
  case DB_AUTHORIZER: {
    if( objc>3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
    }else if( objc==2 ){
      if( pDb->zBegin ){
        Tcl_AppendResult(interp, pDb->zAuth, 0);
      }
    }else{
      char *zAuth;
      int len;
      if( pDb->zAuth ){
        Tcl_Free(pDb->zAuth);
................................................................................
        pDb->interp = interp;
        sqlite_set_authorizer(pDb->db, auth_callback, pDb);
      }else{
        sqlite_set_authorizer(pDb->db, 0, 0);
      }
#endif
    }
    break;
  }


  /*    $db begin_callback ?CALLBACK?
  **
  ** Invoke the given callback at the beginning of every SQL transaction.
  ** If the callback throws an exception or returns non-zero, then the
  ** transaction is aborted.  If CALLBACK is an empty string, the callback
  ** is disabled.
  */
  case DB_BEGIN_HOOK: {
    if( objc>3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
    }else if( objc==2 ){
      if( pDb->zBegin ){
        Tcl_AppendResult(interp, pDb->zBegin, 0);
      }
    }else{
      char *zBegin;
      int len;
      if( pDb->zBegin ){
        Tcl_Free(pDb->zBegin);
      }
      zBegin = Tcl_GetStringFromObj(objv[2], &len);
      if( zBegin && len>0 ){
        pDb->zBegin = Tcl_Alloc( len + 1 );
        strcpy(pDb->zBegin, zBegin);
      }else{
        pDb->zBegin = 0;
      }
      if( pDb->zBegin ){
        pDb->interp = interp;
        sqlite_begin_hook(pDb->db, DbBeginHandler, pDb);
      }else{
        sqlite_begin_hook(pDb->db, 0, 0);
      }
    }
    break;
  }

  /*    $db busy ?CALLBACK?
  **
  ** Invoke the given callback if an SQL statement attempts to open
  ** a locked database file.
................................................................................

  /*    $db close
  **
  ** Shutdown the database
  */
  case DB_CLOSE: {
    Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0));
    break;
  }

  /*    $db commit_hook ?CALLBACK?
  **
  ** Invoke the given callback just before committing every SQL transaction.
  ** If the callback throws an exception or returns non-zero, then the
  ** transaction is aborted.  If CALLBACK is an empty string, the callback
  ** is disabled.
  */
  case DB_COMMIT_HOOK: {
    if( objc>3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
    }else if( objc==2 ){
      if( pDb->zCommit ){
        Tcl_AppendResult(interp, pDb->zCommit, 0);
      }
    }else{
      char *zCommit;
      int len;
      if( pDb->zCommit ){
        Tcl_Free(pDb->zCommit);
      }
      zCommit = Tcl_GetStringFromObj(objv[2], &len);
      if( zCommit && len>0 ){
        pDb->zCommit = Tcl_Alloc( len + 1 );
        strcpy(pDb->zCommit, zCommit);
      }else{
        pDb->zCommit = 0;
      }
      if( pDb->zCommit ){
        pDb->interp = interp;
        sqlite_commit_hook(pDb->db, DbCommitHandler, pDb);
      }else{
        sqlite_commit_hook(pDb->db, 0, 0);
      }
    }
    break;
  }

  /*    $db complete SQL
  **
  ** Return TRUE if SQL is a complete SQL statement.  Return FALSE if
  ** additional lines of input are needed.  This is similar to the
................................................................................
      Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR;
    sqlite_busy_timeout(pDb->db, ms);
    break;
  }





































  } /* End of the SWITCH statement */
  return TCL_OK;
}

/*
**   sqlite DBNAME FILENAME ?MODE?
**







|







 







|
<







 







|
|
<
<
<







 







|
|
<

<
>

<
>

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







 







|
|
|
|
<


|
|
>
|
<







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







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







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
...
258
259
260
261
262
263
264
265
266



267
268
269
270
271
272
273
...
293
294
295
296
297
298
299
300
301

302

303
304

305
306
307
308
309















310
311
312



313
314
315
316
317
318
319
...
418
419
420
421
422
423
424
425
426
427
428

429
430
431
432
433
434

435
436
437
438
439
440
441
...
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
...
487
488
489
490
491
492
493






































494
495
496
497
498
499
500
...
551
552
553
554
555
556
557





































558
559
560
561
562
563
564
...
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
**    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.48 2003/04/23 12:25:24 drh Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
** that has been opened by the SQLite TCL interface.
*/
typedef struct SqliteDb SqliteDb;
struct SqliteDb {
  sqlite *db;           /* The "real" database structure */
  Tcl_Interp *interp;   /* The interpreter used for this database */
  char *zBusy;          /* The busy callback routine */
  char *zTrace;         /* The trace callback routine */

  char *zAuth;          /* The authorization callback routine */
  SqlFunc *pFunc;       /* List of SQL functions */
  int rc;               /* Return code of most recent sqlite_exec() */
};

/*
** An instance of this structure passes information thru the sqlite
................................................................................
    SqlFunc *pFunc = pDb->pFunc;
    pDb->pFunc = pFunc->pNext;
    Tcl_Free((char*)pFunc);
  }
  if( pDb->zBusy ){
    Tcl_Free(pDb->zBusy);
  }
  if( pDb->zTrace ){
    Tcl_Free(pDb->zTrace);



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

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

/*
** This routine is called by the SQLite trace handler whenever a new
** block of SQL is executed.  The TCL script in pDb->zTrace is executed.

*/

static void DbTraceHandler(void *cd, const char *zSql){
  SqliteDb *pDb = (SqliteDb*)cd;

  Tcl_DString str;

  Tcl_DStringInit(&str);
  Tcl_DStringAppend(&str, pDb->zTrace, -1);
  Tcl_DStringAppendElement(&str, zSql);















  Tcl_Eval(pDb->interp, Tcl_DStringValue(&str));
  Tcl_DStringFree(&str);
  Tcl_ResetResult(pDb->interp);



}

/*
** This routine is called to evaluate an SQL function implemented
** using TCL script.
*/
static void tclSqlFunc(sqlite_func *context, int argc, const char **argv){
................................................................................
** and calls that connection "db1".  The second command causes this
** subroutine to be invoked.
*/
static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
  SqliteDb *pDb = (SqliteDb*)cd;
  int choice;
  static const char *DB_strs[] = {
    "authorizer",         "busy",              "changes",
    "close",              "complete",          "errorcode",
    "eval",               "function",          "last_insert_rowid",
    "timeout",            "trace",             0

  };
  enum DB_enum {
    DB_AUTHORIZER,        DB_BUSY,             DB_CHANGES,
    DB_CLOSE,             DB_COMPLETE,         DB_ERRORCODE,
    DB_EVAL,              DB_FUNCTION,         DB_LAST_INSERT_ROWID,
    DB_TIMEOUT,           DB_TRACE,            

  };

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
................................................................................
  ** If this method is invoked with no arguments, the current authorization
  ** callback string is returned.
  */
  case DB_AUTHORIZER: {
    if( objc>3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
    }else if( objc==2 ){
      if( pDb->zAuth ){
        Tcl_AppendResult(interp, pDb->zAuth, 0);
      }
    }else{
      char *zAuth;
      int len;
      if( pDb->zAuth ){
        Tcl_Free(pDb->zAuth);
................................................................................
        pDb->interp = interp;
        sqlite_set_authorizer(pDb->db, auth_callback, pDb);
      }else{
        sqlite_set_authorizer(pDb->db, 0, 0);
      }
#endif
    }






































    break;
  }

  /*    $db busy ?CALLBACK?
  **
  ** Invoke the given callback if an SQL statement attempts to open
  ** a locked database file.
................................................................................

  /*    $db close
  **
  ** Shutdown the database
  */
  case DB_CLOSE: {
    Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0));





































    break;
  }

  /*    $db complete SQL
  **
  ** Return TRUE if SQL is a complete SQL statement.  Return FALSE if
  ** additional lines of input are needed.  This is similar to the
................................................................................
      Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR;
    sqlite_busy_timeout(pDb->db, ms);
    break;
  }

  /*    $db trace ?CALLBACK?
  **
  ** Make arrangements to invoke the CALLBACK routine for each SQL statement
  ** that is executed.  The text of the SQL is appended to CALLBACK before
  ** it is executed.
  */
  case DB_TRACE: {
    if( objc>3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
    }else if( objc==2 ){
      if( pDb->zTrace ){
        Tcl_AppendResult(interp, pDb->zTrace, 0);
      }
    }else{
      char *zTrace;
      int len;
      if( pDb->zTrace ){
        Tcl_Free(pDb->zTrace);
      }
      zTrace = Tcl_GetStringFromObj(objv[2], &len);
      if( zTrace && len>0 ){
        pDb->zTrace = Tcl_Alloc( len + 1 );
        strcpy(pDb->zTrace, zTrace);
      }else{
        pDb->zTrace = 0;
      }
      if( pDb->zTrace ){
        pDb->interp = interp;
        sqlite_trace(pDb->db, DbTraceHandler, pDb);
      }else{
        sqlite_trace(pDb->db, 0, 0);
      }
    }
    break;
  }

  } /* End of the SWITCH statement */
  return TCL_OK;
}

/*
**   sqlite DBNAME FILENAME ?MODE?
**

Changes to src/vdbe.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** 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.218 2003/04/17 12:44:25 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
      default: {
        goto abort_due_to_error;
      }
    }
  }
  db->aDb[i].inTrans = 1;
  p->undoTransOnError = 1;
  if( db->xBeginCallback!=0 && i==1 && rc==SQLITE_OK ){
    if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; 
    if( db->xBeginCallback(db->pBeginArg)!=0 ){
      rc = SQLITE_CONSTRAINT;
    }
    if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
  }
  break;
}

/* Opcode: Commit * * *
**
** Cause all modifications to the database that have been made since the
** last Transaction to actually take effect.  No additional modifications
** are allowed until another transaction is started.  The Commit instruction
** deletes the journal file and releases the write lock on the database.
** A read lock continues to be held if there are still cursors open.
*/
case OP_Commit: {
  int i;
  if( db->xCommitCallback!=0 ){
    if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; 
    if( db->xCommitCallback(db->pCommitArg)!=0 ){
      rc = SQLITE_CONSTRAINT;
    }
    if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
  }
  for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
    if( db->aDb[i].inTrans ){
      rc = sqliteBtreeCommit(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  if( rc==SQLITE_OK ){







|







 







<
<
<
<
<
<
<













<
<
<
<
<
<
<







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
3220
3221
3222
3223
3224
3225
3226







3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239







3240
3241
3242
3243
3244
3245
3246
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** 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.219 2003/04/23 12:25:25 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
      default: {
        goto abort_due_to_error;
      }
    }
  }
  db->aDb[i].inTrans = 1;
  p->undoTransOnError = 1;







  break;
}

/* Opcode: Commit * * *
**
** Cause all modifications to the database that have been made since the
** last Transaction to actually take effect.  No additional modifications
** are allowed until another transaction is started.  The Commit instruction
** deletes the journal file and releases the write lock on the database.
** A read lock continues to be held if there are still cursors open.
*/
case OP_Commit: {
  int i;







  for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
    if( db->aDb[i].inTrans ){
      rc = sqliteBtreeCommit(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  if( rc==SQLITE_OK ){

Deleted test/hook.test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
118
119
120
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
# 2003 April 3
#
# The author disclaims copyright to this source code.  In place of
# 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.
#
#***********************************************************************
# 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_begin_hook
#      sqlite_commit_hook
#
# $Id: hook.test,v 1.1 2003/04/03 15:46:05 drh Exp $

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

do_test hook-1.1 {
  db begin_hook
} {}
do_test hook-1.2 {
  db commit_hook
} {}

do_test hook-2.1 {
  set begin_cnt 0
  proc begin_hook {} {
    incr ::begin_cnt
    return 0
  }
  db begin_hook begin_hook
  db begin_hook
} {begin_hook}
do_test hook-2.2 {
  set begin_cnt
} {0}
do_test hook-2.3 {
  execsql {
    CREATE TABLE t1(a,b);
  }
  set begin_cnt
} {1}
do_test hook-2.4 {
  execsql {
    INSERT INTO t1 VALUES(1,2);
    INSERT INTO t1 SELECT a+1, b+1 FROM t1;
    INSERT INTO t1 SELECT a+2, b+2 FROM t1;
  }
  set begin_cnt
} {4}
do_test hook-2.5 {
  execsql {
    SELECT * FROM t1
  }
} {1 2 2 3 3 4 4 5}
do_test hook-2.6 {
  set begin_cnt
} {4}
do_test hook-2.7 {
  proc begin_hook {} {
    incr ::begin_cnt
    return 1
  }
  catchsql {
    INSERT INTO t1 VALUES(9,10);
  }
} {1 {constraint failed}}
do_test hook-2.8 {
  set begin_cnt
} {5}
do_test hook-2.9 {
  execsql {
    SELECT * FROM t1;
  }
} {1 2 2 3 3 4 4 5}
do_test hook-2.10 {
  db begin_hook {}
  db begin_hook
} {}
do_test hook-2.11 {
  execsql {
    INSERT INTO t1 VALUES(9,10);
    SELECT * FROM t1
  }
} {1 2 2 3 3 4 4 5 9 10}

do_test hook-3.1 {
  set commit_cnt 0
  proc commit_hook {} {
    incr ::commit_cnt
    return 0
  }
  db commit_hook ::commit_hook
  db commit_hook
} {::commit_hook}
do_test hook-3.2 {
  set commit_cnt
} {0}
do_test hook-3.3 {
  execsql {
    CREATE TABLE t2(a,b);
  }
  set commit_cnt
} {1}
do_test hook-3.4 {
  execsql {
    INSERT INTO t2 VALUES(1,2);
    INSERT INTO t2 SELECT a+1, b+1 FROM t2;
    INSERT INTO t2 SELECT a+2, b+2 FROM t2;
  }
  set commit_cnt
} {4}
do_test hook-3.5 {
  set commit_cnt {}
  proc commit_hook {} {
    set ::commit_cnt [execsql {SELECT * FROM t2}]
    return 0
  }
  execsql {
    INSERT INTO t2 VALUES(5,6);
  }
  set commit_cnt
} {1 2 2 3 3 4 4 5 5 6}
do_test hook-3.6 {
  set commit_cnt {}
  proc commit_hook {} {
    set ::commit_cnt [execsql {SELECT * FROM t2}]
    return 1
  }
  catchsql {
    INSERT INTO t2 VALUES(6,7);
  }
} {1 {constraint failed}}
do_test hook-3.7 {
  set commit_cnt
} {1 2 2 3 3 4 4 5 5 6 6 7}
do_test hook-3.8 {
  execsql {SELECT * FROM t2}
} {1 2 2 3 3 4 4 5 5 6}


finish_test
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































Changes to test/tclsqlite.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 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.12 2003/04/22 20:30:40 drh Exp $

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

# Check the error messages generated by tclsqlite
#
do_test tcl-1.1 {
  set v [catch {sqlite bogus} msg]
  lappend v $msg
} {1 {wrong # args: should be "sqlite HANDLE FILENAME ?MODE?"}}
do_test tcl-1.2 {
  set v [catch {db bogus} msg]
  lappend v $msg
} {1 {bad option "bogus": must be authorizer, begin_hook, busy, changes, close, commit_hook, complete, errorcode, eval, function, last_insert_rowid, or timeout}}
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 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.13 2003/04/23 12:25:25 drh Exp $

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

# Check the error messages generated by tclsqlite
#
do_test tcl-1.1 {
  set v [catch {sqlite bogus} msg]
  lappend v $msg
} {1 {wrong # args: should be "sqlite HANDLE FILENAME ?MODE?"}}
do_test tcl-1.2 {
  set v [catch {db bogus} msg]
  lappend v $msg
} {1 {bad option "bogus": must be authorizer, busy, changes, close, complete, errorcode, eval, function, last_insert_rowid, timeout, or trace}}
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"
    }

Added tool/mkopts.tcl.







































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/tclsh
#
# This script is used to generate the array of strings and the enum
# that appear at the beginning of the C code implementation of a
# a TCL command and that define the available subcommands for that
# TCL command.

set prefix {}
while {![eof stdin]} {
  set line [gets stdin]
  if {$line==""} continue
  regsub -all "\[ \t\n,\]+" [string trim $line] { } line
  foreach token [split $line { }] {
    if {![regexp {(([a-zA-Z]+)_)?([_a-zA-Z]+)} $token all px p2 name]} continue
    lappend namelist [string tolower $name]
    if {$px!=""} {set prefix $p2}
  }
}

puts "  static const char *${prefix}_strs\[\] = \173"
set col 0
proc put_item x {
  global col
  if {$col==0} {puts -nonewline "   "}
  if {$col<2} {
    puts -nonewline [format " %-21s" $x]
    incr col
  } else {
    puts $x
    set col 0
  }
}
proc finalize {} {
  global col
  if {$col>0} {puts {}}
  set col 0
}

foreach name [lsort $namelist] {
  put_item \"$name\",
}
put_item 0
finalize
puts "  \175;"
puts "  enum ${prefix}_enum \173"
foreach name [lsort $namelist] {
  regsub -all {@} $name {} name
  put_item ${prefix}_[string toupper $name],
}
finalize
puts "  \175;"