xref: /illumos-gate/usr/src/lib/libsqlite/src/test4.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate ** 2003 December 18
37c478bd9Sstevel@tonic-gate **
47c478bd9Sstevel@tonic-gate ** The author disclaims copyright to this source code.  In place of
57c478bd9Sstevel@tonic-gate ** a legal notice, here is a blessing:
67c478bd9Sstevel@tonic-gate **
77c478bd9Sstevel@tonic-gate **    May you do good and not evil.
87c478bd9Sstevel@tonic-gate **    May you find forgiveness for yourself and forgive others.
97c478bd9Sstevel@tonic-gate **    May you share freely, never taking more than you give.
107c478bd9Sstevel@tonic-gate **
117c478bd9Sstevel@tonic-gate *************************************************************************
127c478bd9Sstevel@tonic-gate ** Code for testing the the SQLite library in a multithreaded environment.
137c478bd9Sstevel@tonic-gate **
147c478bd9Sstevel@tonic-gate ** $Id: test4.c,v 1.3 2004/04/23 17:04:45 drh Exp $
157c478bd9Sstevel@tonic-gate */
167c478bd9Sstevel@tonic-gate #include "sqliteInt.h"
177c478bd9Sstevel@tonic-gate #include "tcl.h"
187c478bd9Sstevel@tonic-gate #if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
197c478bd9Sstevel@tonic-gate #include <stdlib.h>
207c478bd9Sstevel@tonic-gate #include <string.h>
217c478bd9Sstevel@tonic-gate #include <pthread.h>
227c478bd9Sstevel@tonic-gate #include <sched.h>
237c478bd9Sstevel@tonic-gate #include <ctype.h>
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*
267c478bd9Sstevel@tonic-gate ** Each thread is controlled by an instance of the following
277c478bd9Sstevel@tonic-gate ** structure.
287c478bd9Sstevel@tonic-gate */
297c478bd9Sstevel@tonic-gate typedef struct Thread Thread;
307c478bd9Sstevel@tonic-gate struct Thread {
317c478bd9Sstevel@tonic-gate   /* The first group of fields are writable by the master and read-only
327c478bd9Sstevel@tonic-gate   ** to the thread. */
337c478bd9Sstevel@tonic-gate   char *zFilename;       /* Name of database file */
347c478bd9Sstevel@tonic-gate   void (*xOp)(Thread*);  /* next operation to do */
357c478bd9Sstevel@tonic-gate   char *zArg;            /* argument usable by xOp */
367c478bd9Sstevel@tonic-gate   int opnum;             /* Operation number */
377c478bd9Sstevel@tonic-gate   int busy;              /* True if this thread is in use */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate   /* The next group of fields are writable by the thread but read-only to the
407c478bd9Sstevel@tonic-gate   ** master. */
417c478bd9Sstevel@tonic-gate   int completed;        /* Number of operations completed */
427c478bd9Sstevel@tonic-gate   sqlite *db;           /* Open database */
437c478bd9Sstevel@tonic-gate   sqlite_vm *vm;        /* Pending operation */
447c478bd9Sstevel@tonic-gate   char *zErr;           /* operation error */
457c478bd9Sstevel@tonic-gate   char *zStaticErr;     /* Static error message */
467c478bd9Sstevel@tonic-gate   int rc;               /* operation return code */
477c478bd9Sstevel@tonic-gate   int argc;             /* number of columns in result */
487c478bd9Sstevel@tonic-gate   const char **argv;    /* result columns */
497c478bd9Sstevel@tonic-gate   const char **colv;    /* result column names */
507c478bd9Sstevel@tonic-gate };
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /*
537c478bd9Sstevel@tonic-gate ** There can be as many as 26 threads running at once.  Each is named
547c478bd9Sstevel@tonic-gate ** by a capital letter: A, B, C, ..., Y, Z.
557c478bd9Sstevel@tonic-gate */
567c478bd9Sstevel@tonic-gate #define N_THREAD 26
577c478bd9Sstevel@tonic-gate static Thread threadset[N_THREAD];
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /*
61*1da57d55SToomas Soome ** The main loop for a thread.  Threads use busy waiting.
627c478bd9Sstevel@tonic-gate */
thread_main(void * pArg)637c478bd9Sstevel@tonic-gate static void *thread_main(void *pArg){
647c478bd9Sstevel@tonic-gate   Thread *p = (Thread*)pArg;
657c478bd9Sstevel@tonic-gate   if( p->db ){
667c478bd9Sstevel@tonic-gate     sqlite_close(p->db);
677c478bd9Sstevel@tonic-gate   }
687c478bd9Sstevel@tonic-gate   p->db = sqlite_open(p->zFilename, 0, &p->zErr);
697c478bd9Sstevel@tonic-gate   p->vm = 0;
707c478bd9Sstevel@tonic-gate   p->completed = 1;
717c478bd9Sstevel@tonic-gate   while( p->opnum<=p->completed ) sched_yield();
727c478bd9Sstevel@tonic-gate   while( p->xOp ){
737c478bd9Sstevel@tonic-gate     if( p->zErr && p->zErr!=p->zStaticErr ){
747c478bd9Sstevel@tonic-gate       sqlite_freemem(p->zErr);
757c478bd9Sstevel@tonic-gate       p->zErr = 0;
767c478bd9Sstevel@tonic-gate     }
777c478bd9Sstevel@tonic-gate     (*p->xOp)(p);
787c478bd9Sstevel@tonic-gate     p->completed++;
797c478bd9Sstevel@tonic-gate     while( p->opnum<=p->completed ) sched_yield();
807c478bd9Sstevel@tonic-gate   }
817c478bd9Sstevel@tonic-gate   if( p->vm ){
827c478bd9Sstevel@tonic-gate     sqlite_finalize(p->vm, 0);
837c478bd9Sstevel@tonic-gate     p->vm = 0;
847c478bd9Sstevel@tonic-gate   }
857c478bd9Sstevel@tonic-gate   if( p->db ){
867c478bd9Sstevel@tonic-gate     sqlite_close(p->db);
877c478bd9Sstevel@tonic-gate     p->db = 0;
887c478bd9Sstevel@tonic-gate   }
897c478bd9Sstevel@tonic-gate   if( p->zErr && p->zErr!=p->zStaticErr ){
907c478bd9Sstevel@tonic-gate     sqlite_freemem(p->zErr);
917c478bd9Sstevel@tonic-gate     p->zErr = 0;
927c478bd9Sstevel@tonic-gate   }
937c478bd9Sstevel@tonic-gate   p->completed++;
947c478bd9Sstevel@tonic-gate   return 0;
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate ** Get a thread ID which is an upper case letter.  Return the index.
997c478bd9Sstevel@tonic-gate ** If the argument is not a valid thread ID put an error message in
1007c478bd9Sstevel@tonic-gate ** the interpreter and return -1.
1017c478bd9Sstevel@tonic-gate */
parse_thread_id(Tcl_Interp * interp,const char * zArg)1027c478bd9Sstevel@tonic-gate static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
1037c478bd9Sstevel@tonic-gate   if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper(zArg[0]) ){
1047c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
1057c478bd9Sstevel@tonic-gate     return -1;
1067c478bd9Sstevel@tonic-gate   }
1077c478bd9Sstevel@tonic-gate   return zArg[0] - 'A';
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate /*
1117c478bd9Sstevel@tonic-gate ** Usage:    thread_create NAME  FILENAME
1127c478bd9Sstevel@tonic-gate **
1137c478bd9Sstevel@tonic-gate ** NAME should be an upper case letter.  Start the thread running with
1147c478bd9Sstevel@tonic-gate ** an open connection to the given database.
1157c478bd9Sstevel@tonic-gate */
tcl_thread_create(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)1167c478bd9Sstevel@tonic-gate static int tcl_thread_create(
1177c478bd9Sstevel@tonic-gate   void *NotUsed,
1187c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
1197c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
1207c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
1217c478bd9Sstevel@tonic-gate ){
1227c478bd9Sstevel@tonic-gate   int i;
1237c478bd9Sstevel@tonic-gate   pthread_t x;
1247c478bd9Sstevel@tonic-gate   int rc;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate   if( argc!=3 ){
1277c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1287c478bd9Sstevel@tonic-gate        " ID FILENAME", 0);
1297c478bd9Sstevel@tonic-gate     return TCL_ERROR;
1307c478bd9Sstevel@tonic-gate   }
1317c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
1327c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
1337c478bd9Sstevel@tonic-gate   if( threadset[i].busy ){
1347c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
1357c478bd9Sstevel@tonic-gate     return TCL_ERROR;
1367c478bd9Sstevel@tonic-gate   }
1377c478bd9Sstevel@tonic-gate   threadset[i].busy = 1;
1387c478bd9Sstevel@tonic-gate   sqliteFree(threadset[i].zFilename);
1397c478bd9Sstevel@tonic-gate   threadset[i].zFilename = sqliteStrDup(argv[2]);
1407c478bd9Sstevel@tonic-gate   threadset[i].opnum = 1;
1417c478bd9Sstevel@tonic-gate   threadset[i].completed = 0;
1427c478bd9Sstevel@tonic-gate   rc = pthread_create(&x, 0, thread_main, &threadset[i]);
1437c478bd9Sstevel@tonic-gate   if( rc ){
1447c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "failed to create the thread", 0);
1457c478bd9Sstevel@tonic-gate     sqliteFree(threadset[i].zFilename);
1467c478bd9Sstevel@tonic-gate     threadset[i].busy = 0;
1477c478bd9Sstevel@tonic-gate     return TCL_ERROR;
1487c478bd9Sstevel@tonic-gate   }
1497c478bd9Sstevel@tonic-gate   pthread_detach(x);
1507c478bd9Sstevel@tonic-gate   return TCL_OK;
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate /*
1547c478bd9Sstevel@tonic-gate ** Wait for a thread to reach its idle state.
1557c478bd9Sstevel@tonic-gate */
thread_wait(Thread * p)1567c478bd9Sstevel@tonic-gate static void thread_wait(Thread *p){
1577c478bd9Sstevel@tonic-gate   while( p->opnum>p->completed ) sched_yield();
1587c478bd9Sstevel@tonic-gate }
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate /*
1617c478bd9Sstevel@tonic-gate ** Usage:  thread_wait ID
1627c478bd9Sstevel@tonic-gate **
1637c478bd9Sstevel@tonic-gate ** Wait on thread ID to reach its idle state.
1647c478bd9Sstevel@tonic-gate */
tcl_thread_wait(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)1657c478bd9Sstevel@tonic-gate static int tcl_thread_wait(
1667c478bd9Sstevel@tonic-gate   void *NotUsed,
1677c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
1687c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
1697c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
1707c478bd9Sstevel@tonic-gate ){
1717c478bd9Sstevel@tonic-gate   int i;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate   if( argc!=2 ){
1747c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1757c478bd9Sstevel@tonic-gate        " ID", 0);
1767c478bd9Sstevel@tonic-gate     return TCL_ERROR;
1777c478bd9Sstevel@tonic-gate   }
1787c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
1797c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
1807c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
1817c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
1827c478bd9Sstevel@tonic-gate     return TCL_ERROR;
1837c478bd9Sstevel@tonic-gate   }
1847c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
1857c478bd9Sstevel@tonic-gate   return TCL_OK;
1867c478bd9Sstevel@tonic-gate }
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate ** Stop a thread.
1907c478bd9Sstevel@tonic-gate */
stop_thread(Thread * p)1917c478bd9Sstevel@tonic-gate static void stop_thread(Thread *p){
1927c478bd9Sstevel@tonic-gate   thread_wait(p);
1937c478bd9Sstevel@tonic-gate   p->xOp = 0;
1947c478bd9Sstevel@tonic-gate   p->opnum++;
1957c478bd9Sstevel@tonic-gate   thread_wait(p);
1967c478bd9Sstevel@tonic-gate   sqliteFree(p->zArg);
1977c478bd9Sstevel@tonic-gate   p->zArg = 0;
1987c478bd9Sstevel@tonic-gate   sqliteFree(p->zFilename);
1997c478bd9Sstevel@tonic-gate   p->zFilename = 0;
2007c478bd9Sstevel@tonic-gate   p->busy = 0;
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate /*
2047c478bd9Sstevel@tonic-gate ** Usage:  thread_halt ID
2057c478bd9Sstevel@tonic-gate **
2067c478bd9Sstevel@tonic-gate ** Cause a thread to shut itself down.  Wait for the shutdown to be
2077c478bd9Sstevel@tonic-gate ** completed.  If ID is "*" then stop all threads.
2087c478bd9Sstevel@tonic-gate */
tcl_thread_halt(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)2097c478bd9Sstevel@tonic-gate static int tcl_thread_halt(
2107c478bd9Sstevel@tonic-gate   void *NotUsed,
2117c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
2127c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
2137c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
2147c478bd9Sstevel@tonic-gate ){
2157c478bd9Sstevel@tonic-gate   int i;
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate   if( argc!=2 ){
2187c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2197c478bd9Sstevel@tonic-gate        " ID", 0);
2207c478bd9Sstevel@tonic-gate     return TCL_ERROR;
2217c478bd9Sstevel@tonic-gate   }
2227c478bd9Sstevel@tonic-gate   if( argv[1][0]=='*' && argv[1][1]==0 ){
2237c478bd9Sstevel@tonic-gate     for(i=0; i<N_THREAD; i++){
2247c478bd9Sstevel@tonic-gate       if( threadset[i].busy ) stop_thread(&threadset[i]);
2257c478bd9Sstevel@tonic-gate     }
2267c478bd9Sstevel@tonic-gate   }else{
2277c478bd9Sstevel@tonic-gate     i = parse_thread_id(interp, argv[1]);
2287c478bd9Sstevel@tonic-gate     if( i<0 ) return TCL_ERROR;
2297c478bd9Sstevel@tonic-gate     if( !threadset[i].busy ){
2307c478bd9Sstevel@tonic-gate       Tcl_AppendResult(interp, "no such thread", 0);
2317c478bd9Sstevel@tonic-gate       return TCL_ERROR;
2327c478bd9Sstevel@tonic-gate     }
2337c478bd9Sstevel@tonic-gate     stop_thread(&threadset[i]);
2347c478bd9Sstevel@tonic-gate   }
2357c478bd9Sstevel@tonic-gate   return TCL_OK;
2367c478bd9Sstevel@tonic-gate }
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate /*
2397c478bd9Sstevel@tonic-gate ** Usage: thread_argc  ID
2407c478bd9Sstevel@tonic-gate **
2417c478bd9Sstevel@tonic-gate ** Wait on the most recent thread_step to complete, then return the
2427c478bd9Sstevel@tonic-gate ** number of columns in the result set.
2437c478bd9Sstevel@tonic-gate */
tcl_thread_argc(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)2447c478bd9Sstevel@tonic-gate static int tcl_thread_argc(
2457c478bd9Sstevel@tonic-gate   void *NotUsed,
2467c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
2477c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
2487c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
2497c478bd9Sstevel@tonic-gate ){
2507c478bd9Sstevel@tonic-gate   int i;
2517c478bd9Sstevel@tonic-gate   char zBuf[100];
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate   if( argc!=2 ){
2547c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2557c478bd9Sstevel@tonic-gate        " ID", 0);
2567c478bd9Sstevel@tonic-gate     return TCL_ERROR;
2577c478bd9Sstevel@tonic-gate   }
2587c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
2597c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
2607c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
2617c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
2627c478bd9Sstevel@tonic-gate     return TCL_ERROR;
2637c478bd9Sstevel@tonic-gate   }
2647c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
2657c478bd9Sstevel@tonic-gate   sprintf(zBuf, "%d", threadset[i].argc);
2667c478bd9Sstevel@tonic-gate   Tcl_AppendResult(interp, zBuf, 0);
2677c478bd9Sstevel@tonic-gate   return TCL_OK;
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /*
2717c478bd9Sstevel@tonic-gate ** Usage: thread_argv  ID   N
2727c478bd9Sstevel@tonic-gate **
2737c478bd9Sstevel@tonic-gate ** Wait on the most recent thread_step to complete, then return the
2747c478bd9Sstevel@tonic-gate ** value of the N-th columns in the result set.
2757c478bd9Sstevel@tonic-gate */
tcl_thread_argv(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)2767c478bd9Sstevel@tonic-gate static int tcl_thread_argv(
2777c478bd9Sstevel@tonic-gate   void *NotUsed,
2787c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
2797c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
2807c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
2817c478bd9Sstevel@tonic-gate ){
2827c478bd9Sstevel@tonic-gate   int i;
2837c478bd9Sstevel@tonic-gate   int n;
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate   if( argc!=3 ){
2867c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2877c478bd9Sstevel@tonic-gate        " ID N", 0);
2887c478bd9Sstevel@tonic-gate     return TCL_ERROR;
2897c478bd9Sstevel@tonic-gate   }
2907c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
2917c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
2927c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
2937c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
2947c478bd9Sstevel@tonic-gate     return TCL_ERROR;
2957c478bd9Sstevel@tonic-gate   }
2967c478bd9Sstevel@tonic-gate   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
2977c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
2987c478bd9Sstevel@tonic-gate   if( n<0 || n>=threadset[i].argc ){
2997c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "column number out of range", 0);
3007c478bd9Sstevel@tonic-gate     return TCL_ERROR;
3017c478bd9Sstevel@tonic-gate   }
3027c478bd9Sstevel@tonic-gate   Tcl_AppendResult(interp, threadset[i].argv[n], 0);
3037c478bd9Sstevel@tonic-gate   return TCL_OK;
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate /*
3077c478bd9Sstevel@tonic-gate ** Usage: thread_colname  ID   N
3087c478bd9Sstevel@tonic-gate **
3097c478bd9Sstevel@tonic-gate ** Wait on the most recent thread_step to complete, then return the
3107c478bd9Sstevel@tonic-gate ** name of the N-th columns in the result set.
3117c478bd9Sstevel@tonic-gate */
tcl_thread_colname(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)3127c478bd9Sstevel@tonic-gate static int tcl_thread_colname(
3137c478bd9Sstevel@tonic-gate   void *NotUsed,
3147c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
3157c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
3167c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
3177c478bd9Sstevel@tonic-gate ){
3187c478bd9Sstevel@tonic-gate   int i;
3197c478bd9Sstevel@tonic-gate   int n;
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate   if( argc!=3 ){
3227c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3237c478bd9Sstevel@tonic-gate        " ID N", 0);
3247c478bd9Sstevel@tonic-gate     return TCL_ERROR;
3257c478bd9Sstevel@tonic-gate   }
3267c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
3277c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
3287c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
3297c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
3307c478bd9Sstevel@tonic-gate     return TCL_ERROR;
3317c478bd9Sstevel@tonic-gate   }
3327c478bd9Sstevel@tonic-gate   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
3337c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
3347c478bd9Sstevel@tonic-gate   if( n<0 || n>=threadset[i].argc ){
3357c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "column number out of range", 0);
3367c478bd9Sstevel@tonic-gate     return TCL_ERROR;
3377c478bd9Sstevel@tonic-gate   }
3387c478bd9Sstevel@tonic-gate   Tcl_AppendResult(interp, threadset[i].colv[n], 0);
3397c478bd9Sstevel@tonic-gate   return TCL_OK;
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate /*
3437c478bd9Sstevel@tonic-gate ** Usage: thread_result  ID
3447c478bd9Sstevel@tonic-gate **
3457c478bd9Sstevel@tonic-gate ** Wait on the most recent operation to complete, then return the
3467c478bd9Sstevel@tonic-gate ** result code from that operation.
3477c478bd9Sstevel@tonic-gate */
tcl_thread_result(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)3487c478bd9Sstevel@tonic-gate static int tcl_thread_result(
3497c478bd9Sstevel@tonic-gate   void *NotUsed,
3507c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
3517c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
3527c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
3537c478bd9Sstevel@tonic-gate ){
3547c478bd9Sstevel@tonic-gate   int i;
3557c478bd9Sstevel@tonic-gate   const char *zName;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate   if( argc!=2 ){
3587c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3597c478bd9Sstevel@tonic-gate        " ID", 0);
3607c478bd9Sstevel@tonic-gate     return TCL_ERROR;
3617c478bd9Sstevel@tonic-gate   }
3627c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
3637c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
3647c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
3657c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
3667c478bd9Sstevel@tonic-gate     return TCL_ERROR;
3677c478bd9Sstevel@tonic-gate   }
3687c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
3697c478bd9Sstevel@tonic-gate   switch( threadset[i].rc ){
3707c478bd9Sstevel@tonic-gate     case SQLITE_OK:         zName = "SQLITE_OK";          break;
3717c478bd9Sstevel@tonic-gate     case SQLITE_ERROR:      zName = "SQLITE_ERROR";       break;
3727c478bd9Sstevel@tonic-gate     case SQLITE_INTERNAL:   zName = "SQLITE_INTERNAL";    break;
3737c478bd9Sstevel@tonic-gate     case SQLITE_PERM:       zName = "SQLITE_PERM";        break;
3747c478bd9Sstevel@tonic-gate     case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break;
3757c478bd9Sstevel@tonic-gate     case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break;
3767c478bd9Sstevel@tonic-gate     case SQLITE_LOCKED:     zName = "SQLITE_LOCKED";      break;
3777c478bd9Sstevel@tonic-gate     case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break;
3787c478bd9Sstevel@tonic-gate     case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break;
3797c478bd9Sstevel@tonic-gate     case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break;
3807c478bd9Sstevel@tonic-gate     case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break;
3817c478bd9Sstevel@tonic-gate     case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break;
3827c478bd9Sstevel@tonic-gate     case SQLITE_NOTFOUND:   zName = "SQLITE_NOTFOUND";    break;
3837c478bd9Sstevel@tonic-gate     case SQLITE_FULL:       zName = "SQLITE_FULL";        break;
3847c478bd9Sstevel@tonic-gate     case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break;
3857c478bd9Sstevel@tonic-gate     case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break;
3867c478bd9Sstevel@tonic-gate     case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break;
3877c478bd9Sstevel@tonic-gate     case SQLITE_SCHEMA:     zName = "SQLITE_SCHEMA";      break;
3887c478bd9Sstevel@tonic-gate     case SQLITE_TOOBIG:     zName = "SQLITE_TOOBIG";      break;
3897c478bd9Sstevel@tonic-gate     case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT";  break;
3907c478bd9Sstevel@tonic-gate     case SQLITE_MISMATCH:   zName = "SQLITE_MISMATCH";    break;
3917c478bd9Sstevel@tonic-gate     case SQLITE_MISUSE:     zName = "SQLITE_MISUSE";      break;
3927c478bd9Sstevel@tonic-gate     case SQLITE_NOLFS:      zName = "SQLITE_NOLFS";       break;
3937c478bd9Sstevel@tonic-gate     case SQLITE_AUTH:       zName = "SQLITE_AUTH";        break;
3947c478bd9Sstevel@tonic-gate     case SQLITE_FORMAT:     zName = "SQLITE_FORMAT";      break;
3957c478bd9Sstevel@tonic-gate     case SQLITE_RANGE:      zName = "SQLITE_RANGE";       break;
3967c478bd9Sstevel@tonic-gate     case SQLITE_ROW:        zName = "SQLITE_ROW";         break;
3977c478bd9Sstevel@tonic-gate     case SQLITE_DONE:       zName = "SQLITE_DONE";        break;
3987c478bd9Sstevel@tonic-gate     default:                zName = "SQLITE_Unknown";     break;
3997c478bd9Sstevel@tonic-gate   }
4007c478bd9Sstevel@tonic-gate   Tcl_AppendResult(interp, zName, 0);
4017c478bd9Sstevel@tonic-gate   return TCL_OK;
4027c478bd9Sstevel@tonic-gate }
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate /*
4057c478bd9Sstevel@tonic-gate ** Usage: thread_error  ID
4067c478bd9Sstevel@tonic-gate **
4077c478bd9Sstevel@tonic-gate ** Wait on the most recent operation to complete, then return the
4087c478bd9Sstevel@tonic-gate ** error string.
4097c478bd9Sstevel@tonic-gate */
tcl_thread_error(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)4107c478bd9Sstevel@tonic-gate static int tcl_thread_error(
4117c478bd9Sstevel@tonic-gate   void *NotUsed,
4127c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
4137c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
4147c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
4157c478bd9Sstevel@tonic-gate ){
4167c478bd9Sstevel@tonic-gate   int i;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate   if( argc!=2 ){
4197c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4207c478bd9Sstevel@tonic-gate        " ID", 0);
4217c478bd9Sstevel@tonic-gate     return TCL_ERROR;
4227c478bd9Sstevel@tonic-gate   }
4237c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
4247c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
4257c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
4267c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
4277c478bd9Sstevel@tonic-gate     return TCL_ERROR;
4287c478bd9Sstevel@tonic-gate   }
4297c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
4307c478bd9Sstevel@tonic-gate   Tcl_AppendResult(interp, threadset[i].zErr, 0);
4317c478bd9Sstevel@tonic-gate   return TCL_OK;
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate ** This procedure runs in the thread to compile an SQL statement.
4367c478bd9Sstevel@tonic-gate */
do_compile(Thread * p)4377c478bd9Sstevel@tonic-gate static void do_compile(Thread *p){
4387c478bd9Sstevel@tonic-gate   if( p->db==0 ){
4397c478bd9Sstevel@tonic-gate     p->zErr = p->zStaticErr = "no database is open";
4407c478bd9Sstevel@tonic-gate     p->rc = SQLITE_ERROR;
4417c478bd9Sstevel@tonic-gate     return;
4427c478bd9Sstevel@tonic-gate   }
4437c478bd9Sstevel@tonic-gate   if( p->vm ){
4447c478bd9Sstevel@tonic-gate     sqlite_finalize(p->vm, 0);
4457c478bd9Sstevel@tonic-gate     p->vm = 0;
4467c478bd9Sstevel@tonic-gate   }
4477c478bd9Sstevel@tonic-gate   p->rc = sqlite_compile(p->db, p->zArg, 0, &p->vm, &p->zErr);
4487c478bd9Sstevel@tonic-gate }
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate /*
4517c478bd9Sstevel@tonic-gate ** Usage: thread_compile ID SQL
4527c478bd9Sstevel@tonic-gate **
4537c478bd9Sstevel@tonic-gate ** Compile a new virtual machine.
4547c478bd9Sstevel@tonic-gate */
tcl_thread_compile(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)4557c478bd9Sstevel@tonic-gate static int tcl_thread_compile(
4567c478bd9Sstevel@tonic-gate   void *NotUsed,
4577c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
4587c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
4597c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
4607c478bd9Sstevel@tonic-gate ){
4617c478bd9Sstevel@tonic-gate   int i;
4627c478bd9Sstevel@tonic-gate   if( argc!=3 ){
4637c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4647c478bd9Sstevel@tonic-gate        " ID SQL", 0);
4657c478bd9Sstevel@tonic-gate     return TCL_ERROR;
4667c478bd9Sstevel@tonic-gate   }
4677c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
4687c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
4697c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
4707c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
4717c478bd9Sstevel@tonic-gate     return TCL_ERROR;
4727c478bd9Sstevel@tonic-gate   }
4737c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
4747c478bd9Sstevel@tonic-gate   threadset[i].xOp = do_compile;
4757c478bd9Sstevel@tonic-gate   sqliteFree(threadset[i].zArg);
4767c478bd9Sstevel@tonic-gate   threadset[i].zArg = sqliteStrDup(argv[2]);
4777c478bd9Sstevel@tonic-gate   threadset[i].opnum++;
4787c478bd9Sstevel@tonic-gate   return TCL_OK;
4797c478bd9Sstevel@tonic-gate }
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate ** This procedure runs in the thread to step the virtual machine.
4837c478bd9Sstevel@tonic-gate */
do_step(Thread * p)4847c478bd9Sstevel@tonic-gate static void do_step(Thread *p){
4857c478bd9Sstevel@tonic-gate   if( p->vm==0 ){
4867c478bd9Sstevel@tonic-gate     p->zErr = p->zStaticErr = "no virtual machine available";
4877c478bd9Sstevel@tonic-gate     p->rc = SQLITE_ERROR;
4887c478bd9Sstevel@tonic-gate     return;
4897c478bd9Sstevel@tonic-gate   }
4907c478bd9Sstevel@tonic-gate   p->rc = sqlite_step(p->vm, &p->argc, &p->argv, &p->colv);
4917c478bd9Sstevel@tonic-gate }
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate /*
4947c478bd9Sstevel@tonic-gate ** Usage: thread_step ID
4957c478bd9Sstevel@tonic-gate **
4967c478bd9Sstevel@tonic-gate ** Advance the virtual machine by one step
4977c478bd9Sstevel@tonic-gate */
tcl_thread_step(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)4987c478bd9Sstevel@tonic-gate static int tcl_thread_step(
4997c478bd9Sstevel@tonic-gate   void *NotUsed,
5007c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
5017c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
5027c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
5037c478bd9Sstevel@tonic-gate ){
5047c478bd9Sstevel@tonic-gate   int i;
5057c478bd9Sstevel@tonic-gate   if( argc!=2 ){
5067c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5077c478bd9Sstevel@tonic-gate        " IDL", 0);
5087c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5097c478bd9Sstevel@tonic-gate   }
5107c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
5117c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
5127c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
5137c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
5147c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5157c478bd9Sstevel@tonic-gate   }
5167c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
5177c478bd9Sstevel@tonic-gate   threadset[i].xOp = do_step;
5187c478bd9Sstevel@tonic-gate   threadset[i].opnum++;
5197c478bd9Sstevel@tonic-gate   return TCL_OK;
5207c478bd9Sstevel@tonic-gate }
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate /*
5237c478bd9Sstevel@tonic-gate ** This procedure runs in the thread to finalize a virtual machine.
5247c478bd9Sstevel@tonic-gate */
do_finalize(Thread * p)5257c478bd9Sstevel@tonic-gate static void do_finalize(Thread *p){
5267c478bd9Sstevel@tonic-gate   if( p->vm==0 ){
5277c478bd9Sstevel@tonic-gate     p->zErr = p->zStaticErr = "no virtual machine available";
5287c478bd9Sstevel@tonic-gate     p->rc = SQLITE_ERROR;
5297c478bd9Sstevel@tonic-gate     return;
5307c478bd9Sstevel@tonic-gate   }
5317c478bd9Sstevel@tonic-gate   p->rc = sqlite_finalize(p->vm, &p->zErr);
5327c478bd9Sstevel@tonic-gate   p->vm = 0;
5337c478bd9Sstevel@tonic-gate }
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate /*
5367c478bd9Sstevel@tonic-gate ** Usage: thread_finalize ID
5377c478bd9Sstevel@tonic-gate **
5387c478bd9Sstevel@tonic-gate ** Finalize the virtual machine.
5397c478bd9Sstevel@tonic-gate */
tcl_thread_finalize(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)5407c478bd9Sstevel@tonic-gate static int tcl_thread_finalize(
5417c478bd9Sstevel@tonic-gate   void *NotUsed,
5427c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
5437c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
5447c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
5457c478bd9Sstevel@tonic-gate ){
5467c478bd9Sstevel@tonic-gate   int i;
5477c478bd9Sstevel@tonic-gate   if( argc!=2 ){
5487c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5497c478bd9Sstevel@tonic-gate        " IDL", 0);
5507c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5517c478bd9Sstevel@tonic-gate   }
5527c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
5537c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
5547c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
5557c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
5567c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5577c478bd9Sstevel@tonic-gate   }
5587c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
5597c478bd9Sstevel@tonic-gate   threadset[i].xOp = do_finalize;
5607c478bd9Sstevel@tonic-gate   sqliteFree(threadset[i].zArg);
5617c478bd9Sstevel@tonic-gate   threadset[i].zArg = 0;
5627c478bd9Sstevel@tonic-gate   threadset[i].opnum++;
5637c478bd9Sstevel@tonic-gate   return TCL_OK;
5647c478bd9Sstevel@tonic-gate }
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate ** Usage: thread_swap ID ID
5687c478bd9Sstevel@tonic-gate **
5697c478bd9Sstevel@tonic-gate ** Interchange the sqlite* pointer between two threads.
5707c478bd9Sstevel@tonic-gate */
tcl_thread_swap(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)5717c478bd9Sstevel@tonic-gate static int tcl_thread_swap(
5727c478bd9Sstevel@tonic-gate   void *NotUsed,
5737c478bd9Sstevel@tonic-gate   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
5747c478bd9Sstevel@tonic-gate   int argc,              /* Number of arguments */
5757c478bd9Sstevel@tonic-gate   const char **argv      /* Text of each argument */
5767c478bd9Sstevel@tonic-gate ){
5777c478bd9Sstevel@tonic-gate   int i, j;
5787c478bd9Sstevel@tonic-gate   sqlite *temp;
5797c478bd9Sstevel@tonic-gate   if( argc!=3 ){
5807c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5817c478bd9Sstevel@tonic-gate        " ID1 ID2", 0);
5827c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5837c478bd9Sstevel@tonic-gate   }
5847c478bd9Sstevel@tonic-gate   i = parse_thread_id(interp, argv[1]);
5857c478bd9Sstevel@tonic-gate   if( i<0 ) return TCL_ERROR;
5867c478bd9Sstevel@tonic-gate   if( !threadset[i].busy ){
5877c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
5887c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5897c478bd9Sstevel@tonic-gate   }
5907c478bd9Sstevel@tonic-gate   thread_wait(&threadset[i]);
5917c478bd9Sstevel@tonic-gate   j = parse_thread_id(interp, argv[2]);
5927c478bd9Sstevel@tonic-gate   if( j<0 ) return TCL_ERROR;
5937c478bd9Sstevel@tonic-gate   if( !threadset[j].busy ){
5947c478bd9Sstevel@tonic-gate     Tcl_AppendResult(interp, "no such thread", 0);
5957c478bd9Sstevel@tonic-gate     return TCL_ERROR;
5967c478bd9Sstevel@tonic-gate   }
5977c478bd9Sstevel@tonic-gate   thread_wait(&threadset[j]);
5987c478bd9Sstevel@tonic-gate   temp = threadset[i].db;
5997c478bd9Sstevel@tonic-gate   threadset[i].db = threadset[j].db;
6007c478bd9Sstevel@tonic-gate   threadset[j].db = temp;
6017c478bd9Sstevel@tonic-gate   return TCL_OK;
6027c478bd9Sstevel@tonic-gate }
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate /*
6057c478bd9Sstevel@tonic-gate ** Register commands with the TCL interpreter.
6067c478bd9Sstevel@tonic-gate */
Sqlitetest4_Init(Tcl_Interp * interp)6077c478bd9Sstevel@tonic-gate int Sqlitetest4_Init(Tcl_Interp *interp){
6087c478bd9Sstevel@tonic-gate   static struct {
6097c478bd9Sstevel@tonic-gate      char *zName;
6107c478bd9Sstevel@tonic-gate      Tcl_CmdProc *xProc;
6117c478bd9Sstevel@tonic-gate   } aCmd[] = {
6127c478bd9Sstevel@tonic-gate      { "thread_create",     (Tcl_CmdProc*)tcl_thread_create     },
6137c478bd9Sstevel@tonic-gate      { "thread_wait",       (Tcl_CmdProc*)tcl_thread_wait       },
6147c478bd9Sstevel@tonic-gate      { "thread_halt",       (Tcl_CmdProc*)tcl_thread_halt       },
6157c478bd9Sstevel@tonic-gate      { "thread_argc",       (Tcl_CmdProc*)tcl_thread_argc       },
6167c478bd9Sstevel@tonic-gate      { "thread_argv",       (Tcl_CmdProc*)tcl_thread_argv       },
6177c478bd9Sstevel@tonic-gate      { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    },
6187c478bd9Sstevel@tonic-gate      { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
6197c478bd9Sstevel@tonic-gate      { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
6207c478bd9Sstevel@tonic-gate      { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
6217c478bd9Sstevel@tonic-gate      { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
6227c478bd9Sstevel@tonic-gate      { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
6237c478bd9Sstevel@tonic-gate      { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
6247c478bd9Sstevel@tonic-gate   };
6257c478bd9Sstevel@tonic-gate   int i;
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
6287c478bd9Sstevel@tonic-gate     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
6297c478bd9Sstevel@tonic-gate   }
6307c478bd9Sstevel@tonic-gate   return TCL_OK;
6317c478bd9Sstevel@tonic-gate }
6327c478bd9Sstevel@tonic-gate #else
Sqlitetest4_Init(Tcl_Interp * interp)6337c478bd9Sstevel@tonic-gate int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
6347c478bd9Sstevel@tonic-gate #endif /* OS_UNIX */
635