xref: /sqlite-3.40.0/src/test7.c (revision 7617e4a8)
129c636bcSdrh /*
229c636bcSdrh ** 2006 January 09
329c636bcSdrh **
429c636bcSdrh ** The author disclaims copyright to this source code.  In place of
529c636bcSdrh ** a legal notice, here is a blessing:
629c636bcSdrh **
729c636bcSdrh **    May you do good and not evil.
829c636bcSdrh **    May you find forgiveness for yourself and forgive others.
929c636bcSdrh **    May you share freely, never taking more than you give.
1029c636bcSdrh **
1129c636bcSdrh *************************************************************************
1229c636bcSdrh ** Code for testing the client/server version of the SQLite library.
1329c636bcSdrh ** Derived from test4.c.
1429c636bcSdrh */
1529c636bcSdrh #include "sqliteInt.h"
1652b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
1752b1dbb5Smistachkin #  include "sqlite_tcl.h"
1852b1dbb5Smistachkin #else
1929c636bcSdrh #  include "tcl.h"
2052b1dbb5Smistachkin #endif
2129c636bcSdrh 
2229c636bcSdrh /*
23d677b3d6Sdrh ** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
2429c636bcSdrh ** the SQLITE_SERVER option.
2529c636bcSdrh */
2620e987a1Sdanielk1977 #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
273a2d29f8Sshaneh     SQLITE_OS_UNIX && SQLITE_THREADSAFE
2829c636bcSdrh 
2929c636bcSdrh #include <stdlib.h>
3029c636bcSdrh #include <string.h>
3129c636bcSdrh #include <pthread.h>
3229c636bcSdrh #include <sched.h>
3329c636bcSdrh #include <ctype.h>
3429c636bcSdrh 
3529c636bcSdrh /*
3629c636bcSdrh ** Interfaces defined in server.c
3729c636bcSdrh */
3829c636bcSdrh int sqlite3_client_open(const char*, sqlite3**);
3929c636bcSdrh int sqlite3_client_prepare(sqlite3*,const char*,int,
4029c636bcSdrh                            sqlite3_stmt**,const char**);
4129c636bcSdrh int sqlite3_client_step(sqlite3_stmt*);
4229c636bcSdrh int sqlite3_client_reset(sqlite3_stmt*);
4329c636bcSdrh int sqlite3_client_finalize(sqlite3_stmt*);
4429c636bcSdrh int sqlite3_client_close(sqlite3*);
4529c636bcSdrh int sqlite3_server_start(void);
4629c636bcSdrh int sqlite3_server_stop(void);
47cfe111b1Sdan void sqlite3_server_start2(int *pnDecr);
4829c636bcSdrh 
4929c636bcSdrh /*
5029c636bcSdrh ** Each thread is controlled by an instance of the following
5129c636bcSdrh ** structure.
5229c636bcSdrh */
5329c636bcSdrh typedef struct Thread Thread;
5429c636bcSdrh struct Thread {
5529c636bcSdrh   /* The first group of fields are writable by the supervisor thread
5629c636bcSdrh   ** and read-only to the client threads
5729c636bcSdrh   */
5829c636bcSdrh   char *zFilename;         /* Name of database file */
5929c636bcSdrh   void (*xOp)(Thread*);    /* next operation to do */
6029c636bcSdrh   char *zArg;              /* argument usable by xOp */
6129c636bcSdrh   volatile int opnum;      /* Operation number */
6229c636bcSdrh   volatile int busy;       /* True if this thread is in use */
6329c636bcSdrh 
6429c636bcSdrh   /* The next group of fields are writable by the client threads
6529c636bcSdrh   ** but read-only to the superviser thread.
6629c636bcSdrh   */
6729c636bcSdrh   volatile int completed;  /* Number of operations completed */
6829c636bcSdrh   sqlite3 *db;             /* Open database */
6929c636bcSdrh   sqlite3_stmt *pStmt;     /* Pending operation */
7029c636bcSdrh   char *zErr;              /* operation error */
7129c636bcSdrh   char *zStaticErr;        /* Static error message */
7229c636bcSdrh   int rc;                  /* operation return code */
7329c636bcSdrh   int argc;                /* number of columns in result */
7429c636bcSdrh   const char *argv[100];   /* result columns */
7529c636bcSdrh   const char *colv[100];   /* result column names */
76cfe111b1Sdan 
77cfe111b1Sdan   /* Initialized to 1 by the supervisor thread when the client is
78cfe111b1Sdan   ** created, and then deemed read-only to the supervisor thread.
79cfe111b1Sdan   ** Is set to 0 by the server thread belonging to this client
80cfe111b1Sdan   ** just before it exits.
81cfe111b1Sdan   */
82cfe111b1Sdan   int nServer;             /* Number of server threads running */
8329c636bcSdrh };
8429c636bcSdrh 
8529c636bcSdrh /*
8629c636bcSdrh ** There can be as many as 26 threads running at once.  Each is named
8729c636bcSdrh ** by a capital letter: A, B, C, ..., Y, Z.
8829c636bcSdrh */
8929c636bcSdrh #define N_THREAD 26
9029c636bcSdrh static Thread threadset[N_THREAD];
9129c636bcSdrh 
9229c636bcSdrh /*
9329c636bcSdrh ** The main loop for a thread.  Threads use busy waiting.
9429c636bcSdrh */
client_main(void * pArg)9529c636bcSdrh static void *client_main(void *pArg){
9629c636bcSdrh   Thread *p = (Thread*)pArg;
9729c636bcSdrh   if( p->db ){
9829c636bcSdrh     sqlite3_client_close(p->db);
9929c636bcSdrh   }
10029c636bcSdrh   sqlite3_client_open(p->zFilename, &p->db);
10129c636bcSdrh   if( SQLITE_OK!=sqlite3_errcode(p->db) ){
10229c636bcSdrh     p->zErr = strdup(sqlite3_errmsg(p->db));
10329c636bcSdrh     sqlite3_client_close(p->db);
10429c636bcSdrh     p->db = 0;
10529c636bcSdrh   }
10629c636bcSdrh   p->pStmt = 0;
10729c636bcSdrh   p->completed = 1;
10829c636bcSdrh   while( p->opnum<=p->completed ) sched_yield();
10929c636bcSdrh   while( p->xOp ){
11029c636bcSdrh     if( p->zErr && p->zErr!=p->zStaticErr ){
11129c636bcSdrh       sqlite3_free(p->zErr);
11229c636bcSdrh       p->zErr = 0;
11329c636bcSdrh     }
11429c636bcSdrh     (*p->xOp)(p);
11529c636bcSdrh     p->completed++;
11629c636bcSdrh     while( p->opnum<=p->completed ) sched_yield();
11729c636bcSdrh   }
11829c636bcSdrh   if( p->pStmt ){
11929c636bcSdrh     sqlite3_client_finalize(p->pStmt);
12029c636bcSdrh     p->pStmt = 0;
12129c636bcSdrh   }
12229c636bcSdrh   if( p->db ){
12329c636bcSdrh     sqlite3_client_close(p->db);
12429c636bcSdrh     p->db = 0;
12529c636bcSdrh   }
12629c636bcSdrh   if( p->zErr && p->zErr!=p->zStaticErr ){
12729c636bcSdrh     sqlite3_free(p->zErr);
12829c636bcSdrh     p->zErr = 0;
12929c636bcSdrh   }
13029c636bcSdrh   p->completed++;
131eec556d3Sshane #ifndef SQLITE_OMIT_DEPRECATED
132b4bc7057Sdrh   sqlite3_thread_cleanup();
133eec556d3Sshane #endif
13429c636bcSdrh   return 0;
13529c636bcSdrh }
13629c636bcSdrh 
13729c636bcSdrh /*
13829c636bcSdrh ** Get a thread ID which is an upper case letter.  Return the index.
13929c636bcSdrh ** If the argument is not a valid thread ID put an error message in
14029c636bcSdrh ** the interpreter and return -1.
14129c636bcSdrh */
parse_client_id(Tcl_Interp * interp,const char * zArg)14229c636bcSdrh static int parse_client_id(Tcl_Interp *interp, const char *zArg){
14329c636bcSdrh   if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
14429c636bcSdrh     Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
14529c636bcSdrh     return -1;
14629c636bcSdrh   }
14729c636bcSdrh   return zArg[0] - 'A';
14829c636bcSdrh }
14929c636bcSdrh 
15029c636bcSdrh /*
15129c636bcSdrh ** Usage:    client_create NAME  FILENAME
15229c636bcSdrh **
15329c636bcSdrh ** NAME should be an upper case letter.  Start the thread running with
15429c636bcSdrh ** an open connection to the given database.
15529c636bcSdrh */
tcl_client_create(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)156*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_create(
15729c636bcSdrh   void *NotUsed,
15829c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
15929c636bcSdrh   int argc,              /* Number of arguments */
16029c636bcSdrh   const char **argv      /* Text of each argument */
16129c636bcSdrh ){
16229c636bcSdrh   int i;
16329c636bcSdrh   pthread_t x;
16429c636bcSdrh   int rc;
16529c636bcSdrh 
16629c636bcSdrh   if( argc!=3 ){
16729c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
16829c636bcSdrh        " ID FILENAME", 0);
16929c636bcSdrh     return TCL_ERROR;
17029c636bcSdrh   }
17129c636bcSdrh   i = parse_client_id(interp, argv[1]);
17229c636bcSdrh   if( i<0 ) return TCL_ERROR;
17329c636bcSdrh   if( threadset[i].busy ){
17429c636bcSdrh     Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
17529c636bcSdrh     return TCL_ERROR;
17629c636bcSdrh   }
17729c636bcSdrh   threadset[i].busy = 1;
178cab5ed7aSdrh   sqlite3_free(threadset[i].zFilename);
179b975598eSdrh   threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);
18029c636bcSdrh   threadset[i].opnum = 1;
18129c636bcSdrh   threadset[i].completed = 0;
18229c636bcSdrh   rc = pthread_create(&x, 0, client_main, &threadset[i]);
18329c636bcSdrh   if( rc ){
18429c636bcSdrh     Tcl_AppendResult(interp, "failed to create the thread", 0);
185cab5ed7aSdrh     sqlite3_free(threadset[i].zFilename);
18629c636bcSdrh     threadset[i].busy = 0;
18729c636bcSdrh     return TCL_ERROR;
18829c636bcSdrh   }
18929c636bcSdrh   pthread_detach(x);
190cfe111b1Sdan   if( threadset[i].nServer==0 ){
191cfe111b1Sdan     threadset[i].nServer = 1;
192cfe111b1Sdan     sqlite3_server_start2(&threadset[i].nServer);
193cfe111b1Sdan   }
19429c636bcSdrh   return TCL_OK;
19529c636bcSdrh }
19629c636bcSdrh 
19729c636bcSdrh /*
19829c636bcSdrh ** Wait for a thread to reach its idle state.
19929c636bcSdrh */
client_wait(Thread * p)20029c636bcSdrh static void client_wait(Thread *p){
20129c636bcSdrh   while( p->opnum>p->completed ) sched_yield();
20229c636bcSdrh }
20329c636bcSdrh 
20429c636bcSdrh /*
20529c636bcSdrh ** Usage:  client_wait ID
20629c636bcSdrh **
20729c636bcSdrh ** Wait on thread ID to reach its idle state.
20829c636bcSdrh */
tcl_client_wait(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)209*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_wait(
21029c636bcSdrh   void *NotUsed,
21129c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
21229c636bcSdrh   int argc,              /* Number of arguments */
21329c636bcSdrh   const char **argv      /* Text of each argument */
21429c636bcSdrh ){
21529c636bcSdrh   int i;
21629c636bcSdrh 
21729c636bcSdrh   if( argc!=2 ){
21829c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
21929c636bcSdrh        " ID", 0);
22029c636bcSdrh     return TCL_ERROR;
22129c636bcSdrh   }
22229c636bcSdrh   i = parse_client_id(interp, argv[1]);
22329c636bcSdrh   if( i<0 ) return TCL_ERROR;
22429c636bcSdrh   if( !threadset[i].busy ){
22529c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
22629c636bcSdrh     return TCL_ERROR;
22729c636bcSdrh   }
22829c636bcSdrh   client_wait(&threadset[i]);
22929c636bcSdrh   return TCL_OK;
23029c636bcSdrh }
23129c636bcSdrh 
23229c636bcSdrh /*
23329c636bcSdrh ** Stop a thread.
23429c636bcSdrh */
stop_thread(Thread * p)23529c636bcSdrh static void stop_thread(Thread *p){
23629c636bcSdrh   client_wait(p);
23729c636bcSdrh   p->xOp = 0;
23829c636bcSdrh   p->opnum++;
23929c636bcSdrh   client_wait(p);
240cab5ed7aSdrh   sqlite3_free(p->zArg);
24129c636bcSdrh   p->zArg = 0;
242cab5ed7aSdrh   sqlite3_free(p->zFilename);
24329c636bcSdrh   p->zFilename = 0;
24429c636bcSdrh   p->busy = 0;
24529c636bcSdrh }
24629c636bcSdrh 
24729c636bcSdrh /*
24829c636bcSdrh ** Usage:  client_halt ID
24929c636bcSdrh **
25029c636bcSdrh ** Cause a client thread to shut itself down.  Wait for the shutdown to be
25129c636bcSdrh ** completed.  If ID is "*" then stop all client threads.
25229c636bcSdrh */
tcl_client_halt(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)253*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_halt(
25429c636bcSdrh   void *NotUsed,
25529c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
25629c636bcSdrh   int argc,              /* Number of arguments */
25729c636bcSdrh   const char **argv      /* Text of each argument */
25829c636bcSdrh ){
25929c636bcSdrh   int i;
26029c636bcSdrh 
26129c636bcSdrh   if( argc!=2 ){
26229c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
26329c636bcSdrh        " ID", 0);
26429c636bcSdrh     return TCL_ERROR;
26529c636bcSdrh   }
26629c636bcSdrh   if( argv[1][0]=='*' && argv[1][1]==0 ){
26729c636bcSdrh     for(i=0; i<N_THREAD; i++){
26829c636bcSdrh       if( threadset[i].busy ){
26929c636bcSdrh         stop_thread(&threadset[i]);
27029c636bcSdrh       }
27129c636bcSdrh     }
27229c636bcSdrh   }else{
27329c636bcSdrh     i = parse_client_id(interp, argv[1]);
27429c636bcSdrh     if( i<0 ) return TCL_ERROR;
27529c636bcSdrh     if( !threadset[i].busy ){
27629c636bcSdrh       Tcl_AppendResult(interp, "no such thread", 0);
27729c636bcSdrh       return TCL_ERROR;
27829c636bcSdrh     }
27929c636bcSdrh     stop_thread(&threadset[i]);
28029c636bcSdrh   }
28129c636bcSdrh 
28229c636bcSdrh   /* If no client threads are still running, also stop the server */
28329c636bcSdrh   for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
28429c636bcSdrh   if( i>=N_THREAD ){
28529c636bcSdrh     sqlite3_server_stop();
286cfe111b1Sdan     while( 1 ){
287cfe111b1Sdan       for(i=0; i<N_THREAD && threadset[i].nServer==0; i++);
288cfe111b1Sdan       if( i==N_THREAD ) break;
289cfe111b1Sdan       sched_yield();
290cfe111b1Sdan     }
29129c636bcSdrh   }
29229c636bcSdrh   return TCL_OK;
29329c636bcSdrh }
29429c636bcSdrh 
29529c636bcSdrh /*
29629c636bcSdrh ** Usage: client_argc  ID
29729c636bcSdrh **
29829c636bcSdrh ** Wait on the most recent client_step to complete, then return the
29929c636bcSdrh ** number of columns in the result set.
30029c636bcSdrh */
tcl_client_argc(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)301*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_argc(
30229c636bcSdrh   void *NotUsed,
30329c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
30429c636bcSdrh   int argc,              /* Number of arguments */
30529c636bcSdrh   const char **argv      /* Text of each argument */
30629c636bcSdrh ){
30729c636bcSdrh   int i;
30829c636bcSdrh   char zBuf[100];
30929c636bcSdrh 
31029c636bcSdrh   if( argc!=2 ){
31129c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
31229c636bcSdrh        " ID", 0);
31329c636bcSdrh     return TCL_ERROR;
31429c636bcSdrh   }
31529c636bcSdrh   i = parse_client_id(interp, argv[1]);
31629c636bcSdrh   if( i<0 ) return TCL_ERROR;
31729c636bcSdrh   if( !threadset[i].busy ){
31829c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
31929c636bcSdrh     return TCL_ERROR;
32029c636bcSdrh   }
32129c636bcSdrh   client_wait(&threadset[i]);
32265545b59Sdrh   sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc);
32329c636bcSdrh   Tcl_AppendResult(interp, zBuf, 0);
32429c636bcSdrh   return TCL_OK;
32529c636bcSdrh }
32629c636bcSdrh 
32729c636bcSdrh /*
32829c636bcSdrh ** Usage: client_argv  ID   N
32929c636bcSdrh **
33029c636bcSdrh ** Wait on the most recent client_step to complete, then return the
33129c636bcSdrh ** value of the N-th columns in the result set.
33229c636bcSdrh */
tcl_client_argv(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)333*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_argv(
33429c636bcSdrh   void *NotUsed,
33529c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
33629c636bcSdrh   int argc,              /* Number of arguments */
33729c636bcSdrh   const char **argv      /* Text of each argument */
33829c636bcSdrh ){
33929c636bcSdrh   int i;
34029c636bcSdrh   int n;
34129c636bcSdrh 
34229c636bcSdrh   if( argc!=3 ){
34329c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
34429c636bcSdrh        " ID N", 0);
34529c636bcSdrh     return TCL_ERROR;
34629c636bcSdrh   }
34729c636bcSdrh   i = parse_client_id(interp, argv[1]);
34829c636bcSdrh   if( i<0 ) return TCL_ERROR;
34929c636bcSdrh   if( !threadset[i].busy ){
35029c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
35129c636bcSdrh     return TCL_ERROR;
35229c636bcSdrh   }
35329c636bcSdrh   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
35429c636bcSdrh   client_wait(&threadset[i]);
35529c636bcSdrh   if( n<0 || n>=threadset[i].argc ){
35629c636bcSdrh     Tcl_AppendResult(interp, "column number out of range", 0);
35729c636bcSdrh     return TCL_ERROR;
35829c636bcSdrh   }
35929c636bcSdrh   Tcl_AppendResult(interp, threadset[i].argv[n], 0);
36029c636bcSdrh   return TCL_OK;
36129c636bcSdrh }
36229c636bcSdrh 
36329c636bcSdrh /*
36429c636bcSdrh ** Usage: client_colname  ID   N
36529c636bcSdrh **
36629c636bcSdrh ** Wait on the most recent client_step to complete, then return the
36729c636bcSdrh ** name of the N-th columns in the result set.
36829c636bcSdrh */
tcl_client_colname(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)369*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_colname(
37029c636bcSdrh   void *NotUsed,
37129c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
37229c636bcSdrh   int argc,              /* Number of arguments */
37329c636bcSdrh   const char **argv      /* Text of each argument */
37429c636bcSdrh ){
37529c636bcSdrh   int i;
37629c636bcSdrh   int n;
37729c636bcSdrh 
37829c636bcSdrh   if( argc!=3 ){
37929c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
38029c636bcSdrh        " ID N", 0);
38129c636bcSdrh     return TCL_ERROR;
38229c636bcSdrh   }
38329c636bcSdrh   i = parse_client_id(interp, argv[1]);
38429c636bcSdrh   if( i<0 ) return TCL_ERROR;
38529c636bcSdrh   if( !threadset[i].busy ){
38629c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
38729c636bcSdrh     return TCL_ERROR;
38829c636bcSdrh   }
38929c636bcSdrh   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
39029c636bcSdrh   client_wait(&threadset[i]);
39129c636bcSdrh   if( n<0 || n>=threadset[i].argc ){
39229c636bcSdrh     Tcl_AppendResult(interp, "column number out of range", 0);
39329c636bcSdrh     return TCL_ERROR;
39429c636bcSdrh   }
39529c636bcSdrh   Tcl_AppendResult(interp, threadset[i].colv[n], 0);
39629c636bcSdrh   return TCL_OK;
39729c636bcSdrh }
39829c636bcSdrh 
399e84d8d32Smistachkin extern const char *sqlite3ErrName(int);
400d040e764Sdrh 
40129c636bcSdrh /*
40229c636bcSdrh ** Usage: client_result  ID
40329c636bcSdrh **
40429c636bcSdrh ** Wait on the most recent operation to complete, then return the
40529c636bcSdrh ** result code from that operation.
40629c636bcSdrh */
tcl_client_result(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)407*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_result(
40829c636bcSdrh   void *NotUsed,
40929c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
41029c636bcSdrh   int argc,              /* Number of arguments */
41129c636bcSdrh   const char **argv      /* Text of each argument */
41229c636bcSdrh ){
41329c636bcSdrh   int i;
41429c636bcSdrh   const char *zName;
41529c636bcSdrh 
41629c636bcSdrh   if( argc!=2 ){
41729c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
41829c636bcSdrh        " ID", 0);
41929c636bcSdrh     return TCL_ERROR;
42029c636bcSdrh   }
42129c636bcSdrh   i = parse_client_id(interp, argv[1]);
42229c636bcSdrh   if( i<0 ) return TCL_ERROR;
42329c636bcSdrh   if( !threadset[i].busy ){
42429c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
42529c636bcSdrh     return TCL_ERROR;
42629c636bcSdrh   }
42729c636bcSdrh   client_wait(&threadset[i]);
428e84d8d32Smistachkin   zName = sqlite3ErrName(threadset[i].rc);
42929c636bcSdrh   Tcl_AppendResult(interp, zName, 0);
43029c636bcSdrh   return TCL_OK;
43129c636bcSdrh }
43229c636bcSdrh 
43329c636bcSdrh /*
43429c636bcSdrh ** Usage: client_error  ID
43529c636bcSdrh **
43629c636bcSdrh ** Wait on the most recent operation to complete, then return the
43729c636bcSdrh ** error string.
43829c636bcSdrh */
tcl_client_error(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)439*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_error(
44029c636bcSdrh   void *NotUsed,
44129c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
44229c636bcSdrh   int argc,              /* Number of arguments */
44329c636bcSdrh   const char **argv      /* Text of each argument */
44429c636bcSdrh ){
44529c636bcSdrh   int i;
44629c636bcSdrh 
44729c636bcSdrh   if( argc!=2 ){
44829c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
44929c636bcSdrh        " ID", 0);
45029c636bcSdrh     return TCL_ERROR;
45129c636bcSdrh   }
45229c636bcSdrh   i = parse_client_id(interp, argv[1]);
45329c636bcSdrh   if( i<0 ) return TCL_ERROR;
45429c636bcSdrh   if( !threadset[i].busy ){
45529c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
45629c636bcSdrh     return TCL_ERROR;
45729c636bcSdrh   }
45829c636bcSdrh   client_wait(&threadset[i]);
45929c636bcSdrh   Tcl_AppendResult(interp, threadset[i].zErr, 0);
46029c636bcSdrh   return TCL_OK;
46129c636bcSdrh }
46229c636bcSdrh 
46329c636bcSdrh /*
46429c636bcSdrh ** This procedure runs in the thread to compile an SQL statement.
46529c636bcSdrh */
do_compile(Thread * p)46629c636bcSdrh static void do_compile(Thread *p){
46729c636bcSdrh   if( p->db==0 ){
46829c636bcSdrh     p->zErr = p->zStaticErr = "no database is open";
46929c636bcSdrh     p->rc = SQLITE_ERROR;
47029c636bcSdrh     return;
47129c636bcSdrh   }
47229c636bcSdrh   if( p->pStmt ){
47329c636bcSdrh     sqlite3_client_finalize(p->pStmt);
47429c636bcSdrh     p->pStmt = 0;
47529c636bcSdrh   }
47629c636bcSdrh   p->rc = sqlite3_client_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
47729c636bcSdrh }
47829c636bcSdrh 
47929c636bcSdrh /*
48029c636bcSdrh ** Usage: client_compile ID SQL
48129c636bcSdrh **
48229c636bcSdrh ** Compile a new virtual machine.
48329c636bcSdrh */
tcl_client_compile(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)484*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_compile(
48529c636bcSdrh   void *NotUsed,
48629c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
48729c636bcSdrh   int argc,              /* Number of arguments */
48829c636bcSdrh   const char **argv      /* Text of each argument */
48929c636bcSdrh ){
49029c636bcSdrh   int i;
49129c636bcSdrh   if( argc!=3 ){
49229c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
49329c636bcSdrh        " ID SQL", 0);
49429c636bcSdrh     return TCL_ERROR;
49529c636bcSdrh   }
49629c636bcSdrh   i = parse_client_id(interp, argv[1]);
49729c636bcSdrh   if( i<0 ) return TCL_ERROR;
49829c636bcSdrh   if( !threadset[i].busy ){
49929c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
50029c636bcSdrh     return TCL_ERROR;
50129c636bcSdrh   }
50229c636bcSdrh   client_wait(&threadset[i]);
50329c636bcSdrh   threadset[i].xOp = do_compile;
504cab5ed7aSdrh   sqlite3_free(threadset[i].zArg);
505b975598eSdrh   threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
50629c636bcSdrh   threadset[i].opnum++;
50729c636bcSdrh   return TCL_OK;
50829c636bcSdrh }
50929c636bcSdrh 
51029c636bcSdrh /*
51129c636bcSdrh ** This procedure runs in the thread to step the virtual machine.
51229c636bcSdrh */
do_step(Thread * p)51329c636bcSdrh static void do_step(Thread *p){
51429c636bcSdrh   int i;
51529c636bcSdrh   if( p->pStmt==0 ){
51629c636bcSdrh     p->zErr = p->zStaticErr = "no virtual machine available";
51729c636bcSdrh     p->rc = SQLITE_ERROR;
51829c636bcSdrh     return;
51929c636bcSdrh   }
52029c636bcSdrh   p->rc = sqlite3_client_step(p->pStmt);
52129c636bcSdrh   if( p->rc==SQLITE_ROW ){
52229c636bcSdrh     p->argc = sqlite3_column_count(p->pStmt);
52329c636bcSdrh     for(i=0; i<sqlite3_data_count(p->pStmt); i++){
52424bd82c3Sdrh       p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
52529c636bcSdrh     }
52629c636bcSdrh     for(i=0; i<p->argc; i++){
52729c636bcSdrh       p->colv[i] = sqlite3_column_name(p->pStmt, i);
52829c636bcSdrh     }
52929c636bcSdrh   }
53029c636bcSdrh }
53129c636bcSdrh 
53229c636bcSdrh /*
53329c636bcSdrh ** Usage: client_step ID
53429c636bcSdrh **
53529c636bcSdrh ** Advance the virtual machine by one step
53629c636bcSdrh */
tcl_client_step(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)537*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_step(
53829c636bcSdrh   void *NotUsed,
53929c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
54029c636bcSdrh   int argc,              /* Number of arguments */
54129c636bcSdrh   const char **argv      /* Text of each argument */
54229c636bcSdrh ){
54329c636bcSdrh   int i;
54429c636bcSdrh   if( argc!=2 ){
54529c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
54629c636bcSdrh        " IDL", 0);
54729c636bcSdrh     return TCL_ERROR;
54829c636bcSdrh   }
54929c636bcSdrh   i = parse_client_id(interp, argv[1]);
55029c636bcSdrh   if( i<0 ) return TCL_ERROR;
55129c636bcSdrh   if( !threadset[i].busy ){
55229c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
55329c636bcSdrh     return TCL_ERROR;
55429c636bcSdrh   }
55529c636bcSdrh   client_wait(&threadset[i]);
55629c636bcSdrh   threadset[i].xOp = do_step;
55729c636bcSdrh   threadset[i].opnum++;
55829c636bcSdrh   return TCL_OK;
55929c636bcSdrh }
56029c636bcSdrh 
56129c636bcSdrh /*
56229c636bcSdrh ** This procedure runs in the thread to finalize a virtual machine.
56329c636bcSdrh */
do_finalize(Thread * p)56429c636bcSdrh static void do_finalize(Thread *p){
56529c636bcSdrh   if( p->pStmt==0 ){
56629c636bcSdrh     p->zErr = p->zStaticErr = "no virtual machine available";
56729c636bcSdrh     p->rc = SQLITE_ERROR;
56829c636bcSdrh     return;
56929c636bcSdrh   }
57029c636bcSdrh   p->rc = sqlite3_client_finalize(p->pStmt);
57129c636bcSdrh   p->pStmt = 0;
57229c636bcSdrh }
57329c636bcSdrh 
57429c636bcSdrh /*
57529c636bcSdrh ** Usage: client_finalize ID
57629c636bcSdrh **
57729c636bcSdrh ** Finalize the virtual machine.
57829c636bcSdrh */
tcl_client_finalize(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)579*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_finalize(
58029c636bcSdrh   void *NotUsed,
58129c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
58229c636bcSdrh   int argc,              /* Number of arguments */
58329c636bcSdrh   const char **argv      /* Text of each argument */
58429c636bcSdrh ){
58529c636bcSdrh   int i;
58629c636bcSdrh   if( argc!=2 ){
58729c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
58829c636bcSdrh        " IDL", 0);
58929c636bcSdrh     return TCL_ERROR;
59029c636bcSdrh   }
59129c636bcSdrh   i = parse_client_id(interp, argv[1]);
59229c636bcSdrh   if( i<0 ) return TCL_ERROR;
59329c636bcSdrh   if( !threadset[i].busy ){
59429c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
59529c636bcSdrh     return TCL_ERROR;
59629c636bcSdrh   }
59729c636bcSdrh   client_wait(&threadset[i]);
59829c636bcSdrh   threadset[i].xOp = do_finalize;
599cab5ed7aSdrh   sqlite3_free(threadset[i].zArg);
60029c636bcSdrh   threadset[i].zArg = 0;
60129c636bcSdrh   threadset[i].opnum++;
60229c636bcSdrh   return TCL_OK;
60329c636bcSdrh }
60429c636bcSdrh 
60529c636bcSdrh /*
60629c636bcSdrh ** This procedure runs in the thread to reset a virtual machine.
60729c636bcSdrh */
do_reset(Thread * p)60829c636bcSdrh static void do_reset(Thread *p){
60929c636bcSdrh   if( p->pStmt==0 ){
61029c636bcSdrh     p->zErr = p->zStaticErr = "no virtual machine available";
61129c636bcSdrh     p->rc = SQLITE_ERROR;
61229c636bcSdrh     return;
61329c636bcSdrh   }
61429c636bcSdrh   p->rc = sqlite3_client_reset(p->pStmt);
61529c636bcSdrh   p->pStmt = 0;
61629c636bcSdrh }
61729c636bcSdrh 
61829c636bcSdrh /*
61929c636bcSdrh ** Usage: client_reset ID
62029c636bcSdrh **
62129c636bcSdrh ** Finalize the virtual machine.
62229c636bcSdrh */
tcl_client_reset(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)623*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_reset(
62429c636bcSdrh   void *NotUsed,
62529c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
62629c636bcSdrh   int argc,              /* Number of arguments */
62729c636bcSdrh   const char **argv      /* Text of each argument */
62829c636bcSdrh ){
62929c636bcSdrh   int i;
63029c636bcSdrh   if( argc!=2 ){
63129c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
63229c636bcSdrh        " IDL", 0);
63329c636bcSdrh     return TCL_ERROR;
63429c636bcSdrh   }
63529c636bcSdrh   i = parse_client_id(interp, argv[1]);
63629c636bcSdrh   if( i<0 ) return TCL_ERROR;
63729c636bcSdrh   if( !threadset[i].busy ){
63829c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
63929c636bcSdrh     return TCL_ERROR;
64029c636bcSdrh   }
64129c636bcSdrh   client_wait(&threadset[i]);
64229c636bcSdrh   threadset[i].xOp = do_reset;
643cab5ed7aSdrh   sqlite3_free(threadset[i].zArg);
64429c636bcSdrh   threadset[i].zArg = 0;
64529c636bcSdrh   threadset[i].opnum++;
64629c636bcSdrh   return TCL_OK;
64729c636bcSdrh }
64829c636bcSdrh 
64929c636bcSdrh /*
65029c636bcSdrh ** Usage: client_swap ID ID
65129c636bcSdrh **
65229c636bcSdrh ** Interchange the sqlite* pointer between two threads.
65329c636bcSdrh */
tcl_client_swap(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)654*7617e4a8Smistachkin static int SQLITE_TCLAPI tcl_client_swap(
65529c636bcSdrh   void *NotUsed,
65629c636bcSdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
65729c636bcSdrh   int argc,              /* Number of arguments */
65829c636bcSdrh   const char **argv      /* Text of each argument */
65929c636bcSdrh ){
66029c636bcSdrh   int i, j;
66129c636bcSdrh   sqlite3 *temp;
66229c636bcSdrh   if( argc!=3 ){
66329c636bcSdrh     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
66429c636bcSdrh        " ID1 ID2", 0);
66529c636bcSdrh     return TCL_ERROR;
66629c636bcSdrh   }
66729c636bcSdrh   i = parse_client_id(interp, argv[1]);
66829c636bcSdrh   if( i<0 ) return TCL_ERROR;
66929c636bcSdrh   if( !threadset[i].busy ){
67029c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
67129c636bcSdrh     return TCL_ERROR;
67229c636bcSdrh   }
67329c636bcSdrh   client_wait(&threadset[i]);
67429c636bcSdrh   j = parse_client_id(interp, argv[2]);
67529c636bcSdrh   if( j<0 ) return TCL_ERROR;
67629c636bcSdrh   if( !threadset[j].busy ){
67729c636bcSdrh     Tcl_AppendResult(interp, "no such thread", 0);
67829c636bcSdrh     return TCL_ERROR;
67929c636bcSdrh   }
68029c636bcSdrh   client_wait(&threadset[j]);
68129c636bcSdrh   temp = threadset[i].db;
68229c636bcSdrh   threadset[i].db = threadset[j].db;
68329c636bcSdrh   threadset[j].db = temp;
68429c636bcSdrh   return TCL_OK;
68529c636bcSdrh }
68629c636bcSdrh 
68729c636bcSdrh /*
68829c636bcSdrh ** Register commands with the TCL interpreter.
68929c636bcSdrh */
Sqlitetest7_Init(Tcl_Interp * interp)69029c636bcSdrh int Sqlitetest7_Init(Tcl_Interp *interp){
69129c636bcSdrh   static struct {
69229c636bcSdrh      char *zName;
69329c636bcSdrh      Tcl_CmdProc *xProc;
69429c636bcSdrh   } aCmd[] = {
69529c636bcSdrh      { "client_create",     (Tcl_CmdProc*)tcl_client_create     },
69629c636bcSdrh      { "client_wait",       (Tcl_CmdProc*)tcl_client_wait       },
69729c636bcSdrh      { "client_halt",       (Tcl_CmdProc*)tcl_client_halt       },
69829c636bcSdrh      { "client_argc",       (Tcl_CmdProc*)tcl_client_argc       },
69929c636bcSdrh      { "client_argv",       (Tcl_CmdProc*)tcl_client_argv       },
70029c636bcSdrh      { "client_colname",    (Tcl_CmdProc*)tcl_client_colname    },
70129c636bcSdrh      { "client_result",     (Tcl_CmdProc*)tcl_client_result     },
70229c636bcSdrh      { "client_error",      (Tcl_CmdProc*)tcl_client_error      },
70329c636bcSdrh      { "client_compile",    (Tcl_CmdProc*)tcl_client_compile    },
70429c636bcSdrh      { "client_step",       (Tcl_CmdProc*)tcl_client_step       },
70529c636bcSdrh      { "client_reset",      (Tcl_CmdProc*)tcl_client_reset      },
70629c636bcSdrh      { "client_finalize",   (Tcl_CmdProc*)tcl_client_finalize   },
70729c636bcSdrh      { "client_swap",       (Tcl_CmdProc*)tcl_client_swap       },
70829c636bcSdrh   };
70929c636bcSdrh   int i;
71029c636bcSdrh 
71129c636bcSdrh   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
71229c636bcSdrh     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
71329c636bcSdrh   }
71429c636bcSdrh   return TCL_OK;
71529c636bcSdrh }
71629c636bcSdrh #else
Sqlitetest7_Init(Tcl_Interp * interp)71729c636bcSdrh int Sqlitetest7_Init(Tcl_Interp *interp){ return TCL_OK; }
71829bafeabSdanielk1977 #endif /* SQLITE_OS_UNIX */
719