| OLD | NEW | 
 | (Empty) | 
|    1 /* |  | 
|    2 ** 2003 December 18 |  | 
|    3 ** |  | 
|    4 ** The author disclaims copyright to this source code.  In place of |  | 
|    5 ** a legal notice, here is a blessing: |  | 
|    6 ** |  | 
|    7 **    May you do good and not evil. |  | 
|    8 **    May you find forgiveness for yourself and forgive others. |  | 
|    9 **    May you share freely, never taking more than you give. |  | 
|   10 ** |  | 
|   11 ************************************************************************* |  | 
|   12 ** Code for testing the the SQLite library in a multithreaded environment. |  | 
|   13 ** |  | 
|   14 ** $Id: test4.c,v 1.24 2008/10/12 00:27:54 shane Exp $ |  | 
|   15 */ |  | 
|   16 #include "sqliteInt.h" |  | 
|   17 #include "tcl.h" |  | 
|   18 #if defined(SQLITE_OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE |  | 
|   19 #include <stdlib.h> |  | 
|   20 #include <string.h> |  | 
|   21 #include <pthread.h> |  | 
|   22 #include <sched.h> |  | 
|   23 #include <ctype.h> |  | 
|   24  |  | 
|   25 /* |  | 
|   26 ** Each thread is controlled by an instance of the following |  | 
|   27 ** structure. |  | 
|   28 */ |  | 
|   29 typedef struct Thread Thread; |  | 
|   30 struct Thread { |  | 
|   31   /* The first group of fields are writable by the master and read-only |  | 
|   32   ** to the thread. */ |  | 
|   33   char *zFilename;       /* Name of database file */ |  | 
|   34   void (*xOp)(Thread*);  /* next operation to do */ |  | 
|   35   char *zArg;            /* argument usable by xOp */ |  | 
|   36   int opnum;             /* Operation number */ |  | 
|   37   int busy;              /* True if this thread is in use */ |  | 
|   38  |  | 
|   39   /* The next group of fields are writable by the thread but read-only to the |  | 
|   40   ** master. */ |  | 
|   41   int completed;        /* Number of operations completed */ |  | 
|   42   sqlite3 *db;           /* Open database */ |  | 
|   43   sqlite3_stmt *pStmt;     /* Pending operation */ |  | 
|   44   char *zErr;           /* operation error */ |  | 
|   45   char *zStaticErr;     /* Static error message */ |  | 
|   46   int rc;               /* operation return code */ |  | 
|   47   int argc;             /* number of columns in result */ |  | 
|   48   const char *argv[100];    /* result columns */ |  | 
|   49   const char *colv[100];    /* result column names */ |  | 
|   50 }; |  | 
|   51  |  | 
|   52 /* |  | 
|   53 ** There can be as many as 26 threads running at once.  Each is named |  | 
|   54 ** by a capital letter: A, B, C, ..., Y, Z. |  | 
|   55 */ |  | 
|   56 #define N_THREAD 26 |  | 
|   57 static Thread threadset[N_THREAD]; |  | 
|   58  |  | 
|   59  |  | 
|   60 /* |  | 
|   61 ** The main loop for a thread.  Threads use busy waiting.  |  | 
|   62 */ |  | 
|   63 static void *thread_main(void *pArg){ |  | 
|   64   Thread *p = (Thread*)pArg; |  | 
|   65   if( p->db ){ |  | 
|   66     sqlite3_close(p->db); |  | 
|   67   } |  | 
|   68   sqlite3_open(p->zFilename, &p->db); |  | 
|   69   if( SQLITE_OK!=sqlite3_errcode(p->db) ){ |  | 
|   70     p->zErr = strdup(sqlite3_errmsg(p->db)); |  | 
|   71     sqlite3_close(p->db); |  | 
|   72     p->db = 0; |  | 
|   73   } |  | 
|   74   p->pStmt = 0; |  | 
|   75   p->completed = 1; |  | 
|   76   while( p->opnum<=p->completed ) sched_yield(); |  | 
|   77   while( p->xOp ){ |  | 
|   78     if( p->zErr && p->zErr!=p->zStaticErr ){ |  | 
|   79       sqlite3_free(p->zErr); |  | 
|   80       p->zErr = 0; |  | 
|   81     } |  | 
|   82     (*p->xOp)(p); |  | 
|   83     p->completed++; |  | 
|   84     while( p->opnum<=p->completed ) sched_yield(); |  | 
|   85   } |  | 
|   86   if( p->pStmt ){ |  | 
|   87     sqlite3_finalize(p->pStmt); |  | 
|   88     p->pStmt = 0; |  | 
|   89   } |  | 
|   90   if( p->db ){ |  | 
|   91     sqlite3_close(p->db); |  | 
|   92     p->db = 0; |  | 
|   93   } |  | 
|   94   if( p->zErr && p->zErr!=p->zStaticErr ){ |  | 
|   95     sqlite3_free(p->zErr); |  | 
|   96     p->zErr = 0; |  | 
|   97   } |  | 
|   98   p->completed++; |  | 
|   99 #ifndef SQLITE_OMIT_DEPRECATED |  | 
|  100   sqlite3_thread_cleanup(); |  | 
|  101 #endif |  | 
|  102   return 0; |  | 
|  103 } |  | 
|  104  |  | 
|  105 /* |  | 
|  106 ** Get a thread ID which is an upper case letter.  Return the index. |  | 
|  107 ** If the argument is not a valid thread ID put an error message in |  | 
|  108 ** the interpreter and return -1. |  | 
|  109 */ |  | 
|  110 static int parse_thread_id(Tcl_Interp *interp, const char *zArg){ |  | 
|  111   if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){ |  | 
|  112     Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0); |  | 
|  113     return -1; |  | 
|  114   } |  | 
|  115   return zArg[0] - 'A'; |  | 
|  116 } |  | 
|  117  |  | 
|  118 /* |  | 
|  119 ** Usage:    thread_create NAME  FILENAME |  | 
|  120 ** |  | 
|  121 ** NAME should be an upper case letter.  Start the thread running with |  | 
|  122 ** an open connection to the given database. |  | 
|  123 */ |  | 
|  124 static int tcl_thread_create( |  | 
|  125   void *NotUsed, |  | 
|  126   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  127   int argc,              /* Number of arguments */ |  | 
|  128   const char **argv      /* Text of each argument */ |  | 
|  129 ){ |  | 
|  130   int i; |  | 
|  131   pthread_t x; |  | 
|  132   int rc; |  | 
|  133  |  | 
|  134   if( argc!=3 ){ |  | 
|  135     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  136        " ID FILENAME", 0); |  | 
|  137     return TCL_ERROR; |  | 
|  138   } |  | 
|  139   i = parse_thread_id(interp, argv[1]); |  | 
|  140   if( i<0 ) return TCL_ERROR; |  | 
|  141   if( threadset[i].busy ){ |  | 
|  142     Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0); |  | 
|  143     return TCL_ERROR; |  | 
|  144   } |  | 
|  145   threadset[i].busy = 1; |  | 
|  146   sqlite3_free(threadset[i].zFilename); |  | 
|  147   threadset[i].zFilename = sqlite3DbStrDup(0, argv[2]); |  | 
|  148   threadset[i].opnum = 1; |  | 
|  149   threadset[i].completed = 0; |  | 
|  150   rc = pthread_create(&x, 0, thread_main, &threadset[i]); |  | 
|  151   if( rc ){ |  | 
|  152     Tcl_AppendResult(interp, "failed to create the thread", 0); |  | 
|  153     sqlite3_free(threadset[i].zFilename); |  | 
|  154     threadset[i].busy = 0; |  | 
|  155     return TCL_ERROR; |  | 
|  156   } |  | 
|  157   pthread_detach(x); |  | 
|  158   return TCL_OK; |  | 
|  159 } |  | 
|  160  |  | 
|  161 /* |  | 
|  162 ** Wait for a thread to reach its idle state. |  | 
|  163 */ |  | 
|  164 static void thread_wait(Thread *p){ |  | 
|  165   while( p->opnum>p->completed ) sched_yield(); |  | 
|  166 } |  | 
|  167  |  | 
|  168 /* |  | 
|  169 ** Usage:  thread_wait ID |  | 
|  170 ** |  | 
|  171 ** Wait on thread ID to reach its idle state. |  | 
|  172 */ |  | 
|  173 static int tcl_thread_wait( |  | 
|  174   void *NotUsed, |  | 
|  175   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  176   int argc,              /* Number of arguments */ |  | 
|  177   const char **argv      /* Text of each argument */ |  | 
|  178 ){ |  | 
|  179   int i; |  | 
|  180  |  | 
|  181   if( argc!=2 ){ |  | 
|  182     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  183        " ID", 0); |  | 
|  184     return TCL_ERROR; |  | 
|  185   } |  | 
|  186   i = parse_thread_id(interp, argv[1]); |  | 
|  187   if( i<0 ) return TCL_ERROR; |  | 
|  188   if( !threadset[i].busy ){ |  | 
|  189     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  190     return TCL_ERROR; |  | 
|  191   } |  | 
|  192   thread_wait(&threadset[i]); |  | 
|  193   return TCL_OK; |  | 
|  194 } |  | 
|  195  |  | 
|  196 /* |  | 
|  197 ** Stop a thread. |  | 
|  198 */ |  | 
|  199 static void stop_thread(Thread *p){ |  | 
|  200   thread_wait(p); |  | 
|  201   p->xOp = 0; |  | 
|  202   p->opnum++; |  | 
|  203   thread_wait(p); |  | 
|  204   sqlite3_free(p->zArg); |  | 
|  205   p->zArg = 0; |  | 
|  206   sqlite3_free(p->zFilename); |  | 
|  207   p->zFilename = 0; |  | 
|  208   p->busy = 0; |  | 
|  209 } |  | 
|  210  |  | 
|  211 /* |  | 
|  212 ** Usage:  thread_halt ID |  | 
|  213 ** |  | 
|  214 ** Cause a thread to shut itself down.  Wait for the shutdown to be |  | 
|  215 ** completed.  If ID is "*" then stop all threads. |  | 
|  216 */ |  | 
|  217 static int tcl_thread_halt( |  | 
|  218   void *NotUsed, |  | 
|  219   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  220   int argc,              /* Number of arguments */ |  | 
|  221   const char **argv      /* Text of each argument */ |  | 
|  222 ){ |  | 
|  223   int i; |  | 
|  224  |  | 
|  225   if( argc!=2 ){ |  | 
|  226     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  227        " ID", 0); |  | 
|  228     return TCL_ERROR; |  | 
|  229   } |  | 
|  230   if( argv[1][0]=='*' && argv[1][1]==0 ){ |  | 
|  231     for(i=0; i<N_THREAD; i++){ |  | 
|  232       if( threadset[i].busy ) stop_thread(&threadset[i]); |  | 
|  233     } |  | 
|  234   }else{ |  | 
|  235     i = parse_thread_id(interp, argv[1]); |  | 
|  236     if( i<0 ) return TCL_ERROR; |  | 
|  237     if( !threadset[i].busy ){ |  | 
|  238       Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  239       return TCL_ERROR; |  | 
|  240     } |  | 
|  241     stop_thread(&threadset[i]); |  | 
|  242   } |  | 
|  243   return TCL_OK; |  | 
|  244 } |  | 
|  245  |  | 
|  246 /* |  | 
|  247 ** Usage: thread_argc  ID |  | 
|  248 ** |  | 
|  249 ** Wait on the most recent thread_step to complete, then return the |  | 
|  250 ** number of columns in the result set. |  | 
|  251 */ |  | 
|  252 static int tcl_thread_argc( |  | 
|  253   void *NotUsed, |  | 
|  254   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  255   int argc,              /* Number of arguments */ |  | 
|  256   const char **argv      /* Text of each argument */ |  | 
|  257 ){ |  | 
|  258   int i; |  | 
|  259   char zBuf[100]; |  | 
|  260  |  | 
|  261   if( argc!=2 ){ |  | 
|  262     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  263        " ID", 0); |  | 
|  264     return TCL_ERROR; |  | 
|  265   } |  | 
|  266   i = parse_thread_id(interp, argv[1]); |  | 
|  267   if( i<0 ) return TCL_ERROR; |  | 
|  268   if( !threadset[i].busy ){ |  | 
|  269     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  270     return TCL_ERROR; |  | 
|  271   } |  | 
|  272   thread_wait(&threadset[i]); |  | 
|  273   sprintf(zBuf, "%d", threadset[i].argc); |  | 
|  274   Tcl_AppendResult(interp, zBuf, 0); |  | 
|  275   return TCL_OK; |  | 
|  276 } |  | 
|  277  |  | 
|  278 /* |  | 
|  279 ** Usage: thread_argv  ID   N |  | 
|  280 ** |  | 
|  281 ** Wait on the most recent thread_step to complete, then return the |  | 
|  282 ** value of the N-th columns in the result set. |  | 
|  283 */ |  | 
|  284 static int tcl_thread_argv( |  | 
|  285   void *NotUsed, |  | 
|  286   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  287   int argc,              /* Number of arguments */ |  | 
|  288   const char **argv      /* Text of each argument */ |  | 
|  289 ){ |  | 
|  290   int i; |  | 
|  291   int n; |  | 
|  292  |  | 
|  293   if( argc!=3 ){ |  | 
|  294     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  295        " ID N", 0); |  | 
|  296     return TCL_ERROR; |  | 
|  297   } |  | 
|  298   i = parse_thread_id(interp, argv[1]); |  | 
|  299   if( i<0 ) return TCL_ERROR; |  | 
|  300   if( !threadset[i].busy ){ |  | 
|  301     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  302     return TCL_ERROR; |  | 
|  303   } |  | 
|  304   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; |  | 
|  305   thread_wait(&threadset[i]); |  | 
|  306   if( n<0 || n>=threadset[i].argc ){ |  | 
|  307     Tcl_AppendResult(interp, "column number out of range", 0); |  | 
|  308     return TCL_ERROR; |  | 
|  309   } |  | 
|  310   Tcl_AppendResult(interp, threadset[i].argv[n], 0); |  | 
|  311   return TCL_OK; |  | 
|  312 } |  | 
|  313  |  | 
|  314 /* |  | 
|  315 ** Usage: thread_colname  ID   N |  | 
|  316 ** |  | 
|  317 ** Wait on the most recent thread_step to complete, then return the |  | 
|  318 ** name of the N-th columns in the result set. |  | 
|  319 */ |  | 
|  320 static int tcl_thread_colname( |  | 
|  321   void *NotUsed, |  | 
|  322   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  323   int argc,              /* Number of arguments */ |  | 
|  324   const char **argv      /* Text of each argument */ |  | 
|  325 ){ |  | 
|  326   int i; |  | 
|  327   int n; |  | 
|  328  |  | 
|  329   if( argc!=3 ){ |  | 
|  330     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  331        " ID N", 0); |  | 
|  332     return TCL_ERROR; |  | 
|  333   } |  | 
|  334   i = parse_thread_id(interp, argv[1]); |  | 
|  335   if( i<0 ) return TCL_ERROR; |  | 
|  336   if( !threadset[i].busy ){ |  | 
|  337     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  338     return TCL_ERROR; |  | 
|  339   } |  | 
|  340   if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; |  | 
|  341   thread_wait(&threadset[i]); |  | 
|  342   if( n<0 || n>=threadset[i].argc ){ |  | 
|  343     Tcl_AppendResult(interp, "column number out of range", 0); |  | 
|  344     return TCL_ERROR; |  | 
|  345   } |  | 
|  346   Tcl_AppendResult(interp, threadset[i].colv[n], 0); |  | 
|  347   return TCL_OK; |  | 
|  348 } |  | 
|  349  |  | 
|  350 /* |  | 
|  351 ** Usage: thread_result  ID |  | 
|  352 ** |  | 
|  353 ** Wait on the most recent operation to complete, then return the |  | 
|  354 ** result code from that operation. |  | 
|  355 */ |  | 
|  356 static int tcl_thread_result( |  | 
|  357   void *NotUsed, |  | 
|  358   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  359   int argc,              /* Number of arguments */ |  | 
|  360   const char **argv      /* Text of each argument */ |  | 
|  361 ){ |  | 
|  362   int i; |  | 
|  363   const char *zName; |  | 
|  364  |  | 
|  365   if( argc!=2 ){ |  | 
|  366     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  367        " ID", 0); |  | 
|  368     return TCL_ERROR; |  | 
|  369   } |  | 
|  370   i = parse_thread_id(interp, argv[1]); |  | 
|  371   if( i<0 ) return TCL_ERROR; |  | 
|  372   if( !threadset[i].busy ){ |  | 
|  373     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  374     return TCL_ERROR; |  | 
|  375   } |  | 
|  376   thread_wait(&threadset[i]); |  | 
|  377   switch( threadset[i].rc ){ |  | 
|  378     case SQLITE_OK:         zName = "SQLITE_OK";          break; |  | 
|  379     case SQLITE_ERROR:      zName = "SQLITE_ERROR";       break; |  | 
|  380     case SQLITE_PERM:       zName = "SQLITE_PERM";        break; |  | 
|  381     case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break; |  | 
|  382     case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break; |  | 
|  383     case SQLITE_LOCKED:     zName = "SQLITE_LOCKED";      break; |  | 
|  384     case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break; |  | 
|  385     case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break; |  | 
|  386     case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break; |  | 
|  387     case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break; |  | 
|  388     case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break; |  | 
|  389     case SQLITE_FULL:       zName = "SQLITE_FULL";        break; |  | 
|  390     case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break; |  | 
|  391     case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break; |  | 
|  392     case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break; |  | 
|  393     case SQLITE_SCHEMA:     zName = "SQLITE_SCHEMA";      break; |  | 
|  394     case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT";  break; |  | 
|  395     case SQLITE_MISMATCH:   zName = "SQLITE_MISMATCH";    break; |  | 
|  396     case SQLITE_MISUSE:     zName = "SQLITE_MISUSE";      break; |  | 
|  397     case SQLITE_NOLFS:      zName = "SQLITE_NOLFS";       break; |  | 
|  398     case SQLITE_AUTH:       zName = "SQLITE_AUTH";        break; |  | 
|  399     case SQLITE_FORMAT:     zName = "SQLITE_FORMAT";      break; |  | 
|  400     case SQLITE_RANGE:      zName = "SQLITE_RANGE";       break; |  | 
|  401     case SQLITE_ROW:        zName = "SQLITE_ROW";         break; |  | 
|  402     case SQLITE_DONE:       zName = "SQLITE_DONE";        break; |  | 
|  403     default:                zName = "SQLITE_Unknown";     break; |  | 
|  404   } |  | 
|  405   Tcl_AppendResult(interp, zName, 0); |  | 
|  406   return TCL_OK; |  | 
|  407 } |  | 
|  408  |  | 
|  409 /* |  | 
|  410 ** Usage: thread_error  ID |  | 
|  411 ** |  | 
|  412 ** Wait on the most recent operation to complete, then return the |  | 
|  413 ** error string. |  | 
|  414 */ |  | 
|  415 static int tcl_thread_error( |  | 
|  416   void *NotUsed, |  | 
|  417   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  418   int argc,              /* Number of arguments */ |  | 
|  419   const char **argv      /* Text of each argument */ |  | 
|  420 ){ |  | 
|  421   int i; |  | 
|  422  |  | 
|  423   if( argc!=2 ){ |  | 
|  424     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  425        " ID", 0); |  | 
|  426     return TCL_ERROR; |  | 
|  427   } |  | 
|  428   i = parse_thread_id(interp, argv[1]); |  | 
|  429   if( i<0 ) return TCL_ERROR; |  | 
|  430   if( !threadset[i].busy ){ |  | 
|  431     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  432     return TCL_ERROR; |  | 
|  433   } |  | 
|  434   thread_wait(&threadset[i]); |  | 
|  435   Tcl_AppendResult(interp, threadset[i].zErr, 0); |  | 
|  436   return TCL_OK; |  | 
|  437 } |  | 
|  438  |  | 
|  439 /* |  | 
|  440 ** This procedure runs in the thread to compile an SQL statement. |  | 
|  441 */ |  | 
|  442 static void do_compile(Thread *p){ |  | 
|  443   if( p->db==0 ){ |  | 
|  444     p->zErr = p->zStaticErr = "no database is open"; |  | 
|  445     p->rc = SQLITE_ERROR; |  | 
|  446     return; |  | 
|  447   } |  | 
|  448   if( p->pStmt ){ |  | 
|  449     sqlite3_finalize(p->pStmt); |  | 
|  450     p->pStmt = 0; |  | 
|  451   } |  | 
|  452   p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0); |  | 
|  453 } |  | 
|  454  |  | 
|  455 /* |  | 
|  456 ** Usage: thread_compile ID SQL |  | 
|  457 ** |  | 
|  458 ** Compile a new virtual machine. |  | 
|  459 */ |  | 
|  460 static int tcl_thread_compile( |  | 
|  461   void *NotUsed, |  | 
|  462   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  463   int argc,              /* Number of arguments */ |  | 
|  464   const char **argv      /* Text of each argument */ |  | 
|  465 ){ |  | 
|  466   int i; |  | 
|  467   if( argc!=3 ){ |  | 
|  468     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  469        " ID SQL", 0); |  | 
|  470     return TCL_ERROR; |  | 
|  471   } |  | 
|  472   i = parse_thread_id(interp, argv[1]); |  | 
|  473   if( i<0 ) return TCL_ERROR; |  | 
|  474   if( !threadset[i].busy ){ |  | 
|  475     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  476     return TCL_ERROR; |  | 
|  477   } |  | 
|  478   thread_wait(&threadset[i]); |  | 
|  479   threadset[i].xOp = do_compile; |  | 
|  480   sqlite3_free(threadset[i].zArg); |  | 
|  481   threadset[i].zArg = sqlite3DbStrDup(0, argv[2]); |  | 
|  482   threadset[i].opnum++; |  | 
|  483   return TCL_OK; |  | 
|  484 } |  | 
|  485  |  | 
|  486 /* |  | 
|  487 ** This procedure runs in the thread to step the virtual machine. |  | 
|  488 */ |  | 
|  489 static void do_step(Thread *p){ |  | 
|  490   int i; |  | 
|  491   if( p->pStmt==0 ){ |  | 
|  492     p->zErr = p->zStaticErr = "no virtual machine available"; |  | 
|  493     p->rc = SQLITE_ERROR; |  | 
|  494     return; |  | 
|  495   } |  | 
|  496   p->rc = sqlite3_step(p->pStmt); |  | 
|  497   if( p->rc==SQLITE_ROW ){ |  | 
|  498     p->argc = sqlite3_column_count(p->pStmt); |  | 
|  499     for(i=0; i<sqlite3_data_count(p->pStmt); i++){ |  | 
|  500       p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i); |  | 
|  501     } |  | 
|  502     for(i=0; i<p->argc; i++){ |  | 
|  503       p->colv[i] = sqlite3_column_name(p->pStmt, i); |  | 
|  504     } |  | 
|  505   } |  | 
|  506 } |  | 
|  507  |  | 
|  508 /* |  | 
|  509 ** Usage: thread_step ID |  | 
|  510 ** |  | 
|  511 ** Advance the virtual machine by one step |  | 
|  512 */ |  | 
|  513 static int tcl_thread_step( |  | 
|  514   void *NotUsed, |  | 
|  515   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  516   int argc,              /* Number of arguments */ |  | 
|  517   const char **argv      /* Text of each argument */ |  | 
|  518 ){ |  | 
|  519   int i; |  | 
|  520   if( argc!=2 ){ |  | 
|  521     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  522        " IDL", 0); |  | 
|  523     return TCL_ERROR; |  | 
|  524   } |  | 
|  525   i = parse_thread_id(interp, argv[1]); |  | 
|  526   if( i<0 ) return TCL_ERROR; |  | 
|  527   if( !threadset[i].busy ){ |  | 
|  528     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  529     return TCL_ERROR; |  | 
|  530   } |  | 
|  531   thread_wait(&threadset[i]); |  | 
|  532   threadset[i].xOp = do_step; |  | 
|  533   threadset[i].opnum++; |  | 
|  534   return TCL_OK; |  | 
|  535 } |  | 
|  536  |  | 
|  537 /* |  | 
|  538 ** This procedure runs in the thread to finalize a virtual machine. |  | 
|  539 */ |  | 
|  540 static void do_finalize(Thread *p){ |  | 
|  541   if( p->pStmt==0 ){ |  | 
|  542     p->zErr = p->zStaticErr = "no virtual machine available"; |  | 
|  543     p->rc = SQLITE_ERROR; |  | 
|  544     return; |  | 
|  545   } |  | 
|  546   p->rc = sqlite3_finalize(p->pStmt); |  | 
|  547   p->pStmt = 0; |  | 
|  548 } |  | 
|  549  |  | 
|  550 /* |  | 
|  551 ** Usage: thread_finalize ID |  | 
|  552 ** |  | 
|  553 ** Finalize the virtual machine. |  | 
|  554 */ |  | 
|  555 static int tcl_thread_finalize( |  | 
|  556   void *NotUsed, |  | 
|  557   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  558   int argc,              /* Number of arguments */ |  | 
|  559   const char **argv      /* Text of each argument */ |  | 
|  560 ){ |  | 
|  561   int i; |  | 
|  562   if( argc!=2 ){ |  | 
|  563     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  564        " IDL", 0); |  | 
|  565     return TCL_ERROR; |  | 
|  566   } |  | 
|  567   i = parse_thread_id(interp, argv[1]); |  | 
|  568   if( i<0 ) return TCL_ERROR; |  | 
|  569   if( !threadset[i].busy ){ |  | 
|  570     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  571     return TCL_ERROR; |  | 
|  572   } |  | 
|  573   thread_wait(&threadset[i]); |  | 
|  574   threadset[i].xOp = do_finalize; |  | 
|  575   sqlite3_free(threadset[i].zArg); |  | 
|  576   threadset[i].zArg = 0; |  | 
|  577   threadset[i].opnum++; |  | 
|  578   return TCL_OK; |  | 
|  579 } |  | 
|  580  |  | 
|  581 /* |  | 
|  582 ** Usage: thread_swap ID ID |  | 
|  583 ** |  | 
|  584 ** Interchange the sqlite* pointer between two threads. |  | 
|  585 */ |  | 
|  586 static int tcl_thread_swap( |  | 
|  587   void *NotUsed, |  | 
|  588   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  589   int argc,              /* Number of arguments */ |  | 
|  590   const char **argv      /* Text of each argument */ |  | 
|  591 ){ |  | 
|  592   int i, j; |  | 
|  593   sqlite3 *temp; |  | 
|  594   if( argc!=3 ){ |  | 
|  595     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  596        " ID1 ID2", 0); |  | 
|  597     return TCL_ERROR; |  | 
|  598   } |  | 
|  599   i = parse_thread_id(interp, argv[1]); |  | 
|  600   if( i<0 ) return TCL_ERROR; |  | 
|  601   if( !threadset[i].busy ){ |  | 
|  602     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  603     return TCL_ERROR; |  | 
|  604   } |  | 
|  605   thread_wait(&threadset[i]); |  | 
|  606   j = parse_thread_id(interp, argv[2]); |  | 
|  607   if( j<0 ) return TCL_ERROR; |  | 
|  608   if( !threadset[j].busy ){ |  | 
|  609     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  610     return TCL_ERROR; |  | 
|  611   } |  | 
|  612   thread_wait(&threadset[j]); |  | 
|  613   temp = threadset[i].db; |  | 
|  614   threadset[i].db = threadset[j].db; |  | 
|  615   threadset[j].db = temp; |  | 
|  616   return TCL_OK; |  | 
|  617 } |  | 
|  618  |  | 
|  619 /* |  | 
|  620 ** Usage: thread_db_get ID |  | 
|  621 ** |  | 
|  622 ** Return the database connection pointer for the given thread.  Then |  | 
|  623 ** remove the pointer from the thread itself.  Afterwards, the thread |  | 
|  624 ** can be stopped and the connection can be used by the main thread. |  | 
|  625 */ |  | 
|  626 static int tcl_thread_db_get( |  | 
|  627   void *NotUsed, |  | 
|  628   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  629   int argc,              /* Number of arguments */ |  | 
|  630   const char **argv      /* Text of each argument */ |  | 
|  631 ){ |  | 
|  632   int i; |  | 
|  633   char zBuf[100]; |  | 
|  634   extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); |  | 
|  635   if( argc!=2 ){ |  | 
|  636     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  637        " ID", 0); |  | 
|  638     return TCL_ERROR; |  | 
|  639   } |  | 
|  640   i = parse_thread_id(interp, argv[1]); |  | 
|  641   if( i<0 ) return TCL_ERROR; |  | 
|  642   if( !threadset[i].busy ){ |  | 
|  643     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  644     return TCL_ERROR; |  | 
|  645   } |  | 
|  646   thread_wait(&threadset[i]); |  | 
|  647   sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db); |  | 
|  648   threadset[i].db = 0; |  | 
|  649   Tcl_AppendResult(interp, zBuf, (char*)0); |  | 
|  650   return TCL_OK; |  | 
|  651 } |  | 
|  652  |  | 
|  653 /* |  | 
|  654 ** Usage: thread_db_put ID DB |  | 
|  655 ** |  | 
|  656 */ |  | 
|  657 static int tcl_thread_db_put( |  | 
|  658   void *NotUsed, |  | 
|  659   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  660   int argc,              /* Number of arguments */ |  | 
|  661   const char **argv      /* Text of each argument */ |  | 
|  662 ){ |  | 
|  663   int i; |  | 
|  664   extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); |  | 
|  665   extern void *sqlite3TestTextToPtr(const char *); |  | 
|  666   if( argc!=3 ){ |  | 
|  667     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  668        " ID DB", 0); |  | 
|  669     return TCL_ERROR; |  | 
|  670   } |  | 
|  671   i = parse_thread_id(interp, argv[1]); |  | 
|  672   if( i<0 ) return TCL_ERROR; |  | 
|  673   if( !threadset[i].busy ){ |  | 
|  674     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  675     return TCL_ERROR; |  | 
|  676   } |  | 
|  677   thread_wait(&threadset[i]); |  | 
|  678   assert( !threadset[i].db ); |  | 
|  679   threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]); |  | 
|  680   return TCL_OK; |  | 
|  681 } |  | 
|  682  |  | 
|  683 /* |  | 
|  684 ** Usage: thread_stmt_get ID |  | 
|  685 ** |  | 
|  686 ** Return the database stmt pointer for the given thread.  Then |  | 
|  687 ** remove the pointer from the thread itself.  |  | 
|  688 */ |  | 
|  689 static int tcl_thread_stmt_get( |  | 
|  690   void *NotUsed, |  | 
|  691   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */ |  | 
|  692   int argc,              /* Number of arguments */ |  | 
|  693   const char **argv      /* Text of each argument */ |  | 
|  694 ){ |  | 
|  695   int i; |  | 
|  696   char zBuf[100]; |  | 
|  697   extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); |  | 
|  698   if( argc!=2 ){ |  | 
|  699     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |  | 
|  700        " ID", 0); |  | 
|  701     return TCL_ERROR; |  | 
|  702   } |  | 
|  703   i = parse_thread_id(interp, argv[1]); |  | 
|  704   if( i<0 ) return TCL_ERROR; |  | 
|  705   if( !threadset[i].busy ){ |  | 
|  706     Tcl_AppendResult(interp, "no such thread", 0); |  | 
|  707     return TCL_ERROR; |  | 
|  708   } |  | 
|  709   thread_wait(&threadset[i]); |  | 
|  710   sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt); |  | 
|  711   threadset[i].pStmt = 0; |  | 
|  712   Tcl_AppendResult(interp, zBuf, (char*)0); |  | 
|  713   return TCL_OK; |  | 
|  714 } |  | 
|  715  |  | 
|  716 /* |  | 
|  717 ** Register commands with the TCL interpreter. |  | 
|  718 */ |  | 
|  719 int Sqlitetest4_Init(Tcl_Interp *interp){ |  | 
|  720   static struct { |  | 
|  721      char *zName; |  | 
|  722      Tcl_CmdProc *xProc; |  | 
|  723   } aCmd[] = { |  | 
|  724      { "thread_create",     (Tcl_CmdProc*)tcl_thread_create     }, |  | 
|  725      { "thread_wait",       (Tcl_CmdProc*)tcl_thread_wait       }, |  | 
|  726      { "thread_halt",       (Tcl_CmdProc*)tcl_thread_halt       }, |  | 
|  727      { "thread_argc",       (Tcl_CmdProc*)tcl_thread_argc       }, |  | 
|  728      { "thread_argv",       (Tcl_CmdProc*)tcl_thread_argv       }, |  | 
|  729      { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    }, |  | 
|  730      { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     }, |  | 
|  731      { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      }, |  | 
|  732      { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    }, |  | 
|  733      { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       }, |  | 
|  734      { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   }, |  | 
|  735      { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       }, |  | 
|  736      { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     }, |  | 
|  737      { "thread_db_put",     (Tcl_CmdProc*)tcl_thread_db_put     }, |  | 
|  738      { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   }, |  | 
|  739   }; |  | 
|  740   int i; |  | 
|  741  |  | 
|  742   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ |  | 
|  743     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); |  | 
|  744   } |  | 
|  745   return TCL_OK; |  | 
|  746 } |  | 
|  747 #else |  | 
|  748 int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; } |  | 
|  749 #endif /* SQLITE_OS_UNIX */ |  | 
| OLD | NEW |