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